diff --git a/.github/ISSUE_TEMPLATE/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE/ISSUE_TEMPLATE.md
new file mode 100644
index 000000000..11290dc66
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/ISSUE_TEMPLATE.md
@@ -0,0 +1,49 @@
+
+
+#### Description
+
+
+#### Steps/Code to Reproduce
+
+
+#### Expected Results
+
+
+#### Actual Results
+
+
+#### Versions
+
+
+
+
+
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 000000000..89ad09697
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,31 @@
+
+
+#### Metadata
+* Reference Issue:
+* New Tests Added:
+* Documentation Updated:
+* Change Log Entry:
+
+
+#### Details
+
+
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 000000000..e5e5092a2
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,9 @@
+version: 2
+
+updates:
+ # This will check for updates to github actions every day
+ # https://docs.github.com/en/enterprise-server@3.4/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "daily"
diff --git a/.github/workflows/dist.yaml b/.github/workflows/dist.yaml
new file mode 100644
index 000000000..ecf6f0a7f
--- /dev/null
+++ b/.github/workflows/dist.yaml
@@ -0,0 +1,48 @@
+name: dist-check
+
+on:
+ workflow_dispatch:
+
+ push:
+ branches:
+ - main
+ - develop
+ tags:
+ - "v*.*.*"
+
+ pull_request:
+ branches:
+ - main
+ - develop
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ dist:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v6
+ - name: Setup Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.10"
+ - name: Build dist
+ run: |
+ pip install build
+ python -m build --sdist
+ - name: Twine check
+ run: |
+ pip install twine
+ last_dist=$(ls -t dist/openml-*.tar.gz | head -n 1)
+ twine check $last_dist
+ - name: Install dist
+ run: |
+ last_dist=$(ls -t dist/openml-*.tar.gz | head -n 1)
+ pip install $last_dist
+ - name: PEP 561 Compliance
+ run: |
+ pip install mypy
+ cd .. # required to use the installed version of openml
+ if ! python -m mypy -c "import openml"; then exit 1; fi
diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml
new file mode 100644
index 000000000..1a5a36a87
--- /dev/null
+++ b/.github/workflows/docs.yaml
@@ -0,0 +1,62 @@
+name: Docs
+on:
+ workflow_dispatch:
+
+ push:
+ branches:
+ - main
+ - develop
+ tags:
+ - "v*.*.*"
+
+ pull_request:
+ branches:
+ - main
+ - develop
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ build-and-deploy:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v6
+ with:
+ fetch-depth: 0
+ - name: Setup Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.10"
+ - name: Install dependencies
+ run: |
+ pip install -e .[docs,examples]
+ - name: Make docs
+ run: |
+ mkdocs build
+ - name: Deploy to GitHub Pages
+ env:
+ CI: false
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ PAGES_BRANCH: gh-pages
+ if: (contains(github.ref, 'develop') || contains(github.ref, 'main')) && github.event_name == 'push'
+ run: |
+ git config user.name doc-bot
+ git config user.email doc-bot@openml.com
+ current_version=$(git tag | sort --version-sort | tail -n 1)
+ # This block will rename previous retitled versions
+ retitled_versions=$(mike list -j | jq ".[] | select(.title != .version) | .version" | tr -d '"')
+ for version in $retitled_versions; do
+ mike retitle "${version}" "${version}"
+ done
+
+ echo "Deploying docs for ${current_version}"
+ mike set-default latest
+ mike deploy \
+ --push \
+ --title "${current_version} (latest)" \
+ --update-aliases \
+ "${current_version}" \
+ "latest"\
+ -b $PAGES_BRANCH
diff --git a/.github/workflows/release_docker.yaml b/.github/workflows/release_docker.yaml
new file mode 100644
index 000000000..fcea357e4
--- /dev/null
+++ b/.github/workflows/release_docker.yaml
@@ -0,0 +1,66 @@
+name: release-docker
+
+on:
+ workflow_dispatch:
+ push:
+ branches:
+ - 'develop'
+ - 'docker'
+ tags:
+ - 'v*'
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+ cancel-in-progress: true
+
+jobs:
+
+ docker:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Set up QEMU
+ uses: docker/setup-qemu-action@v3
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
+
+ - name: Login to DockerHub
+ if: github.event_name != 'pull_request'
+ uses: docker/login-action@v3
+ with:
+ username: ${{ secrets.DOCKERHUB_USERNAME }}
+ password: ${{ secrets.DOCKERHUB_TOKEN }}
+
+ - name: Check out the repo
+ uses: actions/checkout@v6
+
+ - name: Extract metadata (tags, labels) for Docker Hub
+ id: meta_dockerhub
+ uses: docker/metadata-action@v5
+ with:
+ images: "openml/openml-python"
+
+ - name: Build and push
+ id: docker_build
+ uses: docker/build-push-action@v6
+ with:
+ context: ./docker/
+ tags: ${{ steps.meta_dockerhub.outputs.tags }}
+ labels: ${{ steps.meta_dockerhub.outputs.labels }}
+ platforms: linux/amd64,linux/arm64
+ push: ${{ github.event_name == 'push' }}
+
+ - name: Update repo description
+ if: ${{ startsWith(github.ref, 'refs/tags/v') }}
+ uses: peter-evans/dockerhub-description@v4
+ with:
+ username: ${{ secrets.DOCKERHUB_USERNAME }}
+ password: ${{ secrets.DOCKERHUB_TOKEN }}
+ repository: openml/openml-python
+ short-description: "pre-installed openml-python environment"
+ readme-filepath: ./docker/readme.md
+
+ - name: Image digest
+ run: echo ${{ steps.docker_build.outputs.digest }}
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 000000000..7fa3450ca
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,194 @@
+---
+name: Tests
+
+on:
+ workflow_dispatch:
+
+ push:
+ branches:
+ - main
+ - develop
+ tags:
+ - "v*.*.*"
+
+ pull_request:
+ branches:
+ - main
+ - develop
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ test:
+ name: (${{ matrix.os }},Py${{ matrix.python-version }},sk${{ matrix.scikit-learn }}${{ matrix.pandas-version != '' && format(',pd:{0}', matrix.pandas-version) || '' }},sk-only:${{ matrix.sklearn-only }})
+ runs-on: ${{ matrix.os }}
+
+ strategy:
+ fail-fast: false
+ matrix:
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
+ scikit-learn: ["1.3.*", "1.4.*", "1.5.*", "1.6.*", "1.7.*"]
+ os: [ubuntu-latest]
+ sklearn-only: ["true"]
+
+ exclude:
+ # incompatible version combinations
+ - python-version: "3.13"
+ scikit-learn: "1.3.*"
+ - python-version: "3.13"
+ scikit-learn: "1.4.*"
+ - python-version: "3.14"
+ scikit-learn: "1.3.*"
+ - python-version: "3.14"
+ scikit-learn: "1.4.*"
+
+ include:
+ # Full test run on ubuntu, 3.14
+ - os: ubuntu-latest
+ python-version: "3.14"
+ scikit-learn: "1.7.*"
+ sklearn-only: "false"
+
+ # Full test run on Windows
+ - os: windows-latest
+ python-version: "3.12"
+ scikit-learn: "1.5.*"
+ sklearn-only: "false"
+
+ # Coverage run
+ - os: ubuntu-latest
+ python-version: "3.12"
+ scikit-learn: "1.5.*"
+ sklearn-only: "false"
+ code-cov: true
+
+ # Pandas 2 run
+ - os: ubuntu-latest
+ python-version: "3.12"
+ scikit-learn: "1.5.*"
+ sklearn-only: "false"
+ pandas-version: "2.*"
+ code-cov: false
+
+ steps:
+ - uses: actions/checkout@v6
+ with:
+ fetch-depth: 2
+
+ - name: Setup Python ${{ matrix.python-version }}
+ uses: actions/setup-python@v5
+ with:
+ python-version: ${{ matrix.python-version }}
+
+ - name: Install test dependencies, scikit-learn, and optional pandas
+ shell: bash
+ run: |
+ python -m pip install --upgrade pip
+ pip install -e .[test] scikit-learn==${{ matrix.scikit-learn }}
+
+ if [ "${{ matrix.pandas-version }}" != "" ]; then
+ echo "Installing specific pandas version: ${{ matrix.pandas-version }}"
+ pip install "pandas==${{ matrix.pandas-version }}"
+ fi
+
+ - name: Store repository status
+ id: status-before
+ if: matrix.os != 'windows-latest'
+ run: |
+ git_status=$(git status --porcelain -b)
+ echo "BEFORE=$git_status" >> $GITHUB_ENV
+ echo "Repository status before tests: $git_status"
+
+ - name: Show installed dependencies
+ run: python -m pip list
+
+ - name: Run tests on Ubuntu Test
+ if: matrix.os == 'ubuntu-latest'
+ env:
+ OPENML_TEST_SERVER_ADMIN_KEY: ${{ secrets.OPENML_TEST_SERVER_ADMIN_KEY }}
+ run: |
+ if [ "${{ matrix.code-cov }}" = "true" ]; then
+ codecov="--cov=openml --long --cov-report=xml"
+ fi
+
+ if [ "${{ matrix.sklearn-only }}" = "true" ]; then
+ marks="sklearn and not production_server and not test_server"
+ else
+ marks="not production_server and not test_server"
+ fi
+
+ pytest -n 4 --durations=20 --dist load -sv $codecov -o log_cli=true -m "$marks"
+
+ - name: Run tests on Ubuntu Production
+ if: matrix.os == 'ubuntu-latest'
+ env:
+ OPENML_TEST_SERVER_ADMIN_KEY: ${{ secrets.OPENML_TEST_SERVER_ADMIN_KEY }}
+ run: |
+ if [ "${{ matrix.code-cov }}" = "true" ]; then
+ codecov="--cov=openml --long --cov-report=xml"
+ fi
+
+ if [ "${{ matrix.sklearn-only }}" = "true" ]; then
+ marks="sklearn and production_server and not test_server"
+ else
+ marks="production_server and not test_server"
+ fi
+
+ pytest -n 4 --durations=20 --dist load -sv $codecov -o log_cli=true -m "$marks"
+
+ - name: Run tests on Windows
+ if: matrix.os == 'windows-latest'
+ env:
+ OPENML_TEST_SERVER_ADMIN_KEY: ${{ secrets.OPENML_TEST_SERVER_ADMIN_KEY }}
+ run: | # we need a separate step because of the bash-specific if-statement in the previous one.
+ pytest -n 4 --durations=20 --dist load -sv --reruns 5 --reruns-delay 1 -m "not test_server"
+
+ - name: Check for files left behind by test
+ if: matrix.os != 'windows-latest' && always()
+ run: |
+ before="${{ env.BEFORE }}"
+ after="$(git status --porcelain -b)"
+ if [[ "$before" != "$after" ]]; then
+ echo "git status from before: $before"
+ echo "git status from after: $after"
+ echo "Not all generated files have been deleted!"
+ exit 1
+ fi
+
+ - name: Upload coverage
+ if: matrix.code-cov && always()
+ uses: codecov/codecov-action@v4
+ with:
+ files: coverage.xml
+ token: ${{ secrets.CODECOV_TOKEN }}
+ fail_ci_if_error: true
+ verbose: true
+
+ dummy_windows_py_sk024:
+ name: (windows-latest, Py, sk0.24.*, sk-only:false)
+ runs-on: ubuntu-latest
+ steps:
+ - name: Dummy step
+ run: |
+ echo "This is a temporary dummy job."
+ echo "Always succeeds."
+
+ dummy_windows_py_sk023:
+ name: (ubuntu-latest, Py3.8, sk0.23.1, sk-only:false)
+ runs-on: ubuntu-latest
+ steps:
+ - name: Dummy step
+ run: |
+ echo "This is a temporary dummy job."
+ echo "Always succeeds."
+
+ dummy_docker:
+ name: docker
+ runs-on: ubuntu-latest
+ steps:
+ - name: Dummy step
+ run: |
+ echo "This is a temporary dummy docker job."
+ echo "Always succeeds."
diff --git a/.gitignore b/.gitignore
index 9daaf25c6..d512c0ee6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,10 @@
+*~
doc/generated
examples/.ipynb_checkpoints
+venv
+.uv-lock
+uv.lock
+
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
@@ -14,6 +19,14 @@ doc/auto_examples/
doc/modules/generated/
doc/datasets/generated/
+# Some stuff from testing?
+tests/files/org/openml/test/datasets/1/
+tests/files/org/openml/test/datasets/2/features.xml.pkl
+tests/files/org/openml/test/datasets/2/qualities.xml.pkl
+tests/files/org/openml/test/locks/
+tests/files/org/openml/test/tasks/1/datasplits.pkl.py3
+tests/files/org/openml/test/tasks/1882/datasplits.pkl.py3
+
# Distribution / packaging
.Python
@@ -56,6 +69,7 @@ nosetests.xml
coverage.xml
*,cover
.hypothesis/
+prof/
# Translations
*.mo
@@ -73,6 +87,28 @@ target/
# IDE
.idea
*.swp
+.vscode
+.cursorignore
+.cursorindexingignore
+
+# MYPY
+.mypy_cache
+dmypy.json
+dmypy.sock
+
+# Tests
+.pytest_cache
+
+# Virtual environments
+oenv/
+venv/
+.env/
+.venv
+.venv/
+
+# Python cache
+__pycache__/
+*.pyc
-# Other
-*.pkl
+# Ruff
+.ruff-cache/
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 000000000..0987bad90
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,48 @@
+default_language_version:
+ python: python3
+files: |
+ (?x)^(
+ openml|
+ tests
+ )/.*\.py$
+repos:
+ - repo: https://github.com/astral-sh/ruff-pre-commit
+ rev: v0.14.10
+ hooks:
+ - id: ruff
+ args: [--fix, --exit-non-zero-on-fix, --no-cache]
+ - id: ruff-format
+ - repo: https://github.com/pre-commit/mirrors-mypy
+ rev: v1.13.0
+ hooks:
+ - id: mypy
+ additional_dependencies:
+ - types-requests
+ - types-python-dateutil
+ - repo: https://github.com/python-jsonschema/check-jsonschema
+ rev: 0.29.4
+ hooks:
+ - id: check-github-workflows
+ files: '^github/workflows/.*\.ya?ml$'
+ types: ["yaml"]
+ - id: check-dependabot
+ files: '^\.github/dependabot\.ya?ml$'
+ - repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v5.0.0
+ hooks:
+ - id: check-added-large-files
+ files: ".*"
+ - id: check-case-conflict
+ files: ".*"
+ - id: check-merge-conflict
+ files: ".*"
+ - id: check-yaml
+ files: ".*"
+ - id: end-of-file-fixer
+ files: ".*"
+ types: ["yaml"]
+ - id: check-toml
+ files: ".*"
+ types: ["toml"]
+ - id: debug-statements
+ files: '^src/.*\.py$'
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index b659bd70f..000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,23 +0,0 @@
-language: python
-
-sudo: false
-
-cache:
- apt: true
- # We use three different cache directory
- # to work around a Travis bug with multi-platform cache
- directories:
- - $HOME/.cache/pip
- - $HOME/download
-env:
- global:
- # Directory where tests are run from
- - TEST_DIR=/tmp/test_dir/
- - MODULE=openml
- matrix:
- - DISTRIB="conda" PYTHON_VERSION="2.7" NUMPY_VERSION="1.10.4" SCIPY_VERSION="0.17.0" CYTHON_VERSION="0.21"
- - DISTRIB="conda" PYTHON_VERSION="3.4" NUMPY_VERSION="1.10.4" SCIPY_VERSION="0.17.0" CYTHON_VERSION="0.23.4"
- - DISTRIB="conda" PYTHON_VERSION="3.5" COVERAGE="true" NUMPY_VERSION="1.10.4" SCIPY_VERSION="0.17.0" CYTHON_VERSION="0.23.4"
-install: source ci_scripts/install.sh
-script: bash ci_scripts/test.sh
-after_success: source ci_scripts/success.sh
diff --git a/CITATION.cff b/CITATION.cff
new file mode 100644
index 000000000..c5454ef6f
--- /dev/null
+++ b/CITATION.cff
@@ -0,0 +1,40 @@
+cff-version: 1.2.0
+message: "If you use this software in a publication, please cite the metadata from preferred-citation."
+preferred-citation:
+ type: article
+ authors:
+ - family-names: "Feurer"
+ given-names: "Matthias"
+ orcid: "https://orcid.org/0000-0001-9611-8588"
+ - family-names: "van Rijn"
+ given-names: "Jan N."
+ orcid: "https://orcid.org/0000-0003-2898-2168"
+ - family-names: "Kadra"
+ given-names: "Arlind"
+ - family-names: "Gijsbers"
+ given-names: "Pieter"
+ orcid: "https://orcid.org/0000-0001-7346-8075"
+ - family-names: "Mallik"
+ given-names: "Neeratyoy"
+ orcid: "https://orcid.org/0000-0002-0598-1608"
+ - family-names: "Ravi"
+ given-names: "Sahithya"
+ - family-names: "Müller"
+ given-names: "Andreas"
+ orcid: "https://orcid.org/0000-0002-2349-9428"
+ - family-names: "Vanschoren"
+ given-names: "Joaquin"
+ orcid: "https://orcid.org/0000-0001-7044-9805"
+ - family-names: "Hutter"
+ given-names: "Frank"
+ orcid: "https://orcid.org/0000-0002-2037-3694"
+ journal: "Journal of Machine Learning Research"
+ title: "OpenML-Python: an extensible Python API for OpenML"
+ abstract: "OpenML is an online platform for open science collaboration in machine learning, used to share datasets and results of machine learning experiments. In this paper, we introduce OpenML-Python, a client API for Python, which opens up the OpenML platform for a wide range of Python-based machine learning tools. It provides easy access to all datasets, tasks and experiments on OpenML from within Python. It also provides functionality to conduct machine learning experiments, upload the results to OpenML, and reproduce results which are stored on OpenML. Furthermore, it comes with a scikit-learn extension and an extension mechanism to easily integrate other machine learning libraries written in Python into the OpenML ecosystem. Source code and documentation are available at https://github.com/openml/openml-python/."
+ volume: 22
+ year: 2021
+ start: 1
+ end: 5
+ pages: 5
+ number: 100
+ url: https://jmlr.org/papers/v22/19-920.html
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 000000000..d194525ef
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,217 @@
+# Contributing to `openml-python`
+This document describes the workflow on how to contribute to the openml-python package.
+If you are interested in connecting a machine learning package with OpenML (i.e.
+write an openml-python extension) or want to find other ways to contribute, see [this page](https://openml.github.io/openml-python/main/contributing.html#contributing).
+
+## Scope of the package
+
+The scope of the OpenML Python package is to provide a Python interface to
+the OpenML platform which integrates well with Python's scientific stack, most
+notably [numpy](http://www.numpy.org/), [scipy](https://www.scipy.org/) and
+[pandas](https://pandas.pydata.org/).
+To reduce opportunity costs and demonstrate the usage of the package, it also
+implements an interface to the most popular machine learning package written
+in Python, [scikit-learn](http://scikit-learn.org/stable/index.html).
+Thereby it will automatically be compatible with many machine learning
+libraries written in Python.
+
+We aim to keep the package as light-weight as possible, and we will try to
+keep the number of potential installation dependencies as low as possible.
+Therefore, the connection to other machine learning libraries such as
+*pytorch*, *keras* or *tensorflow* should not be done directly inside this
+package, but in a separate package using the OpenML Python connector.
+More information on OpenML Python connectors can be found [here](https://openml.github.io/openml-python/main/contributing.html#contributing).
+
+## Determine what contribution to make
+
+Great! You've decided you want to help out. Now what?
+All contributions should be linked to issues on the [GitHub issue tracker](https://github.com/openml/openml-python/issues).
+In particular for new contributors, the *good first issue* label should help you find
+issues which are suitable for beginners. Resolving these issues allows you to start
+contributing to the project without much prior knowledge. Your assistance in this area
+will be greatly appreciated by the more experienced developers as it helps free up
+their time to concentrate on other issues.
+
+If you encounter a particular part of the documentation or code that you want to improve,
+but there is no related open issue yet, open one first.
+This is important since you can first get feedback or pointers from experienced contributors.
+
+To let everyone know you are working on an issue, please leave a comment that states you will work on the issue
+(or, if you have the permission, *assign* yourself to the issue). This avoids double work!
+
+## Contributing Workflow Overview
+To contribute to the openml-python package, follow these steps:
+
+0. Determine how you want to contribute (see above).
+1. Set up your local development environment.
+ 1. Fork and clone the `openml-python` repository. Then, create a new branch from the ``main`` branch. If you are new to `git`, see our [detailed documentation](#basic-git-workflow), or rely on your favorite IDE.
+ 2. [Install the local dependencies](#install-local-dependencies) to run the tests for your contribution.
+ 3. [Test your installation](#testing-your-installation) to ensure everything is set up correctly.
+4. Implement your contribution. If contributing to the documentation, see [here](#contributing-to-the-documentation).
+5. [Create a pull request](#pull-request-checklist).
+
+### Install Local Dependencies
+
+We recommend following the instructions below to install all requirements locally.
+However, it is also possible to use the [openml-python docker image](https://github.com/openml/openml-python/blob/main/docker/readme.md) for testing and building documentation. Moreover, feel free to use any alternative package managers, such as `pip`.
+
+
+1. To ensure a smooth development experience, we recommend using the `uv` package manager. Thus, first install `uv`. If any Python version already exists on your system, follow the steps below, otherwise see [here](https://docs.astral.sh/uv/getting-started/installation/).
+ ```bash
+ pip install uv
+ ```
+2. Create a virtual environment using `uv` and activate it. This will ensure that the dependencies for `openml-python` do not interfere with other Python projects on your system.
+ ```bash
+ uv venv --seed --python 3.8 ~/.venvs/openml-python
+ source ~/.venvs/openml-python/bin/activate
+ pip install uv # Install uv within the virtual environment
+ ```
+3. Then install openml with its test dependencies by running
+ ```bash
+ uv pip install -e .[test]
+ ```
+ from the repository folder.
+ Then configure the pre-commit to be able to run unit tests, as well as [pre-commit](#pre-commit-details) through:
+ ```bash
+ pre-commit install
+ ```
+
+### Testing (Your Installation)
+To test your installation and run the tests for the first time, run the following from the repository folder:
+```bash
+pytest tests
+```
+For Windows systems, you may need to add `pytest` to PATH before executing the command.
+
+Executing a specific unit test can be done by specifying the module, test case, and test.
+You may then run a specific module, test case, or unit test respectively:
+```bash
+pytest tests/test_datasets/test_dataset.py
+pytest tests/test_datasets/test_dataset.py::OpenMLDatasetTest
+pytest tests/test_datasets/test_dataset.py::OpenMLDatasetTest::test_get_data
+```
+
+To test your new contribution, add [unit tests](https://github.com/openml/openml-python/tree/main/tests), and, if needed, [examples](https://github.com/openml/openml-python/tree/main/examples) for any new functionality being introduced. Some notes on unit tests and examples:
+* If a unit test contains an upload to the test server, please ensure that it is followed by a file collection for deletion, to prevent the test server from bulking up. For example, `TestBase._mark_entity_for_removal('data', dataset.dataset_id)`, `TestBase._mark_entity_for_removal('flow', (flow.flow_id, flow.name))`.
+* Please ensure that the example is run on the test server by beginning with the call to `openml.config.start_using_configuration_for_example()`, which is done by default for tests derived from `TestBase`.
+* Add the `@pytest.mark.sklearn` marker to your unit tests if they have a dependency on scikit-learn.
+
+#### Running Tests That Require Admin Privileges
+
+Some tests require admin privileges on the test server and will be automatically skipped unless you provide an admin API key. For regular contributors, the tests will skip gracefully. For core contributors who need to run these tests locally, you can set up the key by exporting the variable as below before running the tests:
+
+```bash
+# For windows
+$env:OPENML_TEST_SERVER_ADMIN_KEY = "admin-key"
+# For linux/mac
+export OPENML_TEST_SERVER_ADMIN_KEY="admin-key"
+```
+
+### Pull Request Checklist
+
+You can go to the `openml-python` GitHub repository to create the pull request by [comparing the branch](https://github.com/openml/openml-python/compare) from your fork with the `main` branch of the `openml-python` repository. When creating a pull request, make sure to follow the comments and structured provided by the template on GitHub.
+
+**An incomplete contribution** -- where you expect to do more work before
+receiving a full review -- should be submitted as a `draft`. These may be useful
+to: indicate you are working on something to avoid duplicated work,
+request broad review of functionality or API, or seek collaborators.
+Drafts often benefit from the inclusion of a
+[task list](https://github.com/blog/1375-task-lists-in-gfm-issues-pulls-comments)
+in the PR description.
+
+---
+
+# Appendix
+
+## Basic `git` Workflow
+
+The preferred workflow for contributing to openml-python is to
+fork the [main repository](https://github.com/openml/openml-python) on
+GitHub, clone, check out the branch `main`, and develop on a new branch
+branch. Steps:
+
+0. Make sure you have git installed, and a GitHub account.
+
+1. Fork the [project repository](https://github.com/openml/openml-python)
+ by clicking on the 'Fork' button near the top right of the page. This creates
+ a copy of the code under your GitHub user account. For more details on
+ how to fork a repository see [this guide](https://help.github.com/articles/fork-a-repo/).
+
+2. Clone your fork of the openml-python repo from your GitHub account to your
+local disk:
+
+ ```bash
+ git clone git@github.com:YourLogin/openml-python.git
+ cd openml-python
+ ```
+
+3. Switch to the ``develop`` branch:
+
+ ```bash
+ git checkout main
+ ```
+
+3. Create a ``feature`` branch to hold your development changes:
+
+ ```bash
+ git checkout -b feature/my-feature
+ ```
+
+ Always use a ``feature`` branch. It's good practice to never work on the ``main`` branch!
+ To make the nature of your pull request easily visible, please prepend the name of the branch with the type of changes you want to merge, such as ``feature`` if it contains a new feature, ``fix`` for a bugfix, ``doc`` for documentation and ``maint`` for other maintenance on the package.
+
+4. Develop the feature on your feature branch. Add changed files using ``git add`` and then ``git commit`` files:
+
+ ```bash
+ git add modified_files
+ git commit
+ ```
+
+ to record your changes in Git, then push the changes to your GitHub account with:
+
+ ```bash
+ git push -u origin my-feature
+ ```
+
+5. Follow [these instructions](https://help.github.com/articles/creating-a-pull-request-from-a-fork)
+to create a pull request from your fork.
+
+(If any of the above seems like magic to you, please look up the
+[Git documentation](https://git-scm.com/documentation) on the web, or ask a friend or another contributor for help.)
+
+
+## Pre-commit Details
+[Pre-commit](https://pre-commit.com/) is used for various style checking and code formatting.
+Before each commit, it will automatically run:
+ - [ruff](https://docs.astral.sh/ruff/) a code formatter and linter.
+ This will automatically format your code.
+ Make sure to take a second look after any formatting takes place,
+ if the resulting code is very bloated, consider a (small) refactor.
+ - [mypy](https://mypy.readthedocs.io/en/stable/) a static type checker.
+ In particular, make sure each function you work on has type hints.
+
+If you want to run the pre-commit tests without doing a commit, run:
+```bash
+$ make check
+```
+or on a system without make, like Windows:
+```bash
+$ pre-commit run --all-files
+```
+Make sure to do this at least once before your first commit to check your setup works.
+
+## Contributing to the Documentation
+
+We welcome all forms of documentation contributions — whether it's Markdown docstrings, tutorials, guides, or general improvements.
+
+Our documentation is written either in Markdown or as a jupyter notebook and lives in the docs/ and examples/ directories of the source code repository.
+
+To preview the documentation locally, you will need to install a few additional dependencies:
+```bash
+uv pip install -e .[examples,docs]
+```
+When dependencies are installed, run
+```bash
+mkdocs serve
+```
+This will open a preview of the website.
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 000000000..e08aa862b
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,68 @@
+BSD 3-Clause License
+
+Copyright (c) 2014-2019, Matthias Feurer, Jan van Rijn, Andreas Müller,
+Joaquin Vanschoren and others.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+* Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+License of the files CONTRIBUTING.md, ISSUE_TEMPLATE.md and
+PULL_REQUEST_TEMPLATE.md:
+
+Those files are modifications of the respecting templates in scikit-learn and
+they are licensed under a New BSD license:
+
+New BSD License
+
+Copyright (c) 2007–2018 The scikit-learn developers.
+All rights reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ a. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ b. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ c. Neither the name of the Scikit-learn Developers nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
diff --git a/Makefile b/Makefile
index 5f334667a..a25e2972c 100644
--- a/Makefile
+++ b/Makefile
@@ -2,26 +2,27 @@
PYTHON ?= python
CYTHON ?= cython
-NOSETESTS ?= nosetests
+PYTEST ?= pytest
CTAGS ?= ctags
all: clean inplace test
+check:
+ pre-commit run --all-files
+
clean:
$(PYTHON) setup.py clean
- rm -rf dist
+ rm -rf dist openml.egg-info
in: inplace # just a shortcut
inplace:
$(PYTHON) setup.py build_ext -i
test-code: in
- $(NOSETESTS) -s -v tests
-test-doc:
- $(NOSETESTS) -s -v doc/*.rst
+ $(PYTEST) -s -v tests
test-coverage:
rm -rf coverage .coverage
- $(NOSETESTS) -s -v --with-coverage tests
+ $(PYTEST) -s -v --cov=. tests
-test: test-code test-sphinxext test-doc
+test: test-code
diff --git a/README.md b/README.md
index 9c4487375..974c9fa53 100644
--- a/README.md
+++ b/README.md
@@ -1,16 +1,102 @@
-A python interface for [OpenML](http://openml.org). You can find the documentation on the [openml-python website](https://openml.github.io/openml-python).
-Please commit to the right branches following the gitflow pattern:
-http://nvie.com/posts/a-successful-git-branching-model/
-Master branch:
+
-[](https://travis-ci.org/openml/openml-python)
-[](https://landscape.io/github/openml/openml-python/master)
-[](https://coveralls.io/github/openml/openml-python?branch=master)
+
+
+
+
+ OpenML-Python
+
+
+
+
-Development branch:
+## The Python API for a World of Data and More :dizzy:
-[](https://travis-ci.org/openml/openml-python)
-[](https://landscape.io/github/openml/openml-python/master)
-[](https://coveralls.io/github/openml/openml-python?branch=develop)
+[](https://github.com/openml/openml-python/releases)
+[](https://pypi.org/project/openml/)
+[](https://pepy.tech/project/openml)
+[](https://opensource.org/licenses/BSD-3-Clause)
+
+
+[Installation](https://openml.github.io/openml-python/main/#how-to-get-openml-for-python) | [Documentation](https://openml.github.io/openml-python) | [Contribution guidelines](https://github.com/openml/openml-python/blob/main/CONTRIBUTING.md)
+
+
+OpenML-Python provides an easy-to-use and straightforward Python interface for [OpenML](http://openml.org), an online platform for open science collaboration in machine learning.
+It can download or upload data from OpenML, such as datasets and machine learning experiment results.
+
+## :joystick: Minimal Example
+
+Use the following code to get the [credit-g](https://www.openml.org/search?type=data&sort=runs&status=active&id=31) [dataset](https://docs.openml.org/concepts/data/):
+
+```python
+import openml
+
+dataset = openml.datasets.get_dataset("credit-g") # or by ID get_dataset(31)
+X, y, categorical_indicator, attribute_names = dataset.get_data(target="class")
+```
+
+Get a [task](https://docs.openml.org/concepts/tasks/) for [supervised classification on credit-g](https://www.openml.org/search?type=task&id=31&source_data.data_id=31):
+
+```python
+import openml
+
+task = openml.tasks.get_task(31)
+dataset = task.get_dataset()
+X, y, categorical_indicator, attribute_names = dataset.get_data(target=task.target_name)
+# get splits for the first fold of 10-fold cross-validation
+train_indices, test_indices = task.get_train_test_split_indices(fold=0)
+```
+
+Use an [OpenML benchmarking suite](https://docs.openml.org/concepts/benchmarking/) to get a curated list of machine-learning tasks:
+```python
+import openml
+
+suite = openml.study.get_suite("amlb-classification-all") # Get a curated list of tasks for classification
+for task_id in suite.tasks:
+ task = openml.tasks.get_task(task_id)
+```
+
+## :magic_wand: Installation
+
+OpenML-Python is supported on Python 3.10 - 3.14 and is available on Linux, MacOS, and Windows.
+
+You can install OpenML-Python with:
+
+```bash
+pip install openml
+```
+
+## :page_facing_up: Citing OpenML-Python
+
+If you use OpenML-Python in a scientific publication, we would appreciate a reference to the following paper:
+
+[Matthias Feurer, Jan N. van Rijn, Arlind Kadra, Pieter Gijsbers, Neeratyoy Mallik, Sahithya Ravi, Andreas Müller, Joaquin Vanschoren, Frank Hutter
+**OpenML-Python: an extensible Python API for OpenML**
+Journal of Machine Learning Research, 22(100):1−5, 2021](https://www.jmlr.org/papers/v22/19-920.html)
+
+Bibtex entry:
+```bibtex
+@article{JMLR:v22:19-920,
+ author = {Matthias Feurer and Jan N. van Rijn and Arlind Kadra and Pieter Gijsbers and Neeratyoy Mallik and Sahithya Ravi and Andreas Müller and Joaquin Vanschoren and Frank Hutter},
+ title = {OpenML-Python: an extensible Python API for OpenML},
+ journal = {Journal of Machine Learning Research},
+ year = {2021},
+ volume = {22},
+ number = {100},
+ pages = {1--5},
+ url = {http://jmlr.org/papers/v22/19-920.html}
+}
+```
+## :handshake: Contributing
+
+We welcome contributions from both new and experienced developers!
+
+If you would like to contribute to OpenML-Python, please read our
+[Contribution Guidelines](https://github.com/openml/openml-python/blob/main/CONTRIBUTING.md).
+
+If you are new to open-source development, a great way to get started is by
+looking at issues labeled **"good first issue"** in our GitHub issue tracker.
+These tasks are beginner-friendly and help you understand the project structure,
+development workflow, and how to submit a pull request.
diff --git a/ci_scripts/install.sh b/ci_scripts/install.sh
deleted file mode 100644
index 956f219c1..000000000
--- a/ci_scripts/install.sh
+++ /dev/null
@@ -1,40 +0,0 @@
-# Deactivate the travis-provided virtual environment and setup a
-# conda-based environment instead
-deactivate
-
-# Use the miniconda installer for faster download / install of conda
-# itself
-pushd .
-cd
-mkdir -p download
-cd download
-echo "Cached in $HOME/download :"
-ls -l
-echo
-if [[ ! -f miniconda.sh ]]
- then
- wget http://repo.continuum.io/miniconda/Miniconda-3.6.0-Linux-x86_64.sh \
- -O miniconda.sh
- fi
-chmod +x miniconda.sh && ./miniconda.sh -b
-cd ..
-export PATH=/home/travis/miniconda/bin:$PATH
-conda update --yes conda
-popd
-
-# Configure the conda environment and put it in the path using the
-# provided versions
-conda create -n testenv --yes python=$PYTHON_VERSION pip nose \
- numpy=$NUMPY_VERSION scipy=$SCIPY_VERSION cython=$CYTHON_VERSION matplotlib scikit-learn nbconvert nbformat jupyter_client ipython jupyter notebook ipykernel pandas
-source activate testenv
-ipython kernel install
-
-
-if [[ "$COVERAGE" == "true" ]]; then
- pip install coverage coveralls
-fi
-
-python --version
-python -c "import numpy; print('numpy %s' % numpy.__version__)"
-python -c "import scipy; print('scipy %s' % scipy.__version__)"
-python setup.py develop
diff --git a/ci_scripts/push_doc.sh b/ci_scripts/push_doc.sh
deleted file mode 100644
index 3fa944b64..000000000
--- a/ci_scripts/push_doc.sh
+++ /dev/null
@@ -1,42 +0,0 @@
-#!/bin/bash
-# This script is meant to be called in the "deploy" step defined in
-# circle.yml. See https://circleci.com/docs/ for more details.
-# The behavior of the script is controlled by environment variable defined
-# in the circle.yml in the top level folder of the project.
-
-if [ ! -z "$1" ]
- then DOC_FOLDER=$1
-fi
-
-MSG="Pushing the docs for revision for branch: $CIRCLE_BRANCH, commit $CIRCLE_SHA1, folder: $DOC_FOLDER"
-
-cd $HOME
-
-# Clone the docs repo if it isnt already there
-if [ ! -d $DOC_REPO ];
- then git clone "git@github.com:$USERNAME/"$DOC_REPO".git";
-fi
-
-# Copy the build docs to a temporary folder
-rm -rf tmp
-mkdir tmp
-cp -R $HOME/$DOC_REPO/doc/build/html/* ./tmp/
-
-cd $DOC_REPO
-git branch gh-pages
-git checkout -f gh-pages
-git reset --hard origin/gh-pages
-git clean -dfx
-git rm -rf $HOME/$DOC_REPO/$DOC_FOLDER && rm -rf $HOME/$DOC_REPO/$DOC_FOLDER
-
-# Copy the new build docs
-mkdir $DOC_FOLDER
-cp -R $HOME/tmp/* ./$DOC_FOLDER/
-
-git config --global user.email $EMAIL
-git config --global user.name $USERNAME
-git add -f ./$DOC_FOLDER/
-git commit -m "$MSG"
-git push -f origin gh-pages
-
-echo $MSG
\ No newline at end of file
diff --git a/ci_scripts/success.sh b/ci_scripts/success.sh
deleted file mode 100644
index 043da742f..000000000
--- a/ci_scripts/success.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-set -e
-
-if [[ "$COVERAGE" == "true" ]]; then
- # Need to run coveralls from a git checkout, so we copy .coverage
- # from TEST_DIR where nosetests has been run
- cp $TEST_DIR/.coverage $TRAVIS_BUILD_DIR
- cd $TRAVIS_BUILD_DIR
- # Ignore coveralls failures as the coveralls server is not
- # very reliable but we don't want travis to report a failure
- # in the github UI just because the coverage report failed to
- # be published.
- coveralls || echo "Coveralls upload failed"
-fi
\ No newline at end of file
diff --git a/ci_scripts/test.sh b/ci_scripts/test.sh
deleted file mode 100644
index bb4915054..000000000
--- a/ci_scripts/test.sh
+++ /dev/null
@@ -1,16 +0,0 @@
-set -e
-
-# Get into a temp directory to run test from the installed scikit learn and
-# check if we do not leave artifacts
-mkdir -p $TEST_DIR
-
-cwd=`pwd`
-test_dir=$cwd/tests
-
-cd $TEST_DIR
-
-if [[ "$COVERAGE" == "true" ]]; then
- nosetests -sv --with-coverage --cover-package=$MODULE $test_dir
-else
- nosetests -sv $test_dir
-fi
diff --git a/circle.yml b/circle.yml
deleted file mode 100644
index cc61c92a1..000000000
--- a/circle.yml
+++ /dev/null
@@ -1,65 +0,0 @@
-machine:
- environment:
- # The github organization or username of the repository which hosts the
- # project and documentation.
- USERNAME: "openml"
-
- # The repository where the documentation will be hosted
- DOC_REPO: "openml-python"
-
- # The base URL for the Github page where the documentation will be hosted
- DOC_URL: ""
-
- # The email is to be used for commits in the Github Page
- EMAIL: "feurerm@informatik.uni-freiburg.de"
-
-dependencies:
-
- # Various dependencies
- pre:
- - sudo -E apt-get -yq remove texlive-binaries --purge
- - sudo apt-get update
- - sudo apt-get install libatlas-dev libatlas3gf-base
- - sudo apt-get install build-essential python-dev python-setuptools
- # install numpy first as it is a compile time dependency for other packages
- - pip install --upgrade pip
- - pip install --upgrade numpy
- - pip install --upgrade scipy
- - pip install git+https://github.com/mfeurer/liac-arff.git
- # install documentation building dependencies
- - pip install --upgrade matplotlib setuptools nose coverage sphinx pillow sphinx-gallery sphinx_bootstrap_theme cython numpydoc scikit-learn nbformat nbconvert
- # Installing required packages for `make -C doc check command` to work.
- - sudo -E apt-get -yq update
- - sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install dvipng texlive-latex-base texlive-latex-extra
- # finally install the requirements of the package to allow autodoc
- - pip install -r requirements.txt
-
- # The --user is needed to let sphinx see the source and the binaries
- # The pipefail is requested to propagate exit code
- override:
- - python setup.py clean
- - python setup.py develop
- - set -o pipefail && cd doc && make html 2>&1 | tee ~/log.txt
-test:
- # Grep error on the documentation
- override:
- - cat ~/log.txt && if grep -q "Traceback (most recent call last):" ~/log.txt; then false; else true; fi
-deployment:
- master:
- branch: master
- commands:
- - bash ci_scripts/push_doc.sh 'stable'
- development:
- branch: develop
- commands:
- - bash ci_scripts/push_doc.sh 'dev'
-general:
- # Open the doc to the API
- artifacts:
- - "doc/_build/html"
- - "~/log.txt"
- # Restric the build to the branch master only
- branches:
- only:
- - master
- - develop
diff --git a/doc/.nojekyll b/doc/.nojekyll
deleted file mode 100644
index 8b1378917..000000000
--- a/doc/.nojekyll
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/doc/Makefile b/doc/Makefile
deleted file mode 100644
index c27605ff1..000000000
--- a/doc/Makefile
+++ /dev/null
@@ -1,180 +0,0 @@
-# Makefile for Sphinx documentation
-#
-
-# You can set these variables from the command line.
-SPHINXOPTS =
-SPHINXBUILD = sphinx-build
-PAPER =
-BUILDDIR = build
-
-# User-friendly check for sphinx-build
-ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
-$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
-endif
-
-# Internal variables.
-PAPEROPT_a4 = -D latex_paper_size=a4
-PAPEROPT_letter = -D latex_paper_size=letter
-ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
-# the i18n builder cannot share the environment and doctrees with the others
-I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
-
-.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
-
-all: html
-
-help:
- @echo "Please use \`make ' where is one of"
- @echo " html to make standalone HTML files"
- @echo " dirhtml to make HTML files named index.html in directories"
- @echo " singlehtml to make a single large HTML file"
- @echo " pickle to make pickle files"
- @echo " json to make JSON files"
- @echo " htmlhelp to make HTML files and a HTML help project"
- @echo " qthelp to make HTML files and a qthelp project"
- @echo " devhelp to make HTML files and a Devhelp project"
- @echo " epub to make an epub"
- @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
- @echo " latexpdf to make LaTeX files and run them through pdflatex"
- @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
- @echo " text to make text files"
- @echo " man to make manual pages"
- @echo " texinfo to make Texinfo files"
- @echo " info to make Texinfo files and run them through makeinfo"
- @echo " gettext to make PO message catalogs"
- @echo " changes to make an overview of all changed/added/deprecated items"
- @echo " xml to make Docutils-native XML files"
- @echo " pseudoxml to make pseudoxml-XML files for display purposes"
- @echo " linkcheck to check all external links for integrity"
- @echo " doctest to run all doctests embedded in the documentation (if enabled)"
-
-clean:
- rm -rf $(BUILDDIR)/*
- rm -rf generated/
-
-html:
- $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
- @echo
- @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
-
-dirhtml:
- $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
- @echo
- @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
-
-singlehtml:
- $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
- @echo
- @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
-
-pickle:
- $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
- @echo
- @echo "Build finished; now you can process the pickle files."
-
-json:
- $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
- @echo
- @echo "Build finished; now you can process the JSON files."
-
-htmlhelp:
- $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
- @echo
- @echo "Build finished; now you can run HTML Help Workshop with the" \
- ".hhp project file in $(BUILDDIR)/htmlhelp."
-
-qthelp:
- $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
- @echo
- @echo "Build finished; now you can run "qcollectiongenerator" with the" \
- ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
- @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/OpenML.qhcp"
- @echo "To view the help file:"
- @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/OpenML.qhc"
-
-devhelp:
- $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
- @echo
- @echo "Build finished."
- @echo "To view the help file:"
- @echo "# mkdir -p $$HOME/.local/share/devhelp/OpenML"
- @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/OpenML"
- @echo "# devhelp"
-
-epub:
- $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
- @echo
- @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
-
-latex:
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
- @echo
- @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
- @echo "Run \`make' in that directory to run these through (pdf)latex" \
- "(use \`make latexpdf' here to do that automatically)."
-
-latexpdf:
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
- @echo "Running LaTeX files through pdflatex..."
- $(MAKE) -C $(BUILDDIR)/latex all-pdf
- @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
-
-latexpdfja:
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
- @echo "Running LaTeX files through platex and dvipdfmx..."
- $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
- @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
-
-text:
- $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
- @echo
- @echo "Build finished. The text files are in $(BUILDDIR)/text."
-
-man:
- $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
- @echo
- @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
-
-texinfo:
- $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
- @echo
- @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
- @echo "Run \`make' in that directory to run these through makeinfo" \
- "(use \`make info' here to do that automatically)."
-
-info:
- $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
- @echo "Running Texinfo files through makeinfo..."
- make -C $(BUILDDIR)/texinfo info
- @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
-
-gettext:
- $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
- @echo
- @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
-
-changes:
- $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
- @echo
- @echo "The overview file is in $(BUILDDIR)/changes."
-
-linkcheck:
- $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
- @echo
- @echo "Link check complete; look for any errors in the above output " \
- "or in $(BUILDDIR)/linkcheck/output.txt."
-
-doctest:
- $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
- @echo "Testing of doctests in the sources finished, look at the " \
- "results in $(BUILDDIR)/doctest/output.txt."
-
-xml:
- $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
- @echo
- @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
-
-pseudoxml:
- $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
- @echo
- @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
diff --git a/doc/_templates/class.rst b/doc/_templates/class.rst
deleted file mode 100644
index 307b0199c..000000000
--- a/doc/_templates/class.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-:mod:`{{module}}`.{{objname}}
-{{ underline }}==============
-
-.. currentmodule:: {{ module }}
-
-.. autoclass:: {{ objname }}
diff --git a/doc/_templates/class_without_init.rst b/doc/_templates/class_without_init.rst
deleted file mode 100644
index 79ff2cf80..000000000
--- a/doc/_templates/class_without_init.rst
+++ /dev/null
@@ -1,12 +0,0 @@
-:mod:`{{module}}`.{{objname}}
-{{ underline }}==============
-
-.. currentmodule:: {{ module }}
-
-.. autoclass:: {{ objname }}
-
-.. include:: {{module}}.{{objname}}.examples
-
-.. raw:: html
-
-
diff --git a/doc/_templates/function.rst b/doc/_templates/function.rst
deleted file mode 100644
index d8c9bd480..000000000
--- a/doc/_templates/function.rst
+++ /dev/null
@@ -1,10 +0,0 @@
-:mod:`{{module}}`.{{objname}}
-{{ underline }}====================
-
-.. currentmodule:: {{ module }}
-
-.. autofunction:: {{ objname }}
-
-.. raw:: html
-
-
diff --git a/doc/_templates/layout.html b/doc/_templates/layout.html
deleted file mode 100644
index 11777457e..000000000
--- a/doc/_templates/layout.html
+++ /dev/null
@@ -1,23 +0,0 @@
-{% extends "!layout.html" %}
-
-{# Custom CSS overrides #}
-{# set bootswatch_css_custom = ['_static/my-styles.css'] #}
-
-{# Add github banner (from: https://github.com/blog/273-github-ribbons). #}
-{% block header %}
- {{ super() }}
-
-
-{% endblock %}
-
diff --git a/doc/api.rst b/doc/api.rst
deleted file mode 100644
index 79d59577c..000000000
--- a/doc/api.rst
+++ /dev/null
@@ -1,71 +0,0 @@
-:orphan:
-
-.. _api:
-
-APIs
-****
-
-Top-level Classes
------------------
-.. currentmodule:: openml
-
-.. autosummary::
- :toctree: generated/
- :template: class.rst
-
- OpenMLDataset
- OpenMLRun
- OpenMLTask
- OpenMLSplit
- OpenMLFlow
-
-
-:mod:`openml.datasets`: Dataset Functions
------------------------------------------
-.. currentmodule:: openml.datasets
-
-.. autosummary::
- :toctree: generated/
- :template: function.rst
-
- check_datasets_active
- get_dataset
- get_datasets
- list_datasets
-
-:mod:`openml.runs`: Run Functions
-----------------------------------
-.. currentmodule:: openml.runs
-
-.. autosummary::
- :toctree: generated/
- :template: function.rst
-
- run_task
- get_run
- list_runs
- list_runs_by_flow
- list_runs_by_tag
- list_runs_by_task
- list_runs_by_uploader
- list_runs_by_filters
-
-:mod:`openml.tasks`: Task Functions
------------------------------------
-.. currentmodule:: openml.tasks
-
-.. autosummary::
- :toctree: generated/
- :template: function.rst
-
- get_task
- list_tasks
-
-:mod:`openml.flows`: Flow Functions
------------------------------------
-.. currentmodule:: openml.flow
-
-.. autosummary::
- :toctree: generated/
- :template: function.rst
-
diff --git a/doc/conf.py b/doc/conf.py
deleted file mode 100644
index a7cb43f7b..000000000
--- a/doc/conf.py
+++ /dev/null
@@ -1,332 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# OpenML documentation build configuration file, created by
-# sphinx-quickstart on Wed Nov 26 10:46:10 2014.
-#
-# This file is execfile()d with the current directory set to its
-# containing dir.
-#
-# Note that not all possible configuration values are present in this
-# autogenerated file.
-#
-# All configuration values have a default; values that are commented out
-# serve to show the default.
-
-import os
-import sys
-import sphinx_bootstrap_theme
-import openml
-
-
-# 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.
-#sys.path.insert(0, os.path.abspath('.')# )
-
-sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
-sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
-
-# -- General configuration ------------------------------------------------
-
-# If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
-
-# 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.ext.autosummary',
- 'sphinx.ext.doctest',
- 'sphinx.ext.coverage',
- 'sphinx.ext.mathjax',
- 'sphinx.ext.ifconfig',
- 'numpydoc'
-]
-
-autosummary_generate = True
-numpydoc_show_class_members = False
-
-autodoc_default_flags = ['members', 'inherited-members']
-
-# Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
-
-# The suffix of source filenames.
-source_suffix = '.rst'
-
-# The encoding of source files.
-#source_encoding = 'utf-8-sig'
-
-# The master toctree document.
-master_doc = 'index'
-
-# General information about the project.
-project = u'OpenML'
-copyright = u'2014-2016, Matthias Feurer, Andreas Müller, Farzan Majdani, ' \
- u'Joaquin Vanschoren and Pieter Gijsbers'
-
-# The version info for the project you're documenting, acts as replacement for
-# |version| and |release|, also used in various other places throughout the
-# built documents.
-#
-# The short X.Y version.
-version = openml.__version__
-# The full version, including alpha/beta/rc tags.
-release = openml.__version__
-
-# The language for content autogenerated by Sphinx. Refer to documentation
-# for a list of supported languages.
-#language = None
-
-# There are two options for replacing |today|: either, you set today to some
-# non-false value, then it is used:
-#today = ''
-# Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
-
-# List of patterns, relative to source directory, that match files and
-# directories to ignore when looking for source files.
-exclude_patterns = ['_build', '_templates', '_static']
-
-# The reST default role (used for this markup: `text`) to use for all
-# documents.
-#default_role = None
-
-# If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
-
-# If true, the current module name will be prepended to all description
-# unit titles (such as .. function::).
-#add_module_names = True
-
-# If true, sectionauthor and moduleauthor directives will be shown in the
-# output. They are ignored by default.
-#show_authors = False
-
-# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
-
-# A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
-
-# If true, keep warnings as "system message" paragraphs in the built documents.
-#keep_warnings = False
-
-
-# -- 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 = 'bootstrap'
-
-html_theme_options = {
- # Navigation bar title. (Default: ``project`` value)
- 'navbar_title': "OpenML",
-
- # Tab name for entire site. (Default: "Site")
- #'navbar_site_name': "Site",
-
- # A list of tuples containting pages to link to. The value should
- # be in the form [(name, page), ..]
- 'navbar_links': [
- ('Start', 'index'),
- ('API', 'api'),
- ('User Guide', 'usage'),
- ('Progress', 'progress'),
- ],
-
- # Render the next and previous page links in navbar. (Default: true)
- 'navbar_sidebarrel': False,
-
- # Render the current pages TOC in the navbar. (Default: true)
- 'navbar_pagenav': False,
-
- # Tab name for the current pages TOC. (Default: "Page")
- 'navbar_pagenav_name': "On this page",
-
- # Global TOC depth for "site" navbar tab. (Default: 1)
- # Switching to -1 shows all levels.
- 'globaltoc_depth': 1,
-
- # Include hidden TOCs in Site navbar?
- #
- # Note: If this is "false", you cannot have mixed ``:hidden:`` and
- # non-hidden ``toctree`` directives in the same page, or else the build
- # will break.
- #
- # Values: "true" (default) or "false"
- 'globaltoc_includehidden': "false",
-
- # HTML navbar class (Default: "navbar") to attach to element.
- # For black navbar, do "navbar navbar-inverse"
- 'navbar_class': "navbar",
-
- # Fix navigation bar to top of page?
- # Values: "true" (default) or "false"
- 'navbar_fixed_top': "true",
-
- # Location of link to source.
- # Options are "nav" (default), "footer" or anything else to exclude.
- 'source_link_position': "None",
-
- # Bootswatch (http://bootswatch.com/) theme.
- #
- # Options are nothing with "" (default) or the name of a valid theme
- # such as "amelia" or "cosmo".
- 'bootswatch_theme': "flatly",
-
- # Choose Bootstrap version.
- # Values: "3" (default) or "2" (in quotes)
- 'bootstrap_version': "3",
-}
-
-# Add any paths that contain custom themes here, relative to this directory.
-html_theme_path = sphinx_bootstrap_theme.get_html_theme_path()
-
-# The name for this set of Sphinx documents. If None, it defaults to
-# "
v documentation".
-#html_title = None
-
-# A shorter title for the navigation bar. Default is the same as html_title.
-#html_short_title = None
-
-# The name of an image file (relative to this directory) to place at the top
-# of the sidebar.
-#html_logo = None
-
-# The name of an image file (within the static path) to use as favicon of the
-# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
-# pixels large.
-#html_favicon = None
-
-# 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']
-
-# Add any extra paths that contain custom files (such as robots.txt or
-# .htaccess) here, relative to this directory. These files are copied
-# directly to the root of the documentation.
-#html_extra_path = []
-
-# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
-# using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
-
-# If true, SmartyPants will be used to convert quotes and dashes to
-# typographically correct entities.
-#html_use_smartypants = True
-
-# Custom sidebar templates, maps document names to template names.
-html_sidebars = {'**': ['localtoc.html']}
-
-# Additional templates that should be rendered to pages, maps page names to
-# template names.
-#html_additional_pages = {}
-
-# If false, no module index is generated.
-#html_domain_indices = True
-
-# If false, no index is generated.
-#html_use_index = True
-
-# If true, the index is split into individual pages for each letter.
-#html_split_index = False
-
-# If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
-
-# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
-
-# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
-
-# If true, an OpenSearch description file will be output, and all pages will
-# contain a tag referring to it. The value of this option must be the
-# base URL from which the finished HTML is served.
-#html_use_opensearch = ''
-
-# This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
-
-# Output file base name for HTML help builder.
-htmlhelp_basename = 'OpenMLdoc'
-
-
-# -- Options for LaTeX output ---------------------------------------------
-
-latex_elements = {
- # The paper size ('letterpaper' or 'a4paper').
- #'papersize': 'letterpaper',
-
- # The font size ('10pt', '11pt' or '12pt').
- #'pointsize': '10pt',
-
- # Additional stuff for the LaTeX preamble.
- #'preamble': '',
-}
-
-# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title,
-# author, documentclass [howto, manual, or own class]).
-latex_documents = [('index', 'OpenML.tex', u'OpenML Documentation',
- u'Matthias Feurer', 'manual'), ]
-
-# The name of an image file (relative to this directory) to place at the top of
-# the title page.
-#latex_logo = None
-
-# For "manual" documents, if this is true, then toplevel headings are parts,
-# not chapters.
-#latex_use_parts = False
-
-# If true, show page references after internal links.
-#latex_show_pagerefs = False
-
-# If true, show URL addresses after external links.
-#latex_show_urls = False
-
-# Documents to append as an appendix to all manuals.
-#latex_appendices = []
-
-# If false, no module index is generated.
-#latex_domain_indices = True
-
-
-# -- Options for manual page output ---------------------------------------
-
-# One entry per manual page. List of tuples
-# (source start file, name, description, authors, manual section).
-man_pages = [
- ('index', 'openml', u'OpenML Documentation',
- [u'Matthias Feurer'], 1)
-]
-
-# If true, show URL addresses after external links.
-#man_show_urls = False
-
-
-# -- Options for Texinfo output -------------------------------------------
-
-# Grouping the document tree into Texinfo files. List of tuples
-# (source start file, target name, title, author,
-# dir menu entry, description, category)
-texinfo_documents = [
- ('index', 'OpenML', u'OpenML Documentation',
- u'Matthias Feurer', 'OpenML', 'One line description of project.',
- 'Miscellaneous'),
-]
-
-# Documents to append as an appendix to all manuals.
-#texinfo_appendices = []
-
-# If false, no module index is generated.
-#texinfo_domain_indices = True
-
-# How to display URL addresses: 'footnote', 'no', or 'inline'.
-#texinfo_show_urls = 'footnote'
-
-# If true, do not generate a @detailmenu in the "Top" node's menu.
-#texinfo_no_detailmenu = False
diff --git a/doc/developing.rst b/doc/developing.rst
deleted file mode 100644
index 9240a602b..000000000
--- a/doc/developing.rst
+++ /dev/null
@@ -1,19 +0,0 @@
-:orphan:
-
-.. _developing:
-
-
-Updating the API key for travis-ci
-**********************************
-
-OpenML uses an API key to authenticate a user. The API repository also needs an
-API key in order to run tests against the OpenML server. The API key used for
-the tests are linked to a special test user. Since API keys are private, we have
-to use private environment variables for travis-ci. The API key is stored in an
-environment variable `OPENMLAPIKEY` in travis-ci. To encrypt an API key for use
-on travis-ci use the following command to create a private string to put into
-the `.travis.yml` file
-
-.. code:: bash
-
- travis encrypt OPENMLAPIKEY=secretvalue --add
\ No newline at end of file
diff --git a/doc/index.rst b/doc/index.rst
deleted file mode 100644
index ef2f0cd50..000000000
--- a/doc/index.rst
+++ /dev/null
@@ -1,63 +0,0 @@
-.. OpenML documentation master file, created by
- sphinx-quickstart on Wed Nov 26 10:46:10 2014.
- You can adapt this file completely to your liking, but it should at least
- contain the root `toctree` directive.
-
-Welcome to OpenML's documentation!
-==================================
-
-The OpenML module is still under development. You can watch its progress
-:ref:`here `.
-
-
-------------
-Introduction
-------------
-
-How to get OpenML for python
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Currently, the OpenML package for python is only available from
-`github `_.
-
-.. code:: bash
-
- git clone https://github.com/openml/openml-python.git
-
-Installation
-~~~~~~~~~~~~
-
-Once you cloned the package, change into the new directory ``python`` and
-execute
-
-.. code:: bash
-
- python setup.py install
-
-Testing
-~~~~~~~
-
-From within the directory of the cloned package, execute
-
-.. code:: bash
-
- python setup.py test
-
-Usage
-~~~~~
-
-* :ref:`usage`
-* :ref:`api`
-* :ref:`developing`
-
-Contributing
-~~~~~~~~~~~~
-
-Contribution to the OpenML package is highly appreciated. Currently,
-there is a lot of work left on implementing API calls,
-testing them and providing examples to allow new users to easily use the
-OpenML package. See the :ref:`progress` page for open tasks.
-
-Please contact `Matthias `_
-prior to start working on an issue or missing feature to avoid duplicate work
-. Please check the current implementations of the API calls and the method
diff --git a/doc/progress.rst b/doc/progress.rst
deleted file mode 100644
index 6681f51b3..000000000
--- a/doc/progress.rst
+++ /dev/null
@@ -1,78 +0,0 @@
-:orphan:
-
-.. _progress:
-
-========
-Progress
-========
-
-Changelog
-=========
-
-0.3.0
-~~~~~
-
-* Add this changelog (Matthias Feurer)
-* 2nd example notebook PyOpenML.ipynb (Joaquin Vanschoren)
-* Pagination support for list datasets and list tasks
-
-API calls
-=========
-
-=============================================== =========== ====== =============== ========== =====================
-API call implemented tested properly tested loads json proper error handling
-=============================================== =========== ====== =============== ========== =====================
-/data/{id} yes yes
-/data/features/{id} yes yes
-/data/qualities/{id} yes yes
-/data/list/ yes yes
-/data/list/tag/{tag} yes yes
-/data/upload/ yes yes
-/data/tag
-/data/untag
-/data/delete/ X
-
-/task/{task} yes yes
-/task/list yes yes
-/task/list/type/{id} yes yes
-/task/list/tag/{tag} yes yes
-/task {POST}
-/task/tag
-/task/untag
-/task/delete X
-
-/tasktype/{id}
-/tasktype/list
-
-/flow/{id}
-/flow/exists/{name}/{ext_version} yes
-/flow/list yes
-/flow/list/tag/{tag}
-/flow/owned
-/flow/ {POST} yes yes
-/flow/tag
-/flow/untag
-/flow/{id} {DELETE} X
-
-/run/list/task/{ids} yes yes
-/run/list/run/{ids} yes yes
-/run/list/tag/{tag} yes yes
-/run/{id} yes yes
-/run/list/uploader/{ids} yes yes
-/run/list/flow/{ids} yes yes
-/run/list/{filters} yes yes
-/run/untag
-/run (POST) yes yes
-/run/tag
-/run/{id} (DELETE) X
-
-/evaluation/list/run/{ids}
-/evaluation/list/tag/{tag}
-/evaluation/list/task/{ids}
-/evaluation/list/uploader/{ids}
-/evaluation/list/flow/{ids}
-/evaluation/list/{filters}
-
-=============================================== =========== ====== =============== ========== =====================
-
-We do not plan to implement API calls marked with an **X**!
diff --git a/doc/usage.rst b/doc/usage.rst
deleted file mode 100644
index 498ac2533..000000000
--- a/doc/usage.rst
+++ /dev/null
@@ -1,544 +0,0 @@
-:orphan:
-
-.. _usage:
-
-.. role:: bash(code)
- :language: bash
-
-.. role:: python(code)
- :language: python
-
-***********
-Basic Usage
-***********
-
-This document will guide you through the most important functions and classes
-in the OpenML Python API. Throughout this document, we will use
-`pandas `_ to format and filter tables.
-
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Connecting to the OpenML server
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The OpenML server can only be accessed by users who have signed up on the OpenML
-platform. If you don't have an account yet,
-`sign up now `_. You will receive an API key, which
-will authenticate you to the server and allow you to download and upload
-datasets, tasks, runs and flows. There are two ways of providing the API key
-to the OpenML API package. The first option is to specify the API key
-programmatically after loading the package:
-
-.. code:: python
-
- >>> import openml
-
- >>> apikey = 'Your API key'
- >>> openml.config.apikey = apikey
-
-The second option is to create a config file:
-
-.. code:: bash
-
- apikey = qxlfpbeaudtprb23985hcqlfoebairtd
-
-The config file must be in the directory :bash:`~/.openml/config` and
-exist prior to importing the openml module.
-
-When downloading datasets, tasks, runs and flows, they will be cached to
-retrieve them without calling the server later. As with the API key, the cache
-directory can be either specified through the API or through the config file:
-
-API:
-
-.. code:: python
-
- >>> openml.config.set_cache_directory('~/.openml/cache')
-
-Config file:
-
-.. code:: bash
-
- cachedir = '~/.openml/cache'
-
-~~~~~~~~~~~~~~~~~~~~~
-Working with datasets
-~~~~~~~~~~~~~~~~~~~~~
-
-# TODO mention third, searching for tags
-Datasets are a key concept in OpenML (see `OpenML documentation `_).
-Datasets are identified by IDs and can be accessed in two different ways:
-
-1. In a list providing basic information on all datasets available on OpenML.
- This function will not download the actual dataset, but will instead download
- meta data which can be used to filter the datasets and retrieve a set of IDs.
-2. A single dataset by its ID. A single dataset contains all meta information and the actual
- data in form of an .arff file. The .arff file will be converted into a numpy
- array by the OpenML Python API.
-
-Listing datasets
-~~~~~~~~~~~~~~~~
-
-A common task when using OpenML is to find a set of datasets which fulfill
-several criteria. They should for example have between 1,000 and 10,000
-data points and at least five features.
-
-.. code:: python
-
- >>> datasets = openml.datasets.list_datasets()
-
-:meth:`openml.datasets.list_datasets` returns a list of dictionaries, we will
-convert it into a `pandas dataframe `_
-to have better visualization:
-
-.. code:: python
-
- >>> import pandas as pd
- >>> datasets = pd.DataFrame(datasets)
- >>> datasets.set_index('did', inplace=True)
-
-We have access to the following properties of the datasets:
-
- >>> print(datasets.columns)
- Index([ u'MajorityClassSize',
- u'MaxNominalAttDistinctValues',
- u'MinorityClassSize',
- u'NumBinaryAtts',
- u'NumberOfClasses',
- u'NumberOfFeatures',
- u'NumberOfInstances',
- u'NumberOfInstancesWithMissingValues',
- u'NumberOfMissingValues',
- u'NumberOfNumericFeatures',
- u'NumberOfSymbolicFeatures',
- u'format',
- u'name',
- u'status'],
- dtype='object')
-
-and can see the first data point:
-
- >>> print(datasets.iloc[0])
- MajorityClassSize 684
- MaxNominalAttDistinctValues 10
- MinorityClassSize 0
- NumBinaryAtts 14
- NumberOfClasses 6
- NumberOfFeatures 39
- NumberOfInstances 898
- NumberOfInstancesWithMissingValues 0
- NumberOfMissingValues 0
- NumberOfNumericFeatures 6
- NumberOfSymbolicFeatures 32
- format ARFF
- name anneal
- status active
- Name: 1, dtype: object
-
-We can now filter the data:
-
- >>> filter = (datasets.NumberOfInstances > 1000) & (datasets.NumberOfFeatures > 5)
- >>> filtered_datasets = datasets.loc[filter]
- >>> dataset_indices = list(filtered_datasets.index)
- >>> print(dataset_indices) # doctest: +SKIP
- [3, 6, 12, 14, 16, 18, 20, 21, 22, 23, 24, 26, 28, 30, 32, 36, 38, 44,
- ... 5291, 5293, 5295, 5296, 5297, 5301, 5587, 5648, 5889]
-
-and get a list of dataset indices which can be used in a next step.
-
-Downloading datasets
-~~~~~~~~~~~~~~~~~~~~
-
-We can now use the dataset IDs to download all datasets by their IDs. Let's
-first look at how to download a single dataset and what can be done with the
-dataset object:
-
-.. code:: python
-
- >>> dataset_id = 23
- >>> dataset = openml.datasets.get_dataset(dataset_id)
-
-Properties of the dataset are stored as member variables:
-
-.. code:: python
-
- >>> print(dataset.__dict__) # doctest: +SKIP
- {'upload_date': u'2014-04-06 23:21:03', 'md5_cheksum': u'3149646ecff276abac3e892d1556655f', 'creator': None, 'citation': None, 'tag': [u'study_1', u'study_7', u'uci'], 'version_label': u'1', 'contributor': None, 'paper_url': None, 'original_data_url': None, 'id': 23, 'collection_date': None, 'row_id_attribute': None, 'version': 1, 'data_pickle_file': '/home/matthias/.openml/cache/datasets/23/dataset.pkl', 'default_target_attribute': u'Contraceptive_method_used', 'description': u"**Author**: \n**Source**: Unknown - \n**Please cite**: \n\n1. Title: Contraceptive Method Choice\n \n 2. Sources:\n (a) Origin: This dataset is a subset of the 1987 National Indonesia\n Contraceptive Prevalence Survey\n (b) Creator: Tjen-Sien Lim (limt@stat.wisc.edu)\n (c) Donor: Tjen-Sien Lim (limt@stat.wisc.edu)\n (c) Date: June 7, 1997\n \n 3. Past Usage:\n Lim, T.-S., Loh, W.-Y. & Shih, Y.-S. (1999). A Comparison of\n Prediction Accuracy, Complexity, and Training Time of Thirty-three\n Old and New Classification Algorithms. Machine Learning. Forthcoming.\n (ftp://ftp.stat.wisc.edu/pub/loh/treeprogs/quest1.7/mach1317.pdf or\n (http://www.stat.wisc.edu/~limt/mach1317.pdf)\n \n 4. Relevant Information:\n This dataset is a subset of the 1987 National Indonesia Contraceptive\n Prevalence Survey. The samples are married women who were either not \n pregnant or do not know if they were at the time of interview. The \n problem is to predict the current contraceptive method choice \n (no use, long-term methods, or short-term methods) of a woman based \n on her demographic and socio-economic characteristics.\n \n 5. Number of Instances: 1473\n \n 6. Number of Attributes: 10 (including the class attribute)\n \n 7. Attribute Information:\n \n 1. Wife's age (numerical)\n 2. Wife's education (categorical) 1=low, 2, 3, 4=high\n 3. Husband's education (categorical) 1=low, 2, 3, 4=high\n 4. Number of children ever born (numerical)\n 5. Wife's religion (binary) 0=Non-Islam, 1=Islam\n 6. Wife's now working? (binary) 0=Yes, 1=No\n 7. Husband's occupation (categorical) 1, 2, 3, 4\n 8. Standard-of-living index (categorical) 1=low, 2, 3, 4=high\n 9. Media exposure (binary) 0=Good, 1=Not good\n 10. Contraceptive method used (class attribute) 1=No-use \n 2=Long-term\n 3=Short-term\n \n 8. Missing Attribute Values: None\n\n Information about the dataset\n CLASSTYPE: nominal\n CLASSINDEX: last", 'format': u'ARFF', 'visibility': u'public', 'update_comment': None, 'licence': u'Public', 'name': u'cmc', 'language': None, 'url': u'http://www.openml.org/data/download/23/dataset_23_cmc.arff', 'data_file': '~/.openml/cache/datasets/23/dataset.arff', 'ignore_attributes': None}
-
-Next, to obtain the data matrix:
-
-.. code:: python
-
- >>> X = dataset.get_data()
- >>> print(X.shape, X.dtype)
- ((1473, 10), dtype('float32'))
-
-which returns the dataset as a np.ndarray with dtype :python:`np.float32`.
-In case the data is sparse, a scipy.sparse.csr matrix is returned. All nominal
-variables are encoded as integers, the inverse encoding can be retrieved via:
-
-.. code:: python
-
- >>> X, names = dataset.get_data(return_attribute_names=True)
- >>> print(names)
- [u'Wifes_age', u'Wifes_education', u'Husbands_education', u'Number_of_children_ever_born', u'Wifes_religion', u'Wifes_now_working%3F', u'Husbands_occupation', u'Standard-of-living_index', u'Media_exposure', u'Contraceptive_method_used']
-
-Most times, having a single data matrix :python:`X` is not enough. Two
-useful arguments are :python:`target` and
-:python:`return_categorical_indicator`. :python:`target` makes
-:meth:`get_data()` return :python:`X` and :python:`y`
-seperate; :python:`return_categorical_indicator` makes
-:meth:`get_data()` return a boolean array which indicate
-which attributes are categorical (and should be one hot encoded if necessary.)
-
-.. code:: python
-
- >>> X, y, categorical = dataset.get_data(
- ... target=dataset.default_target_attribute,
- ... return_categorical_indicator=True)
- >>> print(X.shape, y.shape)
- ((1473, 9), (1473,))
- >>> print(categorical)
- [False, True, True, False, True, True, True, True, True]
-
-In case you are working with `scikit-learn
-`_, you can use this data right away:
-
-.. code:: python
-
- >>> from sklearn import preprocessing, ensemble
- >>> enc = preprocessing.OneHotEncoder(categorical_features=categorical)
- >>> print(enc)
- OneHotEncoder(categorical_features=[False, True, True, False, True, True, True, True, True],
- dtype=, handle_unknown='error', n_values='auto',
- sparse=True)
- >>> X = enc.fit_transform(X).todense()
- >>> clf = ensemble.RandomForestClassifier()
- >>> clf.fit(X, y)
- RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
- max_depth=None, max_features='auto', max_leaf_nodes=None,
- min_samples_leaf=1, min_samples_split=2,
- min_weight_fraction_leaf=0.0, n_estimators=10, n_jobs=1,
- oob_score=False, random_state=None, verbose=0,
- warm_start=False)
-
-When you have to retrieve several datasets, you can use the convenience function
-:meth:`openml.datasets.get_datasets()`, which downloads all datasets given by
-a list of IDs:
-
- >>> ids = [12, 14, 16, 18, 20, 22]
- >>> datasets = openml.datasets.get_datasets(ids)
- >>> print(datasets[0].name)
- mfeat-factors
-
-~~~~~~~~~~~~~~~~~~
-Working with tasks
-~~~~~~~~~~~~~~~~~~
-
-#TODO put a link to the OpenML documentation here! Link the Task functions and
-the task class
-
-While datasets provide the most basic information for a machine learning task,
-they do not provide enough information for a reproducible machine learning
-experiment. A task defines how to split the dataset into a train and test set,
-whether to use several disjoint train and test splits (cross-validation) and
-whether this should be repeated several times. Also, the task defines a target
-metric for which a flow should be optimized.
-
-Just like datasets, tasks are identified by IDs and can be accessed in three
-different ways:
-
-1. In a list providing basic information on all tasks available on OpenML.
- This function will not download the actual tasks, but will instead download
- meta data that can be used to filter the tasks and retrieve a set of IDs.
-2. By functions only list a subset of all available tasks, restricted either by
- their :TODO:`task_type`, :TODO:`tag` or :TODO:`check_for_more`.
-3. A single task by its ID. It contains all meta information, the target metric,
- the splits and an iterator which can be used to access the splits in a
- useful manner.
-
-You can also read more about tasks in the `OpenML guide `_.
-
-Listing tasks
-~~~~~~~~~~~~~
-
-Once we decide on the datasets we want to work on, we have to download the
-corresponding tasks. Tasks can be pre-filtered by either by a task type or
-a tag.
-
-So far, this package only supports supervised classification tasks (task
-type :python:`1`) and supervised regression tasks (task type :python:`2`) #TODO check this
-We desribe how to find other task types in the subsection `Finding out task types`_
-and are happy to receive contributions that help us to support all other task
-types.
-
-The most natural way to retrieve tasks is by their task type. In this example
-we will use the most commonly studied machine learning supervised
-classification task (task type :python:`1`):
-
-.. code:: python
-
- >>> tasks = openml.tasks.list_tasks_by_type(1)
-
-Let's find out more about the datasets:
-
-.. code:: python
-
- >>> import pandas as pd
- >>> tasks = pd.DataFrame(tasks)
- >>> tasks.set_index('tid', inplace=True)
- >>> print(tasks.columns)
- Index([ u'cost_matrix', u'did',
- u'estimation_procedure', u'evaluation_measures',
- u'name', u'source_data',
- u'status', u'target_feature',
- u'task_type'],
- dtype='object')
-
-Now we can restrict the tasks to all tasks with the desired resampling strategy:
-
-# TODO add something about the different resampling strategies implemented!
-
-.. code:: python
-
- >>> filter = tasks.estimation_procedure == '10-fold Crossvalidation'
- >>> filtered_tasks = tasks[filter]
- >>> filtered_tasks = list(filtered_tasks.index)
- >>> print(filtered_tasks) # doctest: +SKIP
- [1, 2, 3, 4, 5, 6, 7, 8, 9, ... 10105, 10106, 10107, 10109, 10111, 13907, 13918]
-
-Resampling strategies can be found on the `OpenML Website `_
-or programatically as described in `Finding out evaluation strategies and target metrics`_.
-
-Finally, we can check whether there is a task for each dataset that we want to
-use in our study. If this is not the case, tasks can be created on the
-`OpenML website `_.
-
-The rest of this subsection deals with accessing a list of tasks by tags and
-without any restriction.
-
-A list of tasks, filtered tags, can be retrieved via:
-
-.. code:: python
-
- >>> tasks = openml.tasks.list_tasks_by_tag('study_1')
-
-:meth:`openml.tasks.list_tasks_by_tag` returns a list of dictionaries, we will
-convert it into a `pandas dataframe `_
-to have better visualization:
-
-.. code:: python
-
- >>> import pandas as pd
- >>> tasks = pd.DataFrame(tasks)
- >>> tasks.set_index('tid', inplace=True)
-
-As before, we have to check whether there is a task for each dataset that we
-want to work with. In addition, we have to make sure to use only tasks with the
-desired task type:
-
-#TODO this doesn't look nice, we should have a constant for each known task,
-dynamically created by the task type available (but when do we know that we
-can savely use the api connector? what to do if we do not have an internet
-connection? Maybe have this statically in the program and check from time to
-time if there is something new (via a unit test?)?, the same holds true for
-the resampling strategies available!)
-
-.. code:: python
-
- >>> filter = tasks.task_type == 'Supervised Classification'
- >>> filtered_tasks = tasks[filter]
- >>> print(len(filtered_tasks)) # doctest: +SKIP
- 2599
-
-Finally, it is also possible to list all tasks on OpenML with:
-
-.. code:: python
-
- >>> tasks = openml.tasks.list_tasks()
- >>> print(len(tasks)) # doctest: +SKIP
- 29757
-
-Downloading tasks
-~~~~~~~~~~~~~~~~~
-
-Downloading tasks works similar to downloading datasets. We provide two
-functions for this, one which downloads only a single task by its ID,
-and one which takes a list of IDs and downloads all of these tasks:
-
-.. code:: python
-
- >>> task_id = 1
- >>> task = openml.tasks.get_task(task_id)
-
-Properties of the task are stored as member variables:
-
-.. code:: python
-
- >>> print(task.__dict__)
- {'target_feature': u'class', 'task_type': u'Supervised Classification', 'task_id': 1, 'estimation_procedure': {'type': u'crossvalidation', 'data_splits_url': u'http://www.openml.org/api_splits/get/1/Task_1_splits.arff', 'parameters': {u'number_repeats': u'1', u'percentage': '', u'stratified_sampling': u'true', u'number_folds': u'10'}}, 'class_labels': [u'1', u'2', u'3', u'4', u'5', u'U'], 'cost_matrix': None, 'evaluation_measure': u'predictive_accuracy', 'dataset_id': 1, 'estimation_parameters': {u'number_repeats': u'1', u'percentage': '', u'stratified_sampling': u'true', u'number_folds': u'10'}}
-
-And with a list of task IDs:
-
-.. code:: python
-
- >>> ids = [12, 14, 16, 18, 20, 22]
- >>> tasks = openml.tasks.get_tasks(ids)
- >>> print(tasks[0])
-
-~~~~~~~~~~~~~~~~~~~~~~~
-Finding out tasks types
-~~~~~~~~~~~~~~~~~~~~~~~
-
-Not yet supported by the API. Please use the OpenML website.
-
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Finding out evaluation strategies and target metrics
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Not yet supported by the API. Please use the OpenML website.
-
-~~~~~~~~~~~~~~~
-Using the cache
-~~~~~~~~~~~~~~~
-
-Downloading all datasets, tasks and split every time a get function is called
-would prohibit a user to interact with the API in an exploratory manner.
-OpenML is designed in a way that certain entities are immutable once created.
-This allows the python package to cache datasets, tasks, splits and runs locally
-for fast retrieval. Another benefit is that the API can be used normally on a
-compute cluster without internet access (:ref:`see below`).
-
-Currently, the following objects are cached:
-
-* datasets
- * dataset arff. In order to reduce parsing time, the data is serialized to
- disk in a binary format (using the `pickle library `_.
- * dataset descriptions
- * more?
-* tasks
- * task description
- * split arff. TODO are they cached?
-* runs
- * run description
-
-Run predictions are not cached yet. Flow ojects cannot yet be downloaded and are
-therefore not cached.
-
-Configuring the cache
-~~~~~~~~~~~~~~~~~~~~~
-
-Configuring the cache works as described in the subsection `Connecting to the OpenML server`_:
-It can be done either through the API:
-
-.. code:: python
-
- >>> openml.config.set_cache_directory('~/.openml/cache')
-
-or the config file:
-
-.. code:: bash
-
- cachedir = '~/.openml/cache'
-
-
-Clearing the cache
-~~~~~~~~~~~~~~~~~~
-
-Currently, there is no programmatic way to interact with the cache and we do not
-plan to implement one. If you have any use case for this, please open an issue
-on the `issue tracker `_.
-
-# TODO check that the cache is in a consistent state!
-In case the cache gets too large, you can manually delete unnecessary files.
-Make sure that you always delete a complete entity, for example the whole
-directory caching a dataset named after the datasets ID.
-
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Working with Flows and Runs
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Tasks and datasets allow us to download all information to run an experiment
-locally. In order to upload and share results of such an experiment we need
-the concepts of flows and runs.
-
-Flows are descriptions of something runable which does the machine learning.
-A flow contains all information to set up the necessary machine learning
-library and its dependencies as well as all possible parameters.
-
-A run is the outcome of running a flow on a task. It contains all parameter
-settings for the flow, a setup string (most likely a command line call) and all
-predictions of that run. When a run is uploaded to the server, the server
-automatically calculates several metrics which can be used to compare the
-performance of different flows to each other.
-
-Creating a flow
-~~~~~~~~~~~~~~~
-
-So far, a flow in the OpenML python API is expected to be an estimator object
-following the `scikit-learn estimator API `_.
-Thus, it needs to implement a :python:`fit()`, :python:`predict()`,
-:python:`set_params()` and :python:`get_params()` method.
-
-The only mandatory argument to construct a flow is an estimator object. It is of
-course possible and also useful to pass other arguments to the constructor, for
-example a description or the name of the creator:
-
-.. code:: python
-
- >>> from openml import OpenMLFlow
- >>> model = ensemble.RandomForestClassifier()
- >>> flow = OpenMLFlow(model, description='Out-of-the-box scikit-learn '
- ... 'random forest classifier',
- ... creator='Matthias Feurer')
- >>> print(flow)
- {'description': 'Out-of-the-box scikit-learn random forest classifier', 'creator': 'Matthias Feurer', 'external_version': 'sklearn_0.16.1', 'source': 'FIXME DEFINE PYTHON FLOW', 'tag': None, 'upoader': None, 'model': RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
- max_depth=None, max_features='auto', max_leaf_nodes=None,
- min_samples_leaf=1, min_samples_split=2,
- min_weight_fraction_leaf=0.0, n_estimators=10, n_jobs=1,
- oob_score=False, random_state=None, verbose=0,
- warm_start=False), 'id': None, 'name': 'sklearn.ensemble.forest.RandomForestClassifier'}
-
-Prior to using a flow in experiments, it has to be pushed to the OpenML
-website. The python OpenML API uses methods named :python:`publish()` for this.
-It takes no arguments, but uses information from the estimator object to
-obtain all necessary information. The information is pushed to OpenML, and the
-flow gets assigned a flow ID.
-
-.. code:: python
-
- >>> flow.publish()
- # What happens here? What should it return?
-
-Running a flow on a task
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-We can now use the created flow fo finally run a machine learning model on a
-task and upload the results to the OpenML website. For that we use the function
-:meth:`openml.runs.run_task`. It only accepts two arguments and does what the
-name suggests.
-
-.. code:: python
-
- >>> task_id = 12
- >>> run = openml.runs.run_task(task_id, model)
- >>> print(run)
-
-As for flows, the run must be published so that it can be used by others on
-OpenML:
-
-.. code:: python
-
- >>> run.publish()
- # What happens here? What should it return?
-
-Retrieving results from OpenML
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-
-
-
-
-
-
-
-
diff --git a/docker/Dockerfile b/docker/Dockerfile
new file mode 100644
index 000000000..a84723309
--- /dev/null
+++ b/docker/Dockerfile
@@ -0,0 +1,21 @@
+# Dockerfile to build an image with preinstalled dependencies
+# Useful building docs or running unix tests from a Windows host.
+FROM python:3.10
+
+RUN git clone https://github.com/openml/openml-python.git openml
+WORKDIR openml
+RUN python -m venv venv
+RUN venv/bin/pip install wheel setuptools
+RUN venv/bin/pip install -e .[test,examples,docs,examples_unix]
+
+WORKDIR /
+RUN mkdir scripts
+ADD startup.sh scripts/
+ADD readme.md /
+
+# Due to the nature of the Docker container it might often be built from Windows.
+# It is typical to have the files with \r\n line-ending, we want to remove it for the unix image.
+RUN sed -i 's/\r//g' scripts/startup.sh
+
+# overwrite the default `python` entrypoint
+ENTRYPOINT ["/bin/bash", "/scripts/startup.sh"]
diff --git a/docker/readme.md b/docker/readme.md
new file mode 100644
index 000000000..d0af9d9fe
--- /dev/null
+++ b/docker/readme.md
@@ -0,0 +1,131 @@
+# OpenML Python Container
+
+This docker container has the latest version of openml-python downloaded and pre-installed.
+It can also be used by developers to run unit tests or build the docs in
+a fresh and/or isolated unix environment.
+This document contains information about:
+
+ 1. [Usage](#usage): how to use the image and its main modes.
+ 2. [Using local or remote code](#using-local-or-remote-code): useful when testing your own latest changes.
+ 3. [Versions](#versions): identify which image to use.
+ 4. [Development](#for-developers): information about the Docker image for developers.
+
+*note:* each docker image is shipped with a readme, which you can read with:
+`docker run --entrypoint=/bin/cat openml/openml-python:TAG readme.md`
+
+## Usage
+
+There are three main ways to use the image: running a pre-installed Python environment,
+running tests, and building documentation.
+
+### Running `Python` with pre-installed `OpenML-Python` (default):
+
+To run `Python` with a pre-installed `OpenML-Python` environment run:
+
+```text
+docker run -it openml/openml-python
+```
+
+this accepts the normal `Python` arguments, e.g.:
+
+```text
+docker run openml/openml-python -c "import openml; print(openml.__version__)"
+```
+
+if you want to run a local script, it needs to be mounted first. Mount it into the
+`openml` folder:
+
+```
+docker run -v PATH/TO/FILE:/openml/MY_SCRIPT.py openml/openml-python MY_SCRIPT.py
+```
+
+### Running unit tests
+
+You can run the unit tests by passing `test` as the first argument.
+It also requires a local or remote repository to be specified, which is explained
+[below]((#using-local-or-remote-code). For this example, we specify to test the
+`develop` branch:
+
+```text
+docker run openml/openml-python test develop
+```
+
+### Building documentation
+
+You can build the documentation by passing `doc` as the first argument,
+you should [mount]((https://docs.docker.com/storage/bind-mounts/#start-a-container-with-a-bind-mount))
+an output directory in which the docs will be stored. You also need to provide a remote
+or local repository as explained in [the section below]((#using-local-or-remote-code).
+In this example, we build documentation for the `develop` branch.
+On Windows:
+
+```text
+ docker run --mount type=bind,source="E:\\files/output",destination="/output" openml/openml-python doc develop
+```
+
+on Linux:
+```text
+ docker run --mount type=bind,source="./output",destination="/output" openml/openml-python doc develop
+```
+
+see [the section below]((#using-local-or-remote-code) for running against local changes
+or a remote branch.
+
+*Note: you can forgo mounting an output directory to test if the docs build successfully,
+but the result will only be available within the docker container under `/openml/docs/build`.*
+
+## Using local or remote code
+
+You can build docs or run tests against your local repository or a Github repository.
+In the examples below, change the `source` to match the location of your local repository.
+
+### Using a local repository
+
+To use a local directory, mount it in the `/code` directory, on Windows:
+
+```text
+ docker run --mount type=bind,source="E:\\repositories/openml-python",destination="/code" openml/openml-python test
+```
+
+on Linux:
+```text
+ docker run --mount type=bind,source="/Users/pietergijsbers/repositories/openml-python",destination="/code" openml/openml-python test
+```
+
+when building docs, you also need to mount an output directory as shown above, so add both:
+
+```text
+docker run --mount type=bind,source="./output",destination="/output" --mount type=bind,source="/Users/pietergijsbers/repositories/openml-python",destination="/code" openml/openml-python doc
+```
+
+### Using a Github repository
+Building from a remote repository requires you to specify a branch.
+The branch may be specified by name directly if it exists on the original repository (https://github.com/openml/openml-python/):
+
+ docker run --mount type=bind,source=PATH_TO_OUTPUT,destination=/output openml/openml-python [test,doc] BRANCH
+
+Where `BRANCH` is the name of the branch for which to generate the documentation.
+It is also possible to build the documentation from the branch on a fork,
+in this case the `BRANCH` should be specified as `GITHUB_NAME#BRANCH` (e.g.
+`PGijsbers#my_feature_branch`) and the name of the forked repository should be `openml-python`.
+
+## For developers
+This section contains some notes about the structure of the image,
+intended for those who want to work on it.
+
+### Added Directories
+The `openml/openml-python` image is built on a vanilla `python:3` image.
+Additionally, it contains the following files are directories:
+
+ - `/openml`: contains the openml-python repository in the state with which the image
+ was built by default. If working with a `BRANCH`, this repository will be set to
+ the `HEAD` of `BRANCH`.
+ - `/openml/venv/`: contains the used virtual environment for `doc` and `test`. It has
+ `openml-python` dependencies pre-installed. When invoked with `doc` or `test`, the
+ dependencies will be updated based on the `setup.py` of the `BRANCH` or mounted `/code`.
+ - `/scripts/startup.sh`: the entrypoint of the image. Takes care of the automated features (e.g. `doc` and `test`).
+
+## Building the image
+To build the image yourself, execute `docker build -f Dockerfile .` from the `docker`
+directory of the `openml-python` repository. It will use the `startup.sh` as is, so any
+local changes will be present in the image.
diff --git a/docker/startup.sh b/docker/startup.sh
new file mode 100644
index 000000000..34a5c61f3
--- /dev/null
+++ b/docker/startup.sh
@@ -0,0 +1,80 @@
+# Entry script to switch between the different Docker functionalities.
+# By default, execute Python with OpenML pre-installed
+#
+# Entry script to allow docker to be ran for bash, tests and docs.
+# The script assumes a code repository can be mounted to ``/code`` and an output directory to ``/output``.
+# Executes ``mode`` on ``branch`` or the provided ``code`` directory.
+# $1: Mode, optional. Options:
+# - test: execute unit tests
+# - doc: build documentation, requires a mounted ``output`` directory if built from a branch.
+# - if not provided: execute bash.
+# $2: Branch, optional.
+# Mutually exclusive with mounting a ``code`` directory.
+# Can be a branch on a Github fork, specified with the USERNAME#BRANCH format.
+# The test or doc build is executed on this branch.
+
+if [[ ! ( $1 = "doc" || $1 = "test" ) ]]; then
+ cd openml
+ source venv/bin/activate
+ python "$@"
+ exit 0
+fi
+
+# doc and test modes require mounted directories and/or specified branches
+if ! [ -d "/code" ] && [ -z "$2" ]; then
+ echo "To perform $1 a code repository must be mounted to '/code' or a branch must be specified." >> /dev/stderr
+ exit 1
+fi
+if [ -d "/code" ] && [ -n "$2" ]; then
+ # We want to avoid switching the git environment from within the docker container
+ echo "You can not specify a branch for a mounted code repository." >> /dev/stderr
+ exit 1
+fi
+if [ "$1" == "doc" ] && [ -n "$2" ] && ! [ -d "/output" ]; then
+ echo "To build docs from an online repository, you need to mount an output directory." >> /dev/stderr
+ exit 1
+fi
+
+if [ -n "$2" ]; then
+ # if a branch is provided, we will pull it into the `openml` local repository that was created with the image.
+ cd openml
+ if [[ $2 == *#* ]]; then
+ # If a branch is specified on a fork (with NAME#BRANCH format), we have to construct the url before pulling
+ # We add a trailing '#' delimiter so the second element doesn't get the trailing newline from <<<
+ readarray -d '#' -t fork_name_and_branch<<<"$2#"
+ fork_url="https://github.com/${fork_name_and_branch[0]}/openml-python.git"
+ fork_branch="${fork_name_and_branch[1]}"
+ echo git fetch "$fork_url" "$fork_branch":branch_from_fork
+ git fetch "$fork_url" "$fork_branch":branch_from_fork
+ branch=branch_from_fork
+ else
+ git fetch origin "$2"
+ branch=$2
+ fi
+ if ! git checkout "$branch" ; then
+ echo "Could not checkout $branch. If the branch lives on a fork, specify it as USER#BRANCH. Make sure to push the branch." >> /dev/stderr
+ exit 1
+ fi
+ git pull
+ code_dir="/openml"
+else
+ code_dir="/code"
+fi
+
+source /openml/venv/bin/activate
+cd $code_dir
+# The most recent ``main`` is already installed, but we want to update any outdated dependencies
+pip install -e .[test,examples,docs,examples_unix]
+
+if [ "$1" == "test" ]; then
+ pytest -n 4 --durations=20 --timeout=600 --timeout-method=thread --dist load -sv
+fi
+
+if [ "$1" == "doc" ]; then
+ cd doc
+ make html
+ make linkcheck
+ if [ -d "/output" ]; then
+ cp -r /openml/doc/build /output
+ fi
+fi
\ No newline at end of file
diff --git a/docs/contributing.md b/docs/contributing.md
new file mode 100644
index 000000000..39072d64e
--- /dev/null
+++ b/docs/contributing.md
@@ -0,0 +1,22 @@
+# Contributing
+
+Contribution to the OpenML package is highly appreciated in all forms.
+In particular, a few ways to contribute to openml-python are:
+
+- A direct contribution to the package, by means of improving the
+ code, documentation or examples. To get started, see [this
+ file](https://github.com/openml/openml-python/blob/main/CONTRIBUTING.md)
+ with details on how to set up your environment to develop for
+ openml-python.
+- A contribution to an openml-python extension. An extension package
+ allows OpenML to interface with a machine learning package (such
+ as scikit-learn or keras). These extensions are hosted in separate
+ repositories and may have their own guidelines. For more
+ information, see also [extensions](extensions.md).
+- Bug reports. If something doesn't work for you or is cumbersome,
+ please open a new issue to let us know about the problem.
+- [Cite OpenML](https://www.openml.org/terms) if you use it in a
+ scientific publication.
+- Visit one of our [hackathons](https://www.openml.org/meet).
+- Contribute to another OpenML project, such as [the main OpenML
+ project](https://github.com/openml/OpenML/blob/master/CONTRIBUTING.md).
diff --git a/docs/details.md b/docs/details.md
new file mode 100644
index 000000000..bf4b0cd2b
--- /dev/null
+++ b/docs/details.md
@@ -0,0 +1,76 @@
+# Advanced User Guide
+
+This document highlights some of the more advanced features of
+`openml-python`.
+
+## Configuration
+
+The configuration file resides in a directory `.config/openml` in the
+home directory of the user and is called config (More specifically, it
+resides in the [configuration directory specified by the XDGB Base
+Directory
+Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html)).
+It consists of `key = value` pairs which are separated by newlines. The
+following keys are defined:
+
+- apikey: required to access the server.
+- server: the server to connect to (default: `http://www.openml.org`).
+ For connection to the test server, set this to `test.openml.org`.
+- cachedir: the root folder where the cache file directories should be created.
+ If not given, will default to `~/.openml/cache`
+- avoid_duplicate_runs: if set to `True` (default), when certain functions
+ are called a lookup is performed to see if there already
+ exists such a run on the server. If so, download those
+ results instead.
+- retry_policy: Defines how to react when the server is unavailable or
+ experiencing high load. It determines both how often to
+ attempt to reconnect and how quickly to do so. Please don't
+ use `human` in an automated script that you run more than
+ one instance of, it might increase the time to complete your
+ jobs and that of others. One of:
+ - human (default): For people running openml in interactive
+ fashion. Try only a few times, but in quick succession.
+ - robot: For people using openml in an automated fashion. Keep
+ trying to reconnect for a longer time, quickly increasing
+ the time between retries.
+
+- connection_n_retries: number of times to retry a request if they fail.
+Default depends on retry_policy (5 for `human`, 50 for `robot`)
+- verbosity: the level of output:
+ - 0: normal output
+ - 1: info output
+ - 2: debug output
+
+This file is easily configurable by the `openml` command line interface.
+To see where the file is stored, and what its values are, use openml
+configure none.
+
+## Docker
+
+It is also possible to try out the latest development version of
+`openml-python` with docker:
+
+``` bash
+docker run -it openml/openml-python
+```
+
+See the [openml-python docker
+documentation](https://github.com/openml/openml-python/blob/main/docker/readme.md)
+for more information.
+
+## Key concepts
+
+OpenML contains several key concepts which it needs to make machine
+learning research shareable. A machine learning experiment consists of
+one or several **runs**, which describe the performance of an algorithm
+(called a **flow** in OpenML), its hyperparameter settings (called a
+**setup**) on a **task**. A **Task** is the combination of a
+**dataset**, a split and an evaluation metric. In this user guide we
+will go through listing and exploring existing **tasks** to actually
+running machine learning algorithms on them. In a further user guide we
+will examine how to search through **datasets** in order to curate a
+list of **tasks**.
+
+A further explanation is given in the [OpenML user
+guide](https://docs.openml.org/concepts/).
+
diff --git a/docs/developer_setup.md b/docs/developer_setup.md
new file mode 100644
index 000000000..55a73fef9
--- /dev/null
+++ b/docs/developer_setup.md
@@ -0,0 +1,210 @@
+# OpenML Local Development Environment Setup
+
+This guide outlines the standard procedures for setting up a local development environment for the OpenML ecosystem. It covers the configuration of the backend servers (API v1 and API v2) and the Python Client SDK.
+
+OpenML currently has two backend architecture:
+
+* **API v1**: The PHP-based server currently serving production traffic.
+* **API v2**: The Python-based server (FastAPI) currently under active development.
+
+> Note on Migration: API v1 is projected to remain operational through at least 2026. API v2 is the target architecture for future development.
+
+## 1. API v1 Setup (PHP Backend)
+
+This section details the deployment of the legacy PHP backend.
+
+### Prerequisites
+
+* **Docker**: Docker Desktop (Ensure the daemon is running).
+* **Version Control**: Git.
+
+### Installation Steps
+
+#### 1. Clone the Repository
+
+Retrieve the OpenML services source code:
+
+```bash
+git clone https://github.com/openml/services
+cd services
+```
+
+#### 2. Configure File Permissions
+
+To ensure the containerized PHP service can write to the local filesystem, initialize the data directory permissions.
+
+From the repository root:
+
+```bash
+chown -R www-data:www-data data/php
+```
+
+If the `www-data` user does not exist on the host system, grant full permissions as a fallback:
+
+```bash
+chmod -R 777 data/php
+```
+
+#### 3. Launch Services
+
+Initialize the container stack:
+
+```bash
+docker compose --profile all up -d
+```
+
+#### Warning: Container Conflicts
+
+If API v2 (Python backend) containers are present on the system, name conflicts may occur. To resolve this, stop and remove existing containers before launching API v1:
+
+```bash
+docker compose --profile all down
+docker compose --profile all up -d
+```
+
+#### 4. Verification
+
+Validate the deployment by accessing the flow endpoint. A successful response will return structured JSON data.
+
+* **Endpoint**: http://localhost:8080/api/v1/json/flow/181
+
+### Client Configuration
+
+To direct the `openml-python` client to the local API v1 instance, modify the configuration as shown below. The API key corresponds to the default key located in `services/config/php/.env`.
+
+```python
+import openml
+from openml_sklearn.extension import SklearnExtension
+from sklearn.neighbors import KNeighborsClassifier
+
+# Configure client to use local Docker instance
+openml.config.server = "http://localhost:8080/api/v1/xml"
+openml.config.apikey = "AD000000000000000000000000000000"
+
+# Test flow publication
+clf = KNeighborsClassifier(n_neighbors=3)
+extension = SklearnExtension()
+knn_flow = extension.model_to_flow(clf)
+
+knn_flow.publish()
+```
+
+## 2. API v2 Setup (Python Backend)
+
+This section details the deployment of the FastAPI backend.
+
+### Prerequisites
+
+* **Docker**: Docker Desktop (Ensure the daemon is running).
+* **Version Control**: Git.
+
+### Installation Steps
+
+#### 1. Clone the Repository
+
+Retrieve the API v2 source code:
+
+```bash
+git clone https://github.com/openml/server-api
+cd server-api
+```
+
+#### 2. Launch Services
+
+Build and start the container stack:
+
+```bash
+docker compose --profile all up
+```
+
+#### 3. Verification
+
+Validate the deployment using the following endpoints:
+
+* **Task Endpoint**: http://localhost:8001/tasks/31
+* **Swagger UI (Documentation)**: http://localhost:8001/docs
+
+## 3. Python SDK (`openml-python`) Setup
+
+This section outlines the environment setup for contributing to the OpenML Python client.
+
+### Installation Steps
+
+#### 1. Clone the Repository
+
+```bash
+git clone https://github.com/openml/openml-python
+cd openml-python
+```
+
+#### 2. Environment Initialization
+
+Create an isolated virtual environment (example using Conda):
+
+```bash
+conda create -n openml-python-dev python=3.12
+conda activate openml-python-dev
+```
+
+#### 3. Install Dependencies
+
+Install the package in editable mode, including development and documentation dependencies:
+
+```bash
+python -m pip install -e ".[dev,docs]"
+```
+
+#### 4. Configure Quality Gates
+
+Install pre-commit hooks to enforce coding standards:
+
+```bash
+pre-commit install
+pre-commit run --all-files
+```
+
+## 4. Testing Guidelines
+
+The OpenML Python SDK utilizes `pytest` markers to categorize tests based on dependencies and execution context.
+
+| Marker | Description |
+|-------------------|-----------------------------------------------------------------------------|
+| `sklearn` | Tests requiring `scikit-learn`. Skipped if the library is missing. |
+| `production_server`| Tests that interact with the live OpenML server (real API calls). |
+| `test_server` | Tests requiring the OpenML test server environment. |
+
+### Execution Examples
+
+Run the full test suite:
+
+```bash
+pytest
+```
+
+Run a specific subset (e.g., `scikit-learn` tests):
+
+```bash
+pytest -m sklearn
+```
+
+Exclude production tests (local only):
+
+```bash
+pytest -m "not production_server"
+```
+
+### Admin Privilege Tests
+
+Certain tests require administrative privileges on the test server. These are skipped automatically unless an admin API key is provided via environment variables.
+
+#### Windows (PowerShell):
+
+```shell
+$env:OPENML_TEST_SERVER_ADMIN_KEY = "admin-key"
+```
+
+#### Linux/macOS:
+
+```bash
+export OPENML_TEST_SERVER_ADMIN_KEY="admin-key"
+```
diff --git a/docs/extensions.md b/docs/extensions.md
new file mode 100644
index 000000000..858447440
--- /dev/null
+++ b/docs/extensions.md
@@ -0,0 +1,160 @@
+# Extensions
+
+OpenML-Python provides an extension interface to connect other machine
+learning libraries than scikit-learn to OpenML. Please check the
+[`api_extensions`](../reference/extensions/extension_interface/) and use the scikit-learn
+extension as a starting point.
+
+## List of extensions
+
+Here is a list of currently maintained OpenML extensions:
+
+- [openml-sklearn](https://github.com/openml/openml-sklearn)
+- [openml-keras](https://github.com/openml/openml-keras)
+- [openml-pytorch](https://github.com/openml/openml-pytorch)
+- [openml-tensorflow (for tensorflow
+ 2+)](https://github.com/openml/openml-tensorflow)
+
+## Connecting new machine learning libraries
+
+### Content of the Library
+
+To leverage support from the community and to tap in the potential of
+OpenML, interfacing with popular machine learning libraries is
+essential. The OpenML-Python package is capable of downloading meta-data
+and results (data, flows, runs), regardless of the library that was used
+to upload it. However, in order to simplify the process of uploading
+flows and runs from a specific library, an additional interface can be
+built. The OpenML-Python team does not have the capacity to develop and
+maintain such interfaces on its own. For this reason, we have built an
+extension interface to allows others to contribute back. Building a
+suitable extension for therefore requires an understanding of the
+current OpenML-Python support.
+
+[This tutorial](../examples/Basics/simple_flows_and_runs_tutorial) shows how the scikit-learn
+extension works with OpenML-Python.
+
+#### API
+
+- The extension scripts must import the openml-python package
+ and be able to interface with any function from the API.
+- The extension has to be defined as a Python class and must inherit
+ from [`openml.extensions.Extension`](../reference/extensions/extension_interface/#openml.extensions.extension_interface.Extension).
+- This class needs to have all the functions from `openml.extensions.Extension` overloaded as required.
+- The redefined functions should have adequate and appropriate
+ docstrings. The sklearn Extension API is a good example to follow.
+
+#### Interfacing with OpenML-Python
+
+Once the new extension class has been defined, the openml-python module
+to [`openml.extensions.register_extension`](../reference/extensions/functions/#openml.extensions.functions.register_extension)
+must be called to allow OpenML-Python to interface the new extension.
+
+The following methods should get implemented. Although the documentation
+in the extension interface should always be leading, here
+we list some additional information and best practices.
+Note that most methods are relatively simple
+and can be implemented in several lines of code.
+
+- General setup (required)
+ - `can_handle_flow`: Takes as
+ argument an OpenML flow, and checks whether this can be handled
+ by the current extension. The OpenML database consists of many
+ flows, from various workbenches (e.g., scikit-learn, Weka, mlr).
+ This method is called before a model is being deserialized.
+ Typically, the flow-dependency field is used to check whether
+ the specific library is present, and no unknown libraries are
+ present there.
+ - `can_handle_model`: Similar as
+ `can_handle_flow`:, except that in
+ this case a Python object is given. As such, in many cases, this
+ method can be implemented by checking whether this adheres to a
+ certain base class.
+- Serialization and De-serialization (required)
+ - `flow_to_model`: deserializes the
+ OpenML Flow into a model (if the library can indeed handle the
+ flow). This method has an important interplay with
+ `model_to_flow`. Running these
+ two methods in succession should result in exactly the same
+ model (or flow). This property can be used for unit testing
+ (e.g., build a model with hyperparameters, make predictions on a
+ task, serialize it to a flow, deserialize it back, make it
+ predict on the same task, and check whether the predictions are
+ exactly the same.) The example in the scikit-learn interface
+ might seem daunting, but note that here some complicated design
+ choices were made, that allow for all sorts of interesting
+ research questions. It is probably good practice to start easy.
+ - `model_to_flow`: The inverse of `flow_to_model`. Serializes a
+ model into an OpenML Flow. The flow should preserve the class,
+ the library version, and the tunable hyperparameters.
+ - `get_version_information`: Return
+ a tuple with the version information of the important libraries.
+ - `create_setup_string`: No longer
+ used, and will be deprecated soon.
+- Performing runs (required)
+ - `is_estimator`: Gets as input a
+ class, and checks whether it has the status of estimator in the
+ library (typically, whether it has a train method and a predict
+ method).
+ - `seed_model`: Sets a random seed to the model.
+ - `_run_model_on_fold`: One of the
+ main requirements for a library to generate run objects for the
+ OpenML server. Obtains a train split (with labels) and a test
+ split (without labels) and the goal is to train a model on the
+ train split and return the predictions on the test split. On top
+ of the actual predictions, also the class probabilities should
+ be determined. For classifiers that do not return class
+ probabilities, this can just be the hot-encoded predicted label.
+ The predictions will be evaluated on the OpenML server. Also,
+ additional information can be returned, for example,
+ user-defined measures (such as runtime information, as this can
+ not be inferred on the server). Additionally, information about
+ a hyperparameter optimization trace can be provided.
+ - `obtain_parameter_values`:
+ Obtains the hyperparameters of a given model and the current
+ values. Please note that in the case of a hyperparameter
+ optimization procedure (e.g., random search), you only should
+ return the hyperparameters of this procedure (e.g., the
+ hyperparameter grid, budget, etc) and that the chosen model will
+ be inferred from the optimization trace.
+ - `check_if_model_fitted`: Check
+ whether the train method of the model has been called (and as
+ such, whether the predict method can be used).
+- Hyperparameter optimization (optional)
+ - `instantiate_model_from_hpo_class`: If a given run has recorded the hyperparameter
+ optimization trace, then this method can be used to
+ reinstantiate the model with hyperparameters of a given
+ hyperparameter optimization iteration. Has some similarities
+ with `flow_to_model` (as this
+ method also sets the hyperparameters of a model). Note that
+ although this method is required, it is not necessary to
+ implement any logic if hyperparameter optimization is not
+ implemented. Simply raise a `NotImplementedError`
+ then.
+
+### Hosting the library
+
+Each extension created should be a stand-alone repository, compatible
+with the [OpenML-Python repository](https://github.com/openml/openml-python).
+The extension repository should work off-the-shelf with *OpenML-Python* installed.
+
+Create a public Github repo with the following directory structure:
+
+ | [repo name]
+ | |-- [extension name]
+ | | |-- __init__.py
+ | | |-- extension.py
+ | | |-- config.py (optionally)
+
+### Recommended
+
+- Test cases to keep the extension up to date with the
+ Openml-Python upstream changes.
+- Documentation of the extension API, especially if any new
+ functionality added to OpenML-Python\'s extension design.
+- Examples to show how the new extension interfaces and works with
+ OpenML-Python.
+- Create a PR to add the new extension to the OpenML-Python API
+ documentation.
+
+Happy contributing!
diff --git a/docs/images/openml_icon.png b/docs/images/openml_icon.png
new file mode 100644
index 000000000..4808572ff
Binary files /dev/null and b/docs/images/openml_icon.png differ
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 000000000..1058c3956
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,103 @@
+# OpenML
+
+**The Python API for a World of Data and More**
+
+Welcome to the documentation of the OpenML Python API, a connector to
+the collaborative machine learning platform
+[OpenML.org](https://www.openml.org).
+OpenML-Python can download or upload data from OpenML, such as datasets
+and machine learning experiment results.
+
+If you are new to OpenML, we recommend checking out the [OpenML documentation](https://docs.openml.org/)
+to get familiar with the concepts and features of OpenML. In particular, we recommend
+reading more about the [OpenML concepts](https://docs.openml.org/concepts/).
+
+## :joystick: Minimal Examples
+
+Use the following code to get the [credit-g](https://www.openml.org/search?type=data&sort=runs&status=active&id=31) [dataset](https://docs.openml.org/concepts/data/):
+
+```python
+import openml
+
+dataset = openml.datasets.get_dataset("credit-g") # or by ID get_dataset(31)
+X, y, categorical_indicator, attribute_names = dataset.get_data(target="class")
+```
+
+Get a [task](https://docs.openml.org/concepts/tasks/) for [supervised classification on credit-g](https://www.openml.org/search?type=task&id=31&source_data.data_id=31):
+
+```python
+import openml
+
+task = openml.tasks.get_task(31)
+dataset = task.get_dataset()
+X, y, categorical_indicator, attribute_names = dataset.get_data(target=task.target_name)
+# get splits for the first fold of 10-fold cross-validation
+train_indices, test_indices = task.get_train_test_split_indices(fold=0)
+```
+
+Use an [OpenML benchmarking suite](https://docs.openml.org/concepts/benchmarking/) to get a curated list of machine-learning tasks:
+```python
+import openml
+
+suite = openml.study.get_suite("amlb-classification-all") # Get a curated list of tasks for classification
+for task_id in suite.tasks:
+ task = openml.tasks.get_task(task_id)
+```
+Find more examples in the navbar at the top.
+
+## :magic_wand: Installation
+
+OpenML-Python is available on Linux, MacOS, and Windows.
+
+You can install OpenML-Python with:
+
+```bash
+pip install openml
+```
+
+For more advanced installation information, please see the
+["Introduction"](../examples/Basics/introduction_tutorial) example.
+
+
+## Further information
+
+- [OpenML documentation](https://docs.openml.org/)
+- [OpenML client APIs](https://docs.openml.org/APIs/)
+- [OpenML developer guide](https://docs.openml.org/contributing/)
+- [Contact information](https://www.openml.org/contact)
+- [Citation request](https://www.openml.org/cite)
+- [OpenML blog](https://medium.com/open-machine-learning)
+- [OpenML twitter account](https://twitter.com/open_ml)
+
+
+## Contributing
+
+Contributing to the OpenML package is highly appreciated. Please see the
+["Contributing"](contributing.md) page for more information.
+
+## Citing OpenML-Python
+
+If you use OpenML-Python in a scientific publication, we would
+appreciate a reference to our JMLR-MLOSS paper
+["OpenML-Python: an extensible Python API for OpenML"](https://www.jmlr.org/papers/v22/19-920.html):
+
+=== "Bibtex"
+
+ ```bibtex
+ @article{JMLR:v22:19-920,
+ author = {Matthias Feurer and Jan N. van Rijn and Arlind Kadra and Pieter Gijsbers and Neeratyoy Mallik and Sahithya Ravi and Andreas Müller and Joaquin Vanschoren and Frank Hutter},
+ title = {OpenML-Python: an extensible Python API for OpenML},
+ journal = {Journal of Machine Learning Research},
+ year = {2021},
+ volume = {22},
+ number = {100},
+ pages = {1--5},
+ url = {http://jmlr.org/papers/v22/19-920.html}
+ }
+ ```
+
+=== "MLA"
+
+ Feurer, Matthias, et al.
+ "OpenML-Python: an extensible Python API for OpenML."
+ _Journal of Machine Learning Research_ 22.100 (2021):1−5.
diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css
new file mode 100644
index 000000000..d0c4f79d8
--- /dev/null
+++ b/docs/stylesheets/extra.css
@@ -0,0 +1,3 @@
+.jp-InputArea-prompt, .jp-InputPrompt {
+ display: none !important;
+}
diff --git a/examples/Advanced/configure_logging.py b/examples/Advanced/configure_logging.py
new file mode 100644
index 000000000..60b789846
--- /dev/null
+++ b/examples/Advanced/configure_logging.py
@@ -0,0 +1,50 @@
+# %% [markdown]
+# This tutorial explains openml-python logging, and shows how to configure it.
+# Openml-python uses the [Python logging module](https://docs.python.org/3/library/logging.html)
+# to provide users with log messages. Each log message is assigned a level of importance, see
+# the table in Python's logging tutorial
+# [here](https://docs.python.org/3/howto/logging.html#when-to-use-logging).
+#
+# By default, openml-python will print log messages of level `WARNING` and above to console.
+# All log messages (including `DEBUG` and `INFO`) are also saved in a file, which can be
+# found in your cache directory (see also the
+# [introduction tutorial](../Basics/introduction_tutorial).
+# These file logs are automatically deleted if needed, and use at most 2MB of space.
+#
+# It is possible to configure what log levels to send to console and file.
+# When downloading a dataset from OpenML, a `DEBUG`-level message is written:
+
+# %%
+import openml
+
+openml.datasets.get_dataset("iris", version=1)
+
+# %% [markdown]
+# With default configuration, the above example will show no output to console.
+# However, in your cache directory you should find a file named 'openml_python.log',
+# which has a DEBUG message written to it. It should be either like
+# "[DEBUG] [10:46:19:openml.datasets.dataset] Saved dataset 61: iris to file ..."
+# or like
+# "[DEBUG] [10:49:38:openml.datasets.dataset] Data pickle file already exists and is up to date."
+# , depending on whether or not you had downloaded iris before.
+# The processed log levels can be configured programmatically:
+
+# %%
+import logging
+
+openml.config.set_console_log_level(logging.DEBUG)
+openml.config.set_file_log_level(logging.WARNING)
+openml.datasets.get_dataset("iris", version=1)
+
+# %% [markdown]
+# Now the log level that was previously written to file should also be shown in the console.
+# The message is now no longer written to file as the `file_log` was set to level `WARNING`.
+#
+# It is also possible to specify the desired log levels through the configuration file.
+# This way you will not need to set them on each script separately.
+# Add the line **verbosity = NUMBER** and/or **file_verbosity = NUMBER** to the config file,
+# where 'NUMBER' should be one of:
+#
+# * 0: `logging.WARNING` and up.
+# * 1: `logging.INFO` and up.
+# * 2: `logging.DEBUG` and up (i.e. all messages).
diff --git a/examples/Advanced/create_upload_tutorial.py b/examples/Advanced/create_upload_tutorial.py
new file mode 100644
index 000000000..46ec96319
--- /dev/null
+++ b/examples/Advanced/create_upload_tutorial.py
@@ -0,0 +1,303 @@
+# %% [markdown]
+# A tutorial on how to create and upload a dataset to OpenML.
+
+# %%
+import numpy as np
+import pandas as pd
+import sklearn.datasets
+from scipy.sparse import coo_matrix
+
+import openml
+from openml.datasets.functions import create_dataset
+
+# %%
+openml.config.start_using_configuration_for_example()
+
+# %% [markdown]
+# Below we will cover the following cases of the dataset object:
+#
+# * A numpy array
+# * A list
+# * A pandas dataframe
+# * A sparse matrix
+# * A pandas sparse dataframe
+
+# %% [markdown]
+# ## Dataset is a numpy array
+# A numpy array can contain lists in the case of dense data or it can contain
+# OrderedDicts in the case of sparse data.
+#
+# # Prepare dataset
+# Load an example dataset from scikit-learn which we will upload to OpenML.org
+# via the API.
+
+# %%
+diabetes = sklearn.datasets.load_diabetes()
+name = "Diabetes(scikit-learn)"
+X = diabetes.data
+y = diabetes.target
+attribute_names = diabetes.feature_names
+description = diabetes.DESCR
+
+# %% [markdown]
+# OpenML does not distinguish between the attributes and targets on the data
+# level and stores all data in a single matrix.
+#
+# The target feature is indicated as meta-data of the dataset (and tasks on
+# that data).
+
+# %%
+data = np.concatenate((X, y.reshape((-1, 1))), axis=1)
+attribute_names = list(attribute_names)
+attributes = [(attribute_name, "REAL") for attribute_name in attribute_names] + [
+ ("class", "INTEGER")
+]
+citation = (
+ "Bradley Efron, Trevor Hastie, Iain Johnstone and "
+ "Robert Tibshirani (2004) (Least Angle Regression) "
+ "Annals of Statistics (with discussion), 407-499"
+)
+paper_url = "https://web.stanford.edu/~hastie/Papers/LARS/LeastAngle_2002.pdf"
+
+# %% [markdown]
+# ## Create the dataset object
+# The definition of all fields can be found in the XSD files describing the
+# expected format:
+#
+# https://github.com/openml/OpenML/blob/master/openml_OS/views/pages/api_new/v1/xsd/openml.data.upload.xsd
+
+# %%
+diabetes_dataset = create_dataset(
+ # The name of the dataset (needs to be unique).
+ # Must not be longer than 128 characters and only contain
+ # a-z, A-Z, 0-9 and the following special characters: _\-\.(),
+ name=name,
+ # Textual description of the dataset.
+ description=description,
+ # The person who created the dataset.
+ creator="Bradley Efron, Trevor Hastie, Iain Johnstone and Robert Tibshirani",
+ # People who contributed to the current version of the dataset.
+ contributor=None,
+ # The date the data was originally collected, given by the uploader.
+ collection_date="09-01-2012",
+ # Language in which the data is represented.
+ # Starts with 1 upper case letter, rest lower case, e.g. 'English'.
+ language="English",
+ # License under which the data is/will be distributed.
+ licence="BSD (from scikit-learn)",
+ # Name of the target. Can also have multiple values (comma-separated).
+ default_target_attribute="class",
+ # The attribute that represents the row-id column, if present in the
+ # dataset.
+ row_id_attribute=None,
+ # Attribute or list of attributes that should be excluded in modelling, such as
+ # identifiers and indexes. E.g. "feat1" or ["feat1","feat2"]
+ ignore_attribute=None,
+ # How to cite the paper.
+ citation=citation,
+ # Attributes of the data
+ attributes=attributes,
+ data=data,
+ # A version label which is provided by the user.
+ version_label="test",
+ original_data_url="https://www4.stat.ncsu.edu/~boos/var.select/diabetes.html",
+ paper_url=paper_url,
+)
+
+# %%
+
+diabetes_dataset.publish()
+print(f"URL for dataset: {diabetes_dataset.openml_url}")
+
+# %% [markdown]
+# ## Dataset is a list
+# A list can contain lists in the case of dense data or it can contain
+# OrderedDicts in the case of sparse data.
+#
+# Weather dataset:
+# https://storm.cis.fordham.edu/~gweiss/data-mining/datasets.html
+
+# %%
+data = [
+ ["sunny", 85, 85, "FALSE", "no"],
+ ["sunny", 80, 90, "TRUE", "no"],
+ ["overcast", 83, 86, "FALSE", "yes"],
+ ["rainy", 70, 96, "FALSE", "yes"],
+ ["rainy", 68, 80, "FALSE", "yes"],
+ ["rainy", 65, 70, "TRUE", "no"],
+ ["overcast", 64, 65, "TRUE", "yes"],
+ ["sunny", 72, 95, "FALSE", "no"],
+ ["sunny", 69, 70, "FALSE", "yes"],
+ ["rainy", 75, 80, "FALSE", "yes"],
+ ["sunny", 75, 70, "TRUE", "yes"],
+ ["overcast", 72, 90, "TRUE", "yes"],
+ ["overcast", 81, 75, "FALSE", "yes"],
+ ["rainy", 71, 91, "TRUE", "no"],
+]
+
+attribute_names = [
+ ("outlook", ["sunny", "overcast", "rainy"]),
+ ("temperature", "REAL"),
+ ("humidity", "REAL"),
+ ("windy", ["TRUE", "FALSE"]),
+ ("play", ["yes", "no"]),
+]
+
+description = (
+ "The weather problem is a tiny dataset that we will use repeatedly"
+ " to illustrate machine learning methods. Entirely fictitious, it "
+ "supposedly concerns the conditions that are suitable for playing "
+ "some unspecified game. In general, instances in a dataset are "
+ "characterized by the values of features, or attributes, that measure "
+ "different aspects of the instance. In this case there are four "
+ "attributes: outlook, temperature, humidity, and windy. "
+ "The outcome is whether to play or not."
+)
+
+citation = (
+ "I. H. Witten, E. Frank, M. A. Hall, and ITPro,"
+ "Data mining practical machine learning tools and techniques, "
+ "third edition. Burlington, Mass.: Morgan Kaufmann Publishers, 2011"
+)
+
+weather_dataset = create_dataset(
+ name="Weather",
+ description=description,
+ creator="I. H. Witten, E. Frank, M. A. Hall, and ITPro",
+ contributor=None,
+ collection_date="01-01-2011",
+ language="English",
+ licence=None,
+ default_target_attribute="play",
+ row_id_attribute=None,
+ ignore_attribute=None,
+ citation=citation,
+ attributes=attribute_names,
+ data=data,
+ version_label="example",
+)
+
+
+# %%
+weather_dataset.publish()
+print(f"URL for dataset: {weather_dataset.openml_url}")
+
+# %% [markdown]
+# ## Dataset is a pandas DataFrame
+# It might happen that your dataset is made of heterogeneous data which can usually
+# be stored as a Pandas DataFrame. DataFrames offer the advantage of
+# storing the type of data for each column as well as the attribute names.
+# Therefore, when providing a Pandas DataFrame, OpenML can infer this
+# information without needing to explicitly provide it when calling the
+# function :func:`openml.datasets.create_dataset`. In this regard, you only
+# need to pass ``'auto'`` to the ``attributes`` parameter.
+
+# %%
+df = pd.DataFrame(data, columns=[col_name for col_name, _ in attribute_names])
+
+# enforce the categorical column to have a categorical dtype
+df["outlook"] = df["outlook"].astype("category")
+df["windy"] = df["windy"].astype("bool")
+df["play"] = df["play"].astype("category")
+print(df.info())
+
+# %% [markdown]
+# We enforce the column 'outlook' and 'play' to be a categorical
+# dtype while the column 'windy' is kept as a boolean column. 'temperature'
+# and 'humidity' are kept as numeric columns. Then, we can
+# call :func:`openml.datasets.create_dataset` by passing the dataframe and
+# fixing the parameter ``attributes`` to ``'auto'``.
+
+# %%
+weather_dataset = create_dataset(
+ name="Weather",
+ description=description,
+ creator="I. H. Witten, E. Frank, M. A. Hall, and ITPro",
+ contributor=None,
+ collection_date="01-01-2011",
+ language="English",
+ licence=None,
+ default_target_attribute="play",
+ row_id_attribute=None,
+ ignore_attribute=None,
+ citation=citation,
+ attributes="auto",
+ data=df,
+ version_label="example",
+)
+
+# %%
+weather_dataset.publish()
+print(f"URL for dataset: {weather_dataset.openml_url}")
+
+# %% [markdown]
+# ## Dataset is a sparse matrix
+
+# %%
+sparse_data = coo_matrix(
+ ([0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0], ([0, 1, 1, 2, 2, 3, 3], [0, 1, 2, 0, 2, 0, 1]))
+)
+
+column_names = [
+ ("input1", "REAL"),
+ ("input2", "REAL"),
+ ("y", "REAL"),
+]
+
+xor_dataset = create_dataset(
+ name="XOR",
+ description="Dataset representing the XOR operation",
+ creator=None,
+ contributor=None,
+ collection_date=None,
+ language="English",
+ licence=None,
+ default_target_attribute="y",
+ row_id_attribute=None,
+ ignore_attribute=None,
+ citation=None,
+ attributes=column_names,
+ data=sparse_data,
+ version_label="example",
+)
+
+
+# %%
+xor_dataset.publish()
+print(f"URL for dataset: {xor_dataset.openml_url}")
+
+
+# %% [markdown]
+# ## Dataset is a pandas dataframe with sparse columns
+
+sparse_data = coo_matrix(
+ ([1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0], ([0, 1, 1, 2, 2, 3, 3], [0, 1, 2, 0, 2, 0, 1]))
+)
+column_names = ["input1", "input2", "y"]
+df = pd.DataFrame.sparse.from_spmatrix(sparse_data, columns=column_names)
+print(df.info())
+
+xor_dataset = create_dataset(
+ name="XOR",
+ description="Dataset representing the XOR operation",
+ creator=None,
+ contributor=None,
+ collection_date=None,
+ language="English",
+ licence=None,
+ default_target_attribute="y",
+ row_id_attribute=None,
+ ignore_attribute=None,
+ citation=None,
+ attributes="auto",
+ data=df,
+ version_label="example",
+)
+
+# %%
+
+xor_dataset.publish()
+print(f"URL for dataset: {xor_dataset.openml_url}")
+
+# %%
+openml.config.stop_using_configuration_for_example()
diff --git a/examples/Advanced/datasets_tutorial.py b/examples/Advanced/datasets_tutorial.py
new file mode 100644
index 000000000..cc57686d0
--- /dev/null
+++ b/examples/Advanced/datasets_tutorial.py
@@ -0,0 +1,165 @@
+# %% [markdown]
+# How to list and download datasets.
+
+# %%
+import pandas as pd
+
+import openml
+from openml.datasets import edit_dataset, fork_dataset, get_dataset
+
+# %% [markdown]
+# ## Exercise 0
+#
+# * List datasets and return a dataframe
+
+# %%
+datalist = openml.datasets.list_datasets()
+datalist = datalist[["did", "name", "NumberOfInstances", "NumberOfFeatures", "NumberOfClasses"]]
+
+print(f"First 10 of {len(datalist)} datasets...")
+datalist.head(n=10)
+
+# The same can be done with lesser lines of code
+openml_df = openml.datasets.list_datasets()
+openml_df.head(n=10)
+
+# %% [markdown]
+# ## Exercise 1
+#
+# * Find datasets with more than 10000 examples.
+# * Find a dataset called 'eeg_eye_state'.
+# * Find all datasets with more than 50 classes.
+
+# %%
+datalist[datalist.NumberOfInstances > 10000].sort_values(["NumberOfInstances"]).head(n=20)
+
+# %%
+datalist.query('name == "eeg-eye-state"')
+
+# %%
+datalist.query("NumberOfClasses > 50")
+
+# %% [markdown]
+# ## Download datasets
+
+# %%
+# This is done based on the dataset ID.
+dataset = openml.datasets.get_dataset(dataset_id="eeg-eye-state", version=1)
+
+# Print a summary
+print(
+ f"This is dataset '{dataset.name}', the target feature is '{dataset.default_target_attribute}'"
+)
+print(f"URL: {dataset.url}")
+print(dataset.description[:500])
+
+# %% [markdown]
+# Get the actual data.
+#
+# openml-python returns data as pandas dataframes (stored in the `eeg` variable below),
+# and also some additional metadata that we don't care about right now.
+
+# %%
+eeg, *_ = dataset.get_data()
+
+# %% [markdown]
+# You can optionally choose to have openml separate out a column from the
+# dataset. In particular, many datasets for supervised problems have a set
+# `default_target_attribute` which may help identify the target variable.
+
+# %%
+X, y, categorical_indicator, attribute_names = dataset.get_data(
+ target=dataset.default_target_attribute
+)
+print(X.head())
+print(X.info())
+
+# %% [markdown]
+# Sometimes you only need access to a dataset's metadata.
+# In those cases, you can download the dataset without downloading the
+# data file. The dataset object can be used as normal.
+# Whenever you use any functionality that requires the data,
+# such as `get_data`, the data will be downloaded.
+# Starting from 0.15, not downloading data will be the default behavior instead.
+# The data will be downloading automatically when you try to access it through
+# openml objects, e.g., using `dataset.features`.
+
+# %%
+dataset = openml.datasets.get_dataset(1471)
+
+# %% [markdown]
+# ## Exercise 2
+# * Explore the data visually.
+
+# %%
+eegs = eeg.sample(n=1000)
+_ = pd.plotting.scatter_matrix(
+ X.iloc[:100, :4],
+ c=y[:100],
+ figsize=(10, 10),
+ marker="o",
+ hist_kwds={"bins": 20},
+ alpha=0.8,
+ cmap="plasma",
+)
+
+
+# %% [markdown]
+# ## Edit a created dataset
+# This example uses the test server, to avoid editing a dataset on the main server.
+
+# %%
+openml.config.start_using_configuration_for_example()
+# %% [markdown]
+# Edit non-critical fields, allowed for all authorized users:
+# description, creator, contributor, collection_date, language, citation,
+# original_data_url, paper_url
+
+# %%
+desc = (
+ "This data sets consists of 3 different types of irises' "
+ "(Setosa, Versicolour, and Virginica) petal and sepal length,"
+ " stored in a 150x4 numpy.ndarray"
+)
+did = 128
+data_id = edit_dataset(
+ did,
+ description=desc,
+ creator="R.A.Fisher",
+ collection_date="1937",
+ citation="The use of multiple measurements in taxonomic problems",
+ language="English",
+)
+edited_dataset = get_dataset(data_id)
+print(f"Edited dataset ID: {data_id}")
+
+
+# %% [markdown]
+# Editing critical fields (default_target_attribute, row_id_attribute, ignore_attribute) is allowed
+# only for the dataset owner. Further, critical fields cannot be edited if the dataset has any
+# tasks associated with it. To edit critical fields of a dataset (without tasks) owned by you,
+# configure the API key:
+# openml.config.apikey = 'FILL_IN_OPENML_API_KEY'
+# This example here only shows a failure when trying to work on a dataset not owned by you:
+
+# %%
+try:
+ data_id = edit_dataset(1, default_target_attribute="shape")
+except openml.exceptions.OpenMLServerException as e:
+ print(e)
+
+# %% [markdown]
+# ## Fork dataset
+# Used to create a copy of the dataset with you as the owner.
+# Use this API only if you are unable to edit the critical fields (default_target_attribute,
+# ignore_attribute, row_id_attribute) of a dataset through the edit_dataset API.
+# After the dataset is forked, you can edit the new version of the dataset using edit_dataset.
+
+# %%
+data_id = fork_dataset(1)
+print(data_id)
+data_id = edit_dataset(data_id, default_target_attribute="shape")
+print(f"Forked dataset ID: {data_id}")
+
+# %%
+openml.config.stop_using_configuration_for_example()
diff --git a/examples/Advanced/fetch_evaluations_tutorial.py b/examples/Advanced/fetch_evaluations_tutorial.py
new file mode 100644
index 000000000..97b8d1bef
--- /dev/null
+++ b/examples/Advanced/fetch_evaluations_tutorial.py
@@ -0,0 +1,181 @@
+# %% [markdown]
+# Evaluations contain a concise summary of the results of all runs made. Each evaluation
+# provides information on the dataset used, the flow applied, the setup used, the metric
+# evaluated, and the result obtained on the metric, for each such run made. These collection
+# of results can be used for efficient benchmarking of an algorithm and also allow transparent
+# reuse of results from previous experiments on similar parameters.
+#
+# In this example, we shall do the following:
+#
+# * Retrieve evaluations based on different metrics
+# * Fetch evaluations pertaining to a specific task
+# * Sort the obtained results in descending order of the metric
+# * Plot a cumulative distribution function for the evaluations
+# * Compare the top 10 performing flows based on the evaluation performance
+# * Retrieve evaluations with hyperparameter settings
+
+# %%
+import openml
+
+# %% [markdown]
+# ## Listing evaluations
+# Evaluations can be retrieved from the database in the chosen output format.
+# Required filters can be applied to retrieve results from runs as required.
+
+# We shall retrieve a small set (only 10 entries) to test the listing function for evaluations
+
+# %%
+openml.evaluations.list_evaluations(function="predictive_accuracy", size=10)
+
+# Using other evaluation metrics, 'precision' in this case
+evals = openml.evaluations.list_evaluations(
+ function="precision", size=10, output_format="dataframe"
+)
+
+# Querying the returned results for precision above 0.98
+print(evals[evals.value > 0.98])
+
+# %% [markdown]
+# ## Viewing a sample task
+# Over here we shall briefly take a look at the details of the task.
+# We will start by displaying a simple *supervised classification* task:
+
+# %%
+task_id = 167140 # https://www.openml.org/t/167140
+task = openml.tasks.get_task(task_id)
+print(task)
+
+# %% [markdown]
+# ## Obtaining all the evaluations for the task
+# We'll now obtain all the evaluations that were uploaded for the task
+# we displayed previously.
+# Note that we now filter the evaluations based on another parameter 'task'.
+
+# %%
+metric = "predictive_accuracy"
+evals = openml.evaluations.list_evaluations(
+ function=metric, tasks=[task_id], output_format="dataframe"
+)
+# Displaying the first 10 rows
+print(evals.head(n=10))
+# Sorting the evaluations in decreasing order of the metric chosen
+evals = evals.sort_values(by="value", ascending=False)
+print("\nDisplaying head of sorted dataframe: ")
+print(evals.head())
+
+# %% [markdown]
+# ## Obtaining CDF of metric for chosen task
+# We shall now analyse how the performance of various flows have been on this task,
+# by seeing the likelihood of the accuracy obtained across all runs.
+# We shall now plot a cumulative distributive function (CDF) for the accuracies obtained.
+
+# %%
+from matplotlib import pyplot as plt
+
+
+def plot_cdf(values, metric="predictive_accuracy"):
+ max_val = max(values)
+ _, _, patches = plt.hist(values, density=True, histtype="step", cumulative=True, linewidth=3)
+ patches[0].set_xy(patches[0].get_xy()[:-1])
+ plt.xlim(max(0, min(values) - 0.1), 1)
+ plt.title("CDF")
+ plt.xlabel(metric)
+ plt.ylabel("Likelihood")
+ plt.grid(visible=True, which="major", linestyle="-")
+ plt.minorticks_on()
+ plt.grid(visible=True, which="minor", linestyle="--")
+ plt.axvline(max_val, linestyle="--", color="gray")
+ plt.text(max_val, 0, f"{max_val:.3f}", fontsize=9)
+ plt.show()
+
+
+plot_cdf(evals.value, metric)
+
+# %% [markdown]
+# This CDF plot shows that for the given task, based on the results of the
+# runs uploaded, it is almost certain to achieve an accuracy above 52%, i.e.,
+# with non-zero probability. While the maximum accuracy seen till now is 96.5%.
+
+# %% [markdown]
+# ## Comparing top 10 performing flows
+# Let us now try to see which flows generally performed the best for this task.
+# For this, we shall compare the top performing flows.
+
+# %%
+import numpy as np
+import pandas as pd
+
+
+def plot_flow_compare(evaluations, top_n=10, metric="predictive_accuracy"):
+ # Collecting the top 10 performing unique flow_id
+ flow_ids = evaluations.flow_id.unique()[:top_n]
+
+ df = pd.DataFrame()
+ # Creating a data frame containing only the metric values of the selected flows
+ # assuming evaluations is sorted in decreasing order of metric
+ for i in range(len(flow_ids)):
+ flow_values = evaluations[evaluations.flow_id == flow_ids[i]].value
+ df = pd.concat([df, flow_values], ignore_index=True, axis=1)
+ _, axs = plt.subplots()
+ df.boxplot()
+ axs.set_title("Boxplot comparing " + metric + " for different flows")
+ axs.set_ylabel(metric)
+ axs.set_xlabel("Flow ID")
+ axs.set_xticklabels(flow_ids)
+ axs.grid(which="major", linestyle="-", linewidth="0.5", color="gray", axis="y")
+ axs.minorticks_on()
+ axs.grid(which="minor", linestyle="--", linewidth="0.5", color="gray", axis="y")
+ # Counting the number of entries for each flow in the data frame
+ # which gives the number of runs for each flow
+ flow_freq = list(df.count(axis=0, numeric_only=True))
+ for i in range(len(flow_ids)):
+ axs.text(i + 1.05, np.nanmin(df.values), str(flow_freq[i]) + "\nrun(s)", fontsize=7)
+ plt.show()
+
+
+plot_flow_compare(evals, metric=metric, top_n=10)
+
+# %% [markdown]
+# The boxplots below show how the flows perform across multiple runs on the chosen
+# task. The green horizontal lines represent the median accuracy of all the runs for
+# that flow (number of runs denoted at the bottom of the boxplots). The higher the
+# green line, the better the flow is for the task at hand. The ordering of the flows
+# are in the descending order of the higest accuracy value seen under that flow.
+
+# Printing the corresponding flow names for the top 10 performing flow IDs
+
+# %%
+top_n = 10
+flow_ids = evals.flow_id.unique()[:top_n]
+flow_names = evals.flow_name.unique()[:top_n]
+for i in range(top_n):
+ print((flow_ids[i], flow_names[i]))
+
+# %% [markdown]
+# ## Obtaining evaluations with hyperparameter settings
+# We'll now obtain the evaluations of a task and a flow with the hyperparameters
+
+# List evaluations in descending order based on predictive_accuracy with
+# hyperparameters
+
+# %%
+evals_setups = openml.evaluations.list_evaluations_setups(
+ function="predictive_accuracy",
+ tasks=[31],
+ size=100,
+ sort_order="desc",
+)
+
+print(evals_setups.head())
+
+# %% [markdown]
+# Return evaluations for flow_id in descending order based on predictive_accuracy
+# with hyperparameters. parameters_in_separate_columns returns parameters in
+# separate columns
+
+# %%
+evals_setups = openml.evaluations.list_evaluations_setups(
+ function="predictive_accuracy", flows=[6767], size=100, parameters_in_separate_columns=True
+)
+
+print(evals_setups.head(10))
diff --git a/examples/Advanced/study_tutorial.py b/examples/Advanced/study_tutorial.py
new file mode 100644
index 000000000..6912efd06
--- /dev/null
+++ b/examples/Advanced/study_tutorial.py
@@ -0,0 +1,120 @@
+# %% [markdown]
+# How to list, download and upload benchmark studies.
+# In contrast to
+# [benchmark suites](https://docs.openml.org/benchmark/#benchmarking-suites) which
+# hold a list of tasks, studies hold a list of runs. As runs contain all information on flows and
+# tasks, all required information about a study can be retrieved.
+
+# %%
+import uuid
+
+from sklearn.ensemble import RandomForestClassifier
+
+import openml
+
+# %% [markdown]
+# ## Listing studies
+#
+# * Use the output_format parameter to select output type
+# * Default gives ``dict``, but we'll use ``dataframe`` to obtain an
+# easier-to-work-with data structure
+
+# %%
+studies = openml.study.list_studies(status="all")
+print(studies.head(n=10))
+
+
+# %% [markdown]
+# ## Downloading studies
+# This is done based on the study ID.
+
+# %%
+study = openml.study.get_study(123)
+print(study)
+
+# %% [markdown]
+# Studies also features a description:
+
+# %%
+print(study.description)
+
+# %% [markdown]
+# Studies are a container for runs:
+
+# %%
+print(study.runs)
+
+# %% [markdown]
+# And we can use the evaluation listing functionality to learn more about
+# the evaluations available for the conducted runs:
+
+# %%
+evaluations = openml.evaluations.list_evaluations(
+ function="predictive_accuracy",
+ study=study.study_id,
+ output_format="dataframe",
+)
+print(evaluations.head())
+
+# %% [markdown]
+# We'll use the test server for the rest of this tutorial.
+
+# %%
+openml.config.start_using_configuration_for_example()
+
+# %% [markdown]
+# ## Uploading studies
+#
+# Creating a study is as simple as creating any kind of other OpenML entity.
+# In this examples we'll create a few runs for the OpenML-100 benchmark
+# suite which is available on the OpenML test server.
+
+#
+#
Warning
+#
+# For the rest of this tutorial, we will require the `openml-sklearn` package.
+# Install it with `pip install openml-sklearn`.
+#
+#
+
+# %%
+# Get sklearn extension to run sklearn models easily on OpenML tasks.
+from openml_sklearn import SklearnExtension
+
+extension = SklearnExtension()
+
+# Model to be used
+clf = RandomForestClassifier()
+
+# We'll create a study with one run on 3 datasets present in the suite
+tasks = [115, 259, 307]
+
+# To verify
+# https://test.openml.org/api/v1/study/1
+suite = openml.study.get_suite("OpenML100")
+print(all(t_id in suite.tasks for t_id in tasks))
+
+run_ids = []
+for task_id in tasks:
+ task = openml.tasks.get_task(task_id)
+ run = openml.runs.run_model_on_task(clf, task)
+ run.publish()
+ run_ids.append(run.run_id)
+
+# The study needs a machine-readable and unique alias. To obtain this,
+# we simply generate a random uuid.
+alias = uuid.uuid4().hex
+
+new_study = openml.study.create_study(
+ name="Test-Study",
+ description="Test study for the Python tutorial on studies",
+ run_ids=run_ids,
+ alias=alias,
+ benchmark_suite=suite.study_id,
+)
+new_study.publish()
+print(new_study)
+
+
+# %%
+openml.config.stop_using_configuration_for_example()
diff --git a/examples/Advanced/suites_tutorial.py b/examples/Advanced/suites_tutorial.py
new file mode 100644
index 000000000..8459510ef
--- /dev/null
+++ b/examples/Advanced/suites_tutorial.py
@@ -0,0 +1,92 @@
+# %% [markdown]
+# How to list, download and upload benchmark suites.
+
+# %%
+import uuid
+
+import numpy as np
+
+import openml
+
+# %% [markdown]
+# ## Listing suites
+#
+# * Use the output_format parameter to select output type
+# * Default gives ``dict``, but we'll use ``dataframe`` to obtain an
+# easier-to-work-with data structure
+
+# %%
+suites = openml.study.list_suites(status="all")
+print(suites.head(n=10))
+
+# %% [markdown]
+# ## Downloading suites
+# This is done based on the dataset ID.
+
+# %%
+suite = openml.study.get_suite(99)
+print(suite)
+
+# %% [markdown]
+# Suites also feature a description:
+
+# %%
+print(suite.description)
+
+# %% [markdown]
+# Suites are a container for tasks:
+
+# %%
+print(suite.tasks)
+
+# %% [markdown]
+# And we can use the task listing functionality to learn more about them:
+
+# %%
+tasks = openml.tasks.list_tasks()
+
+# %% [markdown]
+# Using ``@`` in
+# [pd.DataFrame.query](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.query.html)
+# accesses variables outside of the current dataframe.
+
+# %%
+tasks = tasks.query("tid in @suite.tasks")
+print(tasks.describe().transpose())
+
+# %% [markdown]
+# We'll use the test server for the rest of this tutorial.
+
+# %%
+openml.config.start_using_configuration_for_example()
+
+# %% [markdown]
+# ## Uploading suites
+#
+# Uploading suites is as simple as uploading any kind of other OpenML
+# entity - the only reason why we need so much code in this example is
+# because we upload some random data.
+
+# We'll take a random subset of at least ten tasks of all available tasks on
+# the test server:
+
+# %%
+all_tasks = list(openml.tasks.list_tasks()["tid"])
+task_ids_for_suite = sorted(np.random.choice(all_tasks, replace=False, size=20)) # noqa: NPY002
+
+# The study needs a machine-readable and unique alias. To obtain this,
+# we simply generate a random uuid.
+
+alias = uuid.uuid4().hex
+
+new_suite = openml.study.create_benchmark_suite(
+ name="Test-Suite",
+ description="Test suite for the Python tutorial on benchmark suites",
+ task_ids=task_ids_for_suite,
+ alias=alias,
+)
+new_suite.publish()
+print(new_suite)
+
+# %%
+openml.config.stop_using_configuration_for_example()
diff --git a/examples/Advanced/task_manual_iteration_tutorial.py b/examples/Advanced/task_manual_iteration_tutorial.py
new file mode 100644
index 000000000..1e630e213
--- /dev/null
+++ b/examples/Advanced/task_manual_iteration_tutorial.py
@@ -0,0 +1,172 @@
+# %% [markdown]
+# Tasks define a target and a train/test split, which we can use for benchmarking.
+
+# %%
+import openml
+
+# %% [markdown]
+# For this tutorial we will use the famous King+Rook versus King+Pawn on A7 dataset, which has
+# the dataset ID 3 ([dataset on OpenML](https://www.openml.org/d/3)), and for which there exist
+# tasks with all important estimation procedures. It is small enough (less than 5000 samples) to
+# efficiently use it in an example.
+#
+# We will first start with ([task 233](https://www.openml.org/t/233)), which is a task with a
+# holdout estimation procedure.
+
+# %%
+task_id = 233
+task = openml.tasks.get_task(task_id)
+
+# %% [markdown]
+# Now that we have a task object we can obtain the number of repetitions, folds and samples as
+# defined by the task:
+
+# %%
+n_repeats, n_folds, n_samples = task.get_split_dimensions()
+
+# %% [markdown]
+# * ``n_repeats``: Number of times the model quality estimation is performed
+# * ``n_folds``: Number of folds per repeat
+# * ``n_samples``: How many data points to use. This is only relevant for learning curve tasks
+#
+# A list of all available estimation procedures is available
+# [here](https://www.openml.org/search?q=%2520measure_type%3Aestimation_procedure&type=measure).
+#
+# Task ``233`` is a simple task using the holdout estimation procedure and therefore has only a
+# single repeat, a single fold and a single sample size:
+
+# %%
+print(
+ f"Task {task_id}: number of repeats: {n_repeats}, number of folds: {n_folds}, number of samples {n_samples}."
+)
+
+# %% [markdown]
+# We can now retrieve the train/test split for this combination of repeats, folds and number of
+# samples (indexing is zero-based). Usually, one would loop over all repeats, folds and sample
+# sizes, but we can neglect this here as there is only a single repetition.
+
+# %%
+train_indices, test_indices = task.get_train_test_split_indices(
+ repeat=0,
+ fold=0,
+ sample=0,
+)
+
+print(train_indices.shape, train_indices.dtype)
+print(test_indices.shape, test_indices.dtype)
+
+# %% [markdown]
+# And then split the data based on this:
+
+# %%
+X, y = task.get_X_and_y()
+X_train = X.iloc[train_indices]
+y_train = y.iloc[train_indices]
+X_test = X.iloc[test_indices]
+y_test = y.iloc[test_indices]
+
+print(
+ f"X_train.shape: {X_train.shape}, y_train.shape: {y_train.shape}, X_test.shape: {X_test.shape}, y_test.shape: {y_test.shape}"
+)
+
+# %% [markdown]
+# Obviously, we can also retrieve cross-validation versions of the dataset used in task ``233``:
+
+# %%
+task_id = 3
+task = openml.tasks.get_task(task_id)
+X, y = task.get_X_and_y()
+n_repeats, n_folds, n_samples = task.get_split_dimensions()
+print(
+ f"Task {task_id}: number of repeats: {n_repeats}, number of folds: {n_folds}, number of samples {n_samples}."
+)
+
+# %% [markdown]
+# And then perform the aforementioned iteration over all splits:
+
+# %%
+for repeat_idx in range(n_repeats):
+ for fold_idx in range(n_folds):
+ for sample_idx in range(n_samples):
+ train_indices, test_indices = task.get_train_test_split_indices(
+ repeat=repeat_idx,
+ fold=fold_idx,
+ sample=sample_idx,
+ )
+ X_train = X.iloc[train_indices]
+ y_train = y.iloc[train_indices]
+ X_test = X.iloc[test_indices]
+ y_test = y.iloc[test_indices]
+
+ print(
+ f"Repeat #{repeat_idx}, fold #{fold_idx}, samples {sample_idx}: X_train.shape: {X_train.shape}, "
+ f"y_train.shape {y_train.shape}, X_test.shape {X_test.shape}, y_test.shape {y_test.shape}"
+ )
+
+# %% [markdown]
+# And also versions with multiple repeats:
+
+# %%
+task_id = 1767
+task = openml.tasks.get_task(task_id)
+X, y = task.get_X_and_y()
+n_repeats, n_folds, n_samples = task.get_split_dimensions()
+print(
+ f"Task {task_id}: number of repeats: {n_repeats}, number of folds: {n_folds}, number of samples {n_samples}."
+)
+
+# %% [markdown]
+# And then again perform the aforementioned iteration over all splits:
+
+# %%
+for repeat_idx in range(n_repeats):
+ for fold_idx in range(n_folds):
+ for sample_idx in range(n_samples):
+ train_indices, test_indices = task.get_train_test_split_indices(
+ repeat=repeat_idx,
+ fold=fold_idx,
+ sample=sample_idx,
+ )
+ X_train = X.iloc[train_indices]
+ y_train = y.iloc[train_indices]
+ X_test = X.iloc[test_indices]
+ y_test = y.iloc[test_indices]
+
+ print(
+ f"Repeat #{repeat_idx}, fold #{fold_idx}, samples {sample_idx}: X_train.shape: {X_train.shape}, "
+ f"y_train.shape {y_train.shape}, X_test.shape {X_test.shape}, y_test.shape {y_test.shape}"
+ )
+
+# %% [markdown]
+# And finally a task based on learning curves:
+
+# %%
+task_id = 1702
+task = openml.tasks.get_task(task_id)
+X, y = task.get_X_and_y()
+n_repeats, n_folds, n_samples = task.get_split_dimensions()
+print(
+ f"Task {task_id}: number of repeats: {n_repeats}, number of folds: {n_folds}, number of samples {n_samples}."
+)
+
+# %% [markdown]
+# And then again perform the aforementioned iteration over all splits:
+
+# %%
+for repeat_idx in range(n_repeats):
+ for fold_idx in range(n_folds):
+ for sample_idx in range(n_samples):
+ train_indices, test_indices = task.get_train_test_split_indices(
+ repeat=repeat_idx,
+ fold=fold_idx,
+ sample=sample_idx,
+ )
+ X_train = X.iloc[train_indices]
+ y_train = y.iloc[train_indices]
+ X_test = X.iloc[test_indices]
+ y_test = y.iloc[test_indices]
+
+ print(
+ f"Repeat #{repeat_idx}, fold #{fold_idx}, samples {sample_idx}: X_train.shape: {X_train.shape}, "
+ f"y_train.shape {y_train.shape}, X_test.shape {X_test.shape}, y_test.shape {y_test.shape}"
+ )
diff --git a/examples/Advanced/tasks_tutorial.py b/examples/Advanced/tasks_tutorial.py
new file mode 100644
index 000000000..dff7293ad
--- /dev/null
+++ b/examples/Advanced/tasks_tutorial.py
@@ -0,0 +1,201 @@
+# %% [markdown]
+# A tutorial on how to list and download tasks.
+
+# %%
+import openml
+from openml.tasks import TaskType
+
+# %% [markdown]
+#
+# Tasks are identified by IDs and can be accessed in two different ways:
+#
+# 1. In a list providing basic information on all tasks available on OpenML.
+# This function will not download the actual tasks, but will instead download
+# meta data that can be used to filter the tasks and retrieve a set of IDs.
+# We can filter this list, for example, we can only list tasks having a
+# special tag or only tasks for a specific target such as
+# *supervised classification*.
+# 2. A single task by its ID. It contains all meta information, the target
+# metric, the splits and an iterator which can be used to access the
+# splits in a useful manner.
+
+# %% [markdown]
+# ## Listing tasks
+#
+# We will start by simply listing only *supervised classification* tasks.
+#
+# **openml.tasks.list_tasks()** returns a dictionary of dictionaries by default, but we
+# request a
+# [pandas dataframe](https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.html)
+# instead to have better visualization capabilities and easier access:
+
+# %%
+tasks = openml.tasks.list_tasks(task_type=TaskType.SUPERVISED_CLASSIFICATION)
+print(tasks.columns)
+print(f"First 5 of {len(tasks)} tasks:")
+print(tasks.head())
+
+# %% [markdown]
+# We can filter the list of tasks to only contain datasets with more than
+# 500 samples, but less than 1000 samples:
+
+# %%
+filtered_tasks = tasks.query("NumberOfInstances > 500 and NumberOfInstances < 1000")
+print(list(filtered_tasks.index))
+
+
+# %%
+# Number of tasks
+print(len(filtered_tasks))
+
+# %% [markdown]
+# Then, we can further restrict the tasks to all have the same resampling strategy:
+
+# %%
+filtered_tasks = filtered_tasks.query('estimation_procedure == "10-fold Crossvalidation"')
+print(list(filtered_tasks.index))
+
+# %%
+# Number of tasks
+print(len(filtered_tasks))
+
+# %% [markdown]
+# Resampling strategies can be found on the
+# [OpenML Website](https://www.openml.org/search?type=measure&q=estimation%20procedure).
+#
+# Similar to listing tasks by task type, we can list tasks by tags:
+
+# %%
+tasks = openml.tasks.list_tasks(tag="OpenML100")
+print(f"First 5 of {len(tasks)} tasks:")
+print(tasks.head())
+
+# %% [markdown]
+# Furthermore, we can list tasks based on the dataset id:
+
+# %%
+tasks = openml.tasks.list_tasks(data_id=1471)
+print(f"First 5 of {len(tasks)} tasks:")
+print(tasks.head())
+
+# %% [markdown]
+# In addition, a size limit and an offset can be applied both separately and simultaneously:
+
+# %%
+tasks = openml.tasks.list_tasks(size=10, offset=50)
+print(tasks)
+
+# %% [markdown]
+#
+# **OpenML 100**
+# is a curated list of 100 tasks to start using OpenML. They are all
+# supervised classification tasks with more than 500 instances and less than 50000
+# instances per task. To make things easier, the tasks do not contain highly
+# unbalanced data and sparse data. However, the tasks include missing values and
+# categorical features. You can find out more about the *OpenML 100* on
+# [the OpenML benchmarking page](https://docs.openml.org/benchmark/).
+#
+# Finally, it is also possible to list all tasks on OpenML with:
+
+# %%
+tasks = openml.tasks.list_tasks()
+print(len(tasks))
+
+# %% [markdown]
+# ## Exercise
+#
+# Search for the tasks on the 'eeg-eye-state' dataset.
+
+# %%
+tasks.query('name=="eeg-eye-state"')
+
+# %% [markdown]
+# ## Downloading tasks
+#
+# We provide two functions to download tasks, one which downloads only a
+# single task by its ID, and one which takes a list of IDs and downloads
+# all of these tasks:
+
+# %%
+task_id = 31
+task = openml.tasks.get_task(task_id)
+
+# %%
+# Properties of the task are stored as member variables:
+print(task)
+
+# %%
+# And:
+
+ids = [2, 1891, 31, 9983]
+tasks = openml.tasks.get_tasks(ids)
+print(tasks[0])
+
+# %% [markdown]
+# ## Creating tasks
+#
+# You can also create new tasks. Take the following into account:
+#
+# * You can only create tasks on *active* datasets
+# * For now, only the following tasks are supported: classification, regression,
+# clustering, and learning curve analysis.
+# * For now, tasks can only be created on a single dataset.
+# * The exact same task must not already exist.
+#
+# Creating a task requires the following input:
+#
+# * task_type: The task type ID, required (see below). Required.
+# * dataset_id: The dataset ID. Required.
+# * target_name: The name of the attribute you aim to predict. Optional.
+# * estimation_procedure_id : The ID of the estimation procedure used to create train-test
+# splits. Optional.
+# * evaluation_measure: The name of the evaluation measure. Optional.
+# * Any additional inputs for specific tasks
+#
+# It is best to leave the evaluation measure open if there is no strong prerequisite for a
+# specific measure. OpenML will always compute all appropriate measures and you can filter
+# or sort results on your favourite measure afterwards. Only add an evaluation measure if
+# necessary (e.g. when other measure make no sense), since it will create a new task, which
+# scatters results across tasks.
+
+# %% [markdown]
+# We'll use the test server for the rest of this tutorial.
+
+# %%
+openml.config.start_using_configuration_for_example()
+
+# %% [markdown]
+# ## Example
+#
+# Let's create a classification task on a dataset. In this example we will do this on the
+# Iris dataset (ID=128 (on test server)). We'll use 10-fold cross-validation (ID=1),
+# and *predictive accuracy* as the predefined measure (this can also be left open).
+# If a task with these parameters exists, we will get an appropriate exception.
+# If such a task doesn't exist, a task will be created and the corresponding task_id
+# will be returned.
+
+# %%
+try:
+ my_task = openml.tasks.create_task(
+ task_type=TaskType.SUPERVISED_CLASSIFICATION,
+ dataset_id=128,
+ target_name="class",
+ evaluation_measure="predictive_accuracy",
+ estimation_procedure_id=1,
+ )
+ my_task.publish()
+except openml.exceptions.OpenMLServerException as e:
+ # Error code for 'task already exists'
+ if e.code == 614:
+ # Lookup task
+ tasks = openml.tasks.list_tasks(data_id=128)
+ tasks = tasks.query(
+ 'task_type == "Supervised Classification" '
+ 'and estimation_procedure == "10-fold Crossvalidation" '
+ 'and evaluation_measures == "predictive_accuracy"'
+ )
+ task_id = tasks.loc[:, "tid"].values[0]
+ print("Task already exists. Task ID is", task_id)
+
+# %%
+openml.config.stop_using_configuration_for_example()
diff --git a/examples/Basics/introduction_tutorial.py b/examples/Basics/introduction_tutorial.py
new file mode 100644
index 000000000..2ba2d0ef1
--- /dev/null
+++ b/examples/Basics/introduction_tutorial.py
@@ -0,0 +1,55 @@
+# %% [markdown]
+# ## Installation
+# Installation is done via ``pip``:
+#
+# ```bash
+# pip install openml
+# ```
+
+# %% [markdown]
+# ## Authentication
+#
+# For certain functionality, such as uploading tasks or datasets, users have to
+# sign up. Only accessing the data on OpenML does not require an account!
+#
+# If you don't have an account yet, sign up now.
+# You will receive an API key, which will authenticate you to the server
+# and allow you to download and upload datasets, tasks, runs and flows.
+#
+# * Create an OpenML account (free) on https://www.openml.org.
+# * After logging in, open your account page (avatar on the top right)
+# * Open 'Account Settings', then 'API authentication' to find your API key.
+#
+# There are two ways to permanently authenticate:
+#
+# * Use the ``openml`` CLI tool with ``openml configure apikey MYKEY``,
+# replacing **MYKEY** with your API key.
+# * Create a plain text file **~/.openml/config** with the line
+# **'apikey=MYKEY'**, replacing **MYKEY** with your API key. The config
+# file must be in the directory ~/.openml/config and exist prior to
+# importing the openml module.
+#
+# Alternatively, by running the code below and replacing 'YOURKEY' with your API key,
+# you authenticate for the duration of the Python process.
+
+# %%
+import openml
+
+openml.config.apikey = "YOURKEY"
+
+# %% [markdown]
+# ## Caching
+# When downloading datasets, tasks, runs and flows, they will be cached to
+# retrieve them without calling the server later. As with the API key,
+# the cache directory can be either specified through the config file or
+# through the API:
+#
+# * Add the line **cachedir = 'MYDIR'** to the config file, replacing
+# 'MYDIR' with the path to the cache directory. By default, OpenML
+# will use **~/.openml/cache** as the cache directory.
+# * Run the code below, replacing 'YOURDIR' with the path to the cache directory.
+
+# %%
+import openml
+
+openml.config.set_root_cache_directory("YOURDIR")
diff --git a/examples/Basics/simple_datasets_tutorial.py b/examples/Basics/simple_datasets_tutorial.py
new file mode 100644
index 000000000..75d36ed0f
--- /dev/null
+++ b/examples/Basics/simple_datasets_tutorial.py
@@ -0,0 +1,57 @@
+# %% [markdown]
+# A basic tutorial on how to list, load and visualize datasets.
+#
+# In general, we recommend working with tasks, so that the results can
+# be easily reproduced. Furthermore, the results can be compared to existing results
+# at OpenML. However, for the purposes of this tutorial, we are going to work with
+# the datasets directly.
+
+# %%
+
+import openml
+
+# %% [markdown]
+# ## List datasets stored on OpenML
+
+# %%
+datasets_df = openml.datasets.list_datasets()
+print(datasets_df.head(n=10))
+
+# %% [markdown]
+# ## Download a dataset
+
+# %%
+# Iris dataset https://www.openml.org/d/61
+dataset = openml.datasets.get_dataset(dataset_id=61)
+
+# Print a summary
+print(
+ f"This is dataset '{dataset.name}', the target feature is '{dataset.default_target_attribute}'"
+)
+print(f"URL: {dataset.url}")
+print(dataset.description[:500])
+
+# %% [markdown]
+# ## Load a dataset
+# * `X` - A dataframe where each row represents one example with
+# the corresponding feature values.
+# * `y` - the classes for each example
+# * `categorical_indicator` - a list that indicates which feature is categorical
+# * `attribute_names` - the names of the features for the examples (X) and
+# target feature (y)
+
+# %%
+X, y, categorical_indicator, attribute_names = dataset.get_data(
+ target=dataset.default_target_attribute
+)
+
+# %% [markdown]
+# Visualize the dataset
+
+# %%
+import matplotlib.pyplot as plt
+import pandas as pd
+import seaborn as sns
+
+iris_plot = sns.pairplot(pd.concat([X, y], axis=1), hue="class")
+plt.show()
diff --git a/examples/Basics/simple_flows_and_runs_tutorial.py b/examples/Basics/simple_flows_and_runs_tutorial.py
new file mode 100644
index 000000000..eb42c7d02
--- /dev/null
+++ b/examples/Basics/simple_flows_and_runs_tutorial.py
@@ -0,0 +1,122 @@
+# %% [markdown]
+# A simple tutorial on how to upload results from a machine learning experiment to OpenML.
+
+# %%
+import sklearn
+from sklearn.neighbors import KNeighborsClassifier
+
+import openml
+
+# %% [markdown]
+#
+#
Warning
+#
+# This example uploads data. For that reason, this example connects to the
+# test server at test.openml.org .
+# This prevents the main server from becoming overloaded with example datasets, tasks,
+# runs, and other submissions.
+# Using this test server may affect the behavior and performance of the
+# OpenML-Python API.
+#
+#
+
+# %%
+openml.config.start_using_configuration_for_example()
+
+# %% [markdown]
+# ## Train a machine learning model and evaluate it
+# NOTE: We are using task 119 from the test server: https://test.openml.org/d/20
+
+# %%
+task = openml.tasks.get_task(119)
+
+# Get the data
+dataset = task.get_dataset()
+X, y, categorical_indicator, attribute_names = dataset.get_data(
+ target=dataset.default_target_attribute
+)
+
+# Get the holdout split from the task
+train_indices, test_indices = task.get_train_test_split_indices(fold=0, repeat=0)
+X_train, X_test = X.iloc[train_indices], X.iloc[test_indices]
+y_train, y_test = y.iloc[train_indices], y.iloc[test_indices]
+
+knn_parameters = {
+ "n_neighbors": 3,
+}
+clf = KNeighborsClassifier(**knn_parameters)
+clf.fit(X_train, y_train)
+
+# Get experiment results
+y_pred = clf.predict(X_test)
+y_pred_proba = clf.predict_proba(X_test)
+
+# %% [markdown]
+# ## Upload the machine learning experiments to OpenML
+# First, create a fow and fill it with metadata about the machine learning model.
+
+# %%
+knn_flow = openml.flows.OpenMLFlow(
+ # Metadata
+ model=clf, # or None, if you do not want to upload the model object.
+ name="CustomKNeighborsClassifier",
+ description="A custom KNeighborsClassifier flow for OpenML.",
+ external_version=f"{sklearn.__version__}",
+ language="English",
+ tags=["openml_tutorial_knn"],
+ dependencies=f"{sklearn.__version__}",
+ # Hyperparameters
+ parameters={k: str(v) for k, v in knn_parameters.items()},
+ parameters_meta_info={
+ "n_neighbors": {"description": "number of neighbors to use", "data_type": "int"}
+ },
+ # If you have a pipeline with subcomponents, such as preprocessing, add them here.
+ components={},
+)
+knn_flow.publish()
+print(f"knn_flow was published with the ID {knn_flow.flow_id}")
+
+# %% [markdown]
+# Second, we create a run to store the results associated with the flow.
+
+# %%
+
+# Format the predictions for OpenML
+predictions = []
+for test_index, y_true_i, y_pred_i, y_pred_proba_i in zip(
+ test_indices, y_test, y_pred, y_pred_proba, strict=False
+):
+ predictions.append(
+ openml.runs.functions.format_prediction(
+ task=task,
+ repeat=0,
+ fold=0,
+ index=test_index,
+ prediction=y_pred_i,
+ truth=y_true_i,
+ proba=dict(zip(task.class_labels, y_pred_proba_i, strict=False)),
+ )
+ )
+
+# Format the parameters for OpenML
+oml_knn_parameters = [
+ {"oml:name": k, "oml:value": v, "oml:component": knn_flow.flow_id}
+ for k, v in knn_parameters.items()
+]
+
+knn_run = openml.runs.OpenMLRun(
+ task_id=task.task_id,
+ flow_id=knn_flow.flow_id,
+ dataset_id=dataset.dataset_id,
+ parameter_settings=oml_knn_parameters,
+ data_content=predictions,
+ tags=["openml_tutorial_knn"],
+ description_text="Run generated by the tutorial.",
+)
+knn_run = knn_run.publish()
+print(f"Run was uploaded to {knn_run.openml_url}")
+print(f"The flow can be found at {knn_run.flow.openml_url}")
+
+# %%
+openml.config.stop_using_configuration_for_example()
diff --git a/examples/Basics/simple_suites_tutorial.py b/examples/Basics/simple_suites_tutorial.py
new file mode 100644
index 000000000..cc3c7b1cf
--- /dev/null
+++ b/examples/Basics/simple_suites_tutorial.py
@@ -0,0 +1,53 @@
+# %% [markdown]
+# This is a brief showcase of OpenML benchmark suites, which were introduced by
+# [Bischl et al. (2019)](https://arxiv.org/abs/1708.03731v2). Benchmark suites standardize the
+# datasets and splits to be used in an experiment or paper. They are fully integrated into OpenML
+# and simplify both the sharing of the setup and the results.
+
+# %%
+import openml
+
+# %% [markdown]
+# ## OpenML-CC18
+#
+# As an example we have a look at the OpenML-CC18, which is a suite of 72 classification datasets
+# from OpenML which were carefully selected to be usable by many algorithms. These are all datasets
+# from mid-2018 that satisfy a large set of clear requirements for thorough yet practical benchmarking:
+#
+# 1. the number of observations are between 500 and 100,000 to focus on medium-sized datasets,
+# 2. the number of features does not exceed 5,000 features to keep the runtime of the algorithms
+# low
+# 3. the target attribute has at least two classes with no class having less than 20 observations
+# 4. the ratio of the minority class and the majority class is above 0.05 (to eliminate highly
+# imbalanced datasets which require special treatment for both algorithms and evaluation
+# measures).
+#
+# A full description can be found in the
+# [OpenML benchmarking docs](https://docs.openml.org/benchmark/#openml-cc18).
+#
+# In this example, we'll focus on how to use benchmark suites in practice.
+
+# %% [markdown]
+# ## Downloading benchmark suites
+
+# %%
+suite = openml.study.get_suite(99)
+print(suite)
+
+# %% [markdown]
+# The benchmark suite does not download the included tasks and datasets itself, but only contains
+# a list of which tasks constitute the study.
+#
+# Tasks can then be accessed via
+
+# %%
+tasks = suite.tasks
+print(tasks)
+
+# %% [markdown]
+# and iterated over for benchmarking. For speed reasons, we only iterate over the first three tasks:
+
+# %%
+for task_id in tasks[:3]:
+ task = openml.tasks.get_task(task_id)
+ print(task)
diff --git a/examples/Basics/simple_tasks_tutorial.py b/examples/Basics/simple_tasks_tutorial.py
new file mode 100644
index 000000000..598ce4e71
--- /dev/null
+++ b/examples/Basics/simple_tasks_tutorial.py
@@ -0,0 +1,27 @@
+# %% [markdown]
+# A brief example on how to use tasks from OpenML.
+
+# %%
+
+import openml
+
+# %% [markdown]
+# Get a [task](https://docs.openml.org/concepts/tasks/) for
+# [supervised classification on credit-g](https://www.openml.org/search?type=task&id=31&source_data.data_id=31):
+
+# %%
+task = openml.tasks.get_task(31)
+
+# %% [markdown]
+# Get the dataset and its data from the task.
+
+# %%
+dataset = task.get_dataset()
+X, y, categorical_indicator, attribute_names = dataset.get_data(target=task.target_name)
+
+# %% [markdown]
+# Get the first out of the 10 cross-validation splits from the task.
+
+# %%
+train_indices, test_indices = task.get_train_test_split_indices(fold=0)
+print(train_indices[:10]) # print the first 10 indices of the training set
diff --git a/examples/OpenMLDemo.ipynb b/examples/OpenMLDemo.ipynb
deleted file mode 100644
index 42758cf80..000000000
--- a/examples/OpenMLDemo.ipynb
+++ /dev/null
@@ -1,703 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# OpenML in Python \n",
- "(Work in progress) \n",
- "Joaquin Vanschoren @joavanschoren"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## How to...\n",
- "- Download datasets and tasks\n",
- "- Use scikit-learn to build classifiers\n",
- "- Upload the results to the server\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Setup"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Authentication\n",
- "Via API key (e.g. 'rgu9hw1h03g24j974hr3586g4j598fgh') \n",
- "Always keep it secret"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Go to your OpenML account settings and see 'API authentication' to retrieve your key. \n",
- " "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Installation\n",
- "Via source"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {
- "collapsed": true
- },
- "outputs": [],
- "source": [
- "#! git clone https://github.com/openml/openml-python.\n",
- "#! cd openml-python\n",
- "#! python setup.py install"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Setup"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {
- "collapsed": false
- },
- "outputs": [],
- "source": [
- "from sklearn import preprocessing, ensemble\n",
- "\n",
- "import openml\n",
- "import numpy as np\n",
- "import pandas as pd\n",
- "import os\n",
- "\n",
- "# assumes you have your api key in ~/.openml/config\n",
- "# amueller's read/write key that he will throw away later\n",
- "openml.config.apikey='610344db6388d9ba34f6db45a3cf71de'\n",
- "# we also want to use the test server so as not to polute the production system\n",
- "openml.config.server = \"http://test.openml.org/api/v1/xml\""
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Data"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## List ALL the datasets"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {
- "collapsed": false
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "First 10 of 2855 datasets...\n",
- " did name NumberOfInstances NumberOfFeatures\n",
- "0 1 anneal 898 39\n",
- "1 2 anneal 898 39\n",
- "2 3 kr-vs-kp 3196 37\n",
- "3 4 labor 57 17\n",
- "4 5 arrhythmia 452 280\n",
- "5 6 letter 20000 17\n",
- "6 7 audiology 226 70\n",
- "7 8 liver-disorders 345 7\n",
- "8 9 autos 205 26\n",
- "9 10 lymph 148 19\n"
- ]
- }
- ],
- "source": [
- "datasets = openml.datasets.list_datasets()\n",
- "\n",
- "data = pd.DataFrame(datasets).transpose()\n",
- "print(\"First 10 of %s datasets...\" % len(datasets))\n",
- "print(data[:10][['did','name','NumberOfInstances','NumberOfFeatures']])"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Subset based on any property"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {
- "collapsed": false
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "First 10 of 738 datasets...\n",
- " did name NumberOfInstances NumberOfFeatures\n",
- "2 3 kr-vs-kp 3196 37\n",
- "3 4 labor 57 17\n",
- "12 13 breast-cancer 286 10\n",
- "14 15 breast-w 699 10\n",
- "23 24 mushroom 8124 23\n",
- "24 25 colic 368 28\n",
- "26 27 colic 368 23\n",
- "28 29 credit-a 690 16\n",
- "30 31 credit-g 1000 21\n",
- "32 33 cylinder-bands 540 40\n"
- ]
- }
- ],
- "source": [
- "bin_data = data.loc[data['NumberOfClasses'] == 2]\n",
- "print(\"First 10 of %s datasets...\" % len(bin_data))\n",
- "print(bin_data[:10][['did','name', 'NumberOfInstances','NumberOfFeatures']])"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Subset based on any property"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {
- "collapsed": false
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "First 10 of 738 datasets...\n",
- " did name NumberOfInstances\n",
- "1302 1588 w8a 64700\n",
- "2421 4533 KEGGMetabolicReactionNetwork 65554\n",
- "1305 1591 connect-4 67557\n",
- "423 554 mnist_784 70000\n",
- "1293 1578 real-sim 72309\n",
- "1062 1213 BNG(mv) 78732\n",
- "2420 4532 higgs 98050\n",
- "247 357 vehicle_sensIT 98528\n",
- "1080 1242 vehicleNorm 98528\n",
- "1307 1593 SensIT-Vehicle-Combined 98528\n"
- ]
- }
- ],
- "source": [
- "big_data = data.loc[data['NumberOfInstances'] > 60000]\n",
- "big_data = big_data.sort_values(by='NumberOfInstances', ascending=True)\n",
- "print(\"First 10 of %s datasets...\" % len(bin_data))\n",
- "print(big_data[:10][['did','name', 'NumberOfInstances']])"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Download a specific dataset. This is done based on the dataset ID (called 'did' in the table above)."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {
- "collapsed": false
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "This is dataset 'iris', the target feature is called 'class'\n",
- "URL: http://www.openml.org/data/download/61/dataset_61_iris.arff\n",
- "**Author**: R.A. Fisher \n",
- "**Source**: [UCI](https://archive.ics.uci.edu/ml/datasets/Iris) - 1936 - Donated by Michael Marshall \n",
- "**Please cite**: \n",
- "\n",
- "**Iris Plants Database** \n",
- "This is perhaps the best known database to be found in the pattern recognition literature. Fisher's paper is a classic in the field and is referenced frequently to this day. (See Duda & Hart, for example.) The data set contains 3 classes of 50 instances each, where each class refers to a type of iris plant. One class \n"
- ]
- }
- ],
- "source": [
- "dataset = openml.datasets.get_dataset(61)\n",
- "\n",
- "print(\"This is dataset '%s', the target feature is called '%s'\" % (dataset.name, dataset.default_target_attribute))\n",
- "print(\"URL: %s\" % dataset.url)\n",
- "print(dataset.description[:500])"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "metadata": {
- "collapsed": false
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "{'citation': None,\n",
- " 'collection_date': '1936',\n",
- " 'contributor': None,\n",
- " 'creator': 'R.A. Fisher',\n",
- " 'data_file': '/home/andy/.openml/cache/datasets/61/dataset.arff',\n",
- " 'data_pickle_file': '/home/andy/.openml/cache/datasets/61/dataset.pkl',\n",
- " 'dataset_id': 61,\n",
- " 'default_target_attribute': 'class',\n",
- " 'description': '**Author**: R.A. Fisher \\n'\n",
- " '**Source**: '\n",
- " '[UCI](https://archive.ics.uci.edu/ml/datasets/Iris) - 1936 - '\n",
- " 'Donated by Michael Marshall \\n'\n",
- " '**Please cite**: \\n'\n",
- " '\\n'\n",
- " '**Iris Plants Database** \\n'\n",
- " 'This is perhaps the best known database to be found in the '\n",
- " \"pattern recognition literature. Fisher's paper is a classic \"\n",
- " 'in the field and is referenced frequently to this day. (See '\n",
- " 'Duda & Hart, for example.) The data set contains 3 classes '\n",
- " 'of 50 instances each, where each class refers to a type of '\n",
- " 'iris plant. One class is linearly separable from the '\n",
- " 'other 2; the latter are NOT linearly separable from each '\n",
- " 'other.\\n'\n",
- " '\\n'\n",
- " 'Predicted attribute: class of iris plant. \\n'\n",
- " 'This is an exceedingly simple domain. \\n'\n",
- " ' \\n'\n",
- " '### Attribute Information:\\n'\n",
- " ' 1. sepal length in cm\\n'\n",
- " ' 2. sepal width in cm\\n'\n",
- " ' 3. petal length in cm\\n'\n",
- " ' 4. petal width in cm\\n'\n",
- " ' 5. class: \\n'\n",
- " ' -- Iris Setosa\\n'\n",
- " ' -- Iris Versicolour\\n'\n",
- " ' -- Iris Virginica',\n",
- " 'format': 'ARFF',\n",
- " 'ignore_attributes': None,\n",
- " 'language': None,\n",
- " 'licence': 'Public',\n",
- " 'md5_cheksum': '3a212cce13fc6f9b94f4793285813d95',\n",
- " 'name': 'iris',\n",
- " 'original_data_url': 'https://archive.ics.uci.edu/ml/datasets/Iris',\n",
- " 'paper_url': 'http://digital.library.adelaide.edu.au/dspace/handle/2440/15227',\n",
- " 'row_id_attribute': None,\n",
- " 'tag': ['study_1', 'study_4', 'study_7', 'uci'],\n",
- " 'update_comment': None,\n",
- " 'upload_date': '2014-04-06 23:23:39',\n",
- " 'url': 'http://www.openml.org/data/download/61/dataset_61_iris.arff',\n",
- " 'version': 1,\n",
- " 'version_label': '1',\n",
- " 'visibility': 'public'}\n"
- ]
- }
- ],
- "source": [
- "from pprint import pprint\n",
- "pprint(vars(dataset))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Get the actual data"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "metadata": {
- "collapsed": false
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- " sepallength sepalwidth petallength petalwidth class\n",
- "0 5.1 3.5 1.4 0.2 0\n",
- "1 4.9 3.0 1.4 0.2 0\n",
- "2 4.7 3.2 1.3 0.2 0\n",
- "3 4.6 3.1 1.5 0.2 0\n",
- "4 5.0 3.6 1.4 0.2 0\n",
- "5 5.4 3.9 1.7 0.4 0\n",
- "6 4.6 3.4 1.4 0.3 0\n",
- "7 5.0 3.4 1.5 0.2 0\n",
- "8 4.4 2.9 1.4 0.2 0\n",
- "9 4.9 3.1 1.5 0.1 0\n"
- ]
- }
- ],
- "source": [
- "X, y, attribute_names = dataset.get_data(target=dataset.default_target_attribute, return_attribute_names=True)\n",
- "iris = pd.DataFrame(X, columns=attribute_names)\n",
- "iris['class'] = y\n",
- "print(iris[:10])"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Have fun with it"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 9,
- "metadata": {
- "collapsed": true
- },
- "outputs": [],
- "source": [
- "# iris.plot(kind='scatter', x='petallength', y='petalwidth', c='class', s=50);"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Train a scikit-learn model on the data"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 10,
- "metadata": {
- "collapsed": false
- },
- "outputs": [
- {
- "data": {
- "text/plain": [
- "RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',\n",
- " max_depth=None, max_features='auto', max_leaf_nodes=None,\n",
- " min_samples_leaf=1, min_samples_split=2,\n",
- " min_weight_fraction_leaf=0.0, n_estimators=10, n_jobs=1,\n",
- " oob_score=False, random_state=None, verbose=0,\n",
- " warm_start=False)"
- ]
- },
- "execution_count": 10,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "dataset = openml.datasets.get_dataset(61)\n",
- "X, y = dataset.get_data(target=dataset.default_target_attribute)\n",
- "clf = ensemble.RandomForestClassifier()\n",
- "clf.fit(X, y)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 11,
- "metadata": {
- "collapsed": true
- },
- "outputs": [],
- "source": [
- "# Helper code by Gilles Louppe\n",
- "# % matplotlib inline\n",
- "from matplotlib import pyplot as plt\n",
- "import numpy as np\n",
- "\n",
- "def plot_surface(clf, X, y, \n",
- " xlim=(0, 7), ylim=(0, 3), n_steps=250, \n",
- " subplot=None):\n",
- " if subplot is None:\n",
- " fig = plt.figure()\n",
- " else:\n",
- " plt.subplot(*subplot)\n",
- " \n",
- " xx, yy = np.meshgrid(np.linspace(xlim[0], xlim[1], n_steps), \n",
- " np.linspace(ylim[0], ylim[1], n_steps))\n",
- " \n",
- " if hasattr(clf, \"decision_function\"):\n",
- " z = clf.decision_function(np.c_[xx.ravel(), yy.ravel()])\n",
- " else:\n",
- " z = clf.predict_proba(np.c_[xx.ravel(), yy.ravel()])[:, 1]\n",
- " \n",
- " z = z.reshape(xx.shape)\n",
- " plt.contourf(xx, yy, z, alpha=0.8, cmap=plt.cm.RdBu_r)\n",
- " plt.scatter(X[:, 0], X[:, 1], c=y)\n",
- " plt.xlim(*xlim)\n",
- " plt.ylim(*ylim)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 12,
- "metadata": {
- "collapsed": false
- },
- "outputs": [
- {
- "data": {
- "text/plain": [
- "RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',\n",
- " max_depth=None, max_features='auto', max_leaf_nodes=None,\n",
- " min_samples_leaf=1, min_samples_split=2,\n",
- " min_weight_fraction_leaf=0.0, n_estimators=10, n_jobs=1,\n",
- " oob_score=False, random_state=None, verbose=0,\n",
- " warm_start=False)"
- ]
- },
- "execution_count": 12,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "X_2d = X[:,2:4]\n",
- "clf.fit(X_2d, y)\n",
- "# plot_surface(clf, X_2d, y)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "You can also ask which features are categorical to do your own encoding"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 13,
- "metadata": {
- "collapsed": false
- },
- "outputs": [
- {
- "data": {
- "text/plain": [
- "RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',\n",
- " max_depth=None, max_features='auto', max_leaf_nodes=None,\n",
- " min_samples_leaf=1, min_samples_split=2,\n",
- " min_weight_fraction_leaf=0.0, n_estimators=10, n_jobs=1,\n",
- " oob_score=False, random_state=None, verbose=0,\n",
- " warm_start=False)"
- ]
- },
- "execution_count": 13,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "X, y, categorical = dataset.get_data(target=dataset.default_target_attribute,return_categorical_indicator=True)\n",
- "enc = preprocessing.OneHotEncoder(categorical_features=categorical)\n",
- "X = enc.fit_transform(X)\n",
- "clf.fit(X, y)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Tasks"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "To run benchmarks consistently (also across studies and tools), OpenML offers Tasks, which include specific train-test splits and other information."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## List ALL the tasks"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 14,
- "metadata": {
- "collapsed": false
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "First 5 of 8566 tasks:\n",
- " tid did name task_type estimation_procedure\n",
- "0 1 1 anneal Supervised Classification 10-fold Crossvalidation\n",
- "1 2 2 anneal Supervised Classification 10-fold Crossvalidation\n",
- "2 3 3 kr-vs-kp Supervised Classification 10-fold Crossvalidation\n",
- "3 4 4 labor Supervised Classification 10-fold Crossvalidation\n",
- "4 5 5 arrhythmia Supervised Classification 10-fold Crossvalidation\n"
- ]
- }
- ],
- "source": [
- "task_list = openml.tasks.list_tasks()\n",
- "\n",
- "tasks = pd.DataFrame(task_list).transpose()\n",
- "print(\"First 5 of %s tasks:\" % len(tasks))\n",
- "print(tasks[:5][['tid','did','name','task_type','estimation_procedure']])"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Download tasks"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 15,
- "metadata": {
- "collapsed": false
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "OpenMLTask instance.\n",
- "Task ID: 10\n",
- "Task type: Supervised Classification\n",
- "Dataset id: 10\n"
- ]
- }
- ],
- "source": [
- "task = openml.tasks.get_task(10)\n",
- "print(task)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Runs"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Run a scikit-learn classifier on the task (using the right splits)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 16,
- "metadata": {
- "collapsed": false
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "RandomForest has run on the task.\n"
- ]
- }
- ],
- "source": [
- "from openml.runs import run_task\n",
- "\n",
- "clf = ensemble.RandomForestClassifier()\n",
- "run = run_task(task, clf)\n",
- "print(\"RandomForest has run on the task.\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Upload the run to the OpenML server"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 17,
- "metadata": {
- "collapsed": false
- },
- "outputs": [
- {
- "data": {
- "text/plain": [
- ""
- ]
- },
- "execution_count": 17,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "run.publish()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "More to come soon..."
- ]
- }
- ],
- "metadata": {
- "colabVersion": "0.1",
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3.0
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.4.3"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 0
-}
diff --git a/examples/PyOpenML.ipynb b/examples/PyOpenML.ipynb
deleted file mode 100644
index 2012705d7..000000000
--- a/examples/PyOpenML.ipynb
+++ /dev/null
@@ -1,862 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {
- "slideshow": {
- "slide_type": "slide"
- }
- },
- "source": [
- "# OpenML in Python \n",
- "Joaquin Vanschoren @joavanschoren"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "slideshow": {
- "slide_type": "slide"
- }
- },
- "source": [
- "## How to...\n",
- "- Download datasets and tasks in Python and Jupyter\n",
- "- Use scikit-learn to build classifiers\n",
- "- Upload the results to the server\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "slideshow": {
- "slide_type": "slide"
- }
- },
- "source": [
- "# Setup"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Installation\n",
- "Via source"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {
- "collapsed": true
- },
- "outputs": [],
- "source": [
- "#! git clone https://github.com/openml/openml-python.\n",
- "#! cd openml-python\n",
- "#! python setup.py install"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "collapsed": true,
- "slideshow": {
- "slide_type": "subslide"
- }
- },
- "source": [
- "## Authentication\n",
- "* Create an OpenML account on http://www.openml.org\n",
- "* Go to your OpenML account, click 'Account Settings', and then 'API authentication'. \n",
- " \n",
- "* Create a little file to store the API key locally: ~/.openml/config"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "collapsed": true
- },
- "outputs": [],
- "source": [
- "apikey=FILL_IN_API_KEY\n",
- "cachedir=FILL_IN_CACHE_DIR"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "slideshow": {
- "slide_type": "subslide"
- }
- },
- "source": [
- "## Alternative authentication"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {
- "collapsed": false
- },
- "outputs": [],
- "source": [
- "import openml\n",
- "\n",
- "# assumes you have your api key in ~/.openml/config\n",
- "# amueller's read/write key that he will throw away later\n",
- "openml.config.apikey='610344db6388d9ba34f6db45a3cf71de'\n",
- "# we also want to use the test server so as not to polute the production system\n",
- "openml.config.server = \"http://www.openml.org/api/v1/xml\""
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "slideshow": {
- "slide_type": "slide"
- }
- },
- "source": [
- "# Data"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "slideshow": {
- "slide_type": "subslide"
- }
- },
- "source": [
- "## List ALL the datasets"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 12,
- "metadata": {
- "collapsed": false
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "First 10 of 19538 datasets...\n",
- " did name NumberOfInstances NumberOfFeatures\n",
- "0 1 anneal 898 39\n",
- "1 2 anneal 898 39\n",
- "2 3 kr-vs-kp 3196 37\n",
- "3 4 labor 57 17\n",
- "4 5 arrhythmia 452 280\n",
- "5 6 letter 20000 17\n",
- "6 7 audiology 226 70\n",
- "7 8 liver-disorders 345 7\n",
- "8 9 autos 205 26\n",
- "9 10 lymph 148 19\n"
- ]
- }
- ],
- "source": [
- "from openml import datasets, tasks, runs\n",
- "import numpy as np\n",
- "import pandas as pd\n",
- "import os\n",
- "\n",
- "datalist = datasets.list_datasets()\n",
- "\n",
- "data = pd.DataFrame(datalist)\n",
- "print(\"First 10 of %s datasets...\" % len(datalist))\n",
- "print(data[:10][['did','name','NumberOfInstances','NumberOfFeatures']])"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "slideshow": {
- "slide_type": "subslide"
- }
- },
- "source": [
- "Subset based on any property"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {
- "collapsed": false
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "First 10 of 605 datasets...\n",
- " did name NumberOfInstances NumberOfFeatures\n",
- "2 3 kr-vs-kp 3196 37\n",
- "3 4 labor 57 17\n",
- "12 13 breast-cancer 286 10\n",
- "14 15 breast-w 699 10\n",
- "23 24 mushroom 8124 23\n",
- "24 25 colic 368 28\n",
- "26 27 colic 368 23\n",
- "28 29 credit-a 690 16\n",
- "30 31 credit-g 1000 21\n",
- "32 33 cylinder-bands 540 40\n"
- ]
- }
- ],
- "source": [
- "bin_data = data.loc[data['NumberOfClasses'] == 2]\n",
- "print(\"First 10 of %s datasets...\" % len(bin_data))\n",
- "print(bin_data[:10][['did','name', 'NumberOfInstances','NumberOfFeatures']])"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "slideshow": {
- "slide_type": "subslide"
- }
- },
- "source": [
- "Subset based on any property"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 13,
- "metadata": {
- "collapsed": false
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "First 10 of 605 datasets...\n",
- " did name NumberOfInstances\n",
- "1305 1588 w8a 64700\n",
- "2424 4533 KEGGMetabolicReactionNetwork 65554\n",
- "1308 1591 connect-4 67557\n",
- "423 554 mnist_784 70000\n",
- "1296 1578 real-sim 72309\n",
- "1062 1213 BNG(mv) 78732\n",
- "19509 23396 COMET_MC_SAMPLE 89640\n",
- "19508 23395 COMET_MC_SAMPLE 89640\n",
- "19537 23512 higgs 98050\n",
- "2423 4532 higgs 98050\n"
- ]
- }
- ],
- "source": [
- "big_data = data.loc[data['NumberOfInstances'] > 60000]\n",
- "big_data = big_data.sort_values(by='NumberOfInstances', ascending=True)\n",
- "print(\"First 10 of %s datasets...\" % len(bin_data))\n",
- "print(big_data[:10][['did','name', 'NumberOfInstances']])"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "slideshow": {
- "slide_type": "subslide"
- }
- },
- "source": [
- "Download a specific dataset. This is done based on the dataset ID (called 'did' in the table above)."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 10,
- "metadata": {
- "collapsed": false
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "This is dataset 'higgs', the target feature is called 'class'\n",
- "URL: http://www.openml.org/data/download/2063675/phpZLgL9q\n",
- "**Author**: Daniel Whiteson daniel'@'uci.edu\", Assistant Professor, Physics, Univ. of California Irvine \n",
- "**Source**: [UCI](https://archive.ics.uci.edu/ml/datasets/HIGGS) \n",
- "**Please cite**: Baldi, P., P. Sadowski, and D. Whiteson. Searching for Exotic Particles in High-energy Physics with Deep Learning. Nature Communications 5 (July 2, 2014). \n",
- "\n",
- "**Note: This is the UCI Higgs dataset, same as version 1, but it fixes the definition of the class attribute, which is categorical, not numeric.**\n",
- "\n",
- "Data\n"
- ]
- }
- ],
- "source": [
- "dataset = openml.datasets.get_dataset(23512)\n",
- "\n",
- "print(\"This is dataset '%s', the target feature is called '%s'\" % (dataset.name, dataset.default_target_attribute))\n",
- "print(\"URL: %s\" % dataset.url)\n",
- "print(dataset.description[:500])"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 14,
- "metadata": {
- "collapsed": false,
- "slideshow": {
- "slide_type": "subslide"
- }
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "{'citation': None,\n",
- " 'collection_date': None,\n",
- " 'contributor': None,\n",
- " 'creator': None,\n",
- " 'data_file': '/Users/joa/.openml/cache/datasets/23512/dataset.arff',\n",
- " 'data_pickle_file': '/Users/joa/.openml/cache/datasets/23512/dataset.pkl',\n",
- " 'default_target_attribute': 'class',\n",
- " 'description': '**Author**: Daniel Whiteson daniel\\'@\\'uci.edu\", Assistant '\n",
- " 'Professor, Physics, Univ. of California Irvine \\n'\n",
- " '**Source**: '\n",
- " '[UCI](https://archive.ics.uci.edu/ml/datasets/HIGGS) \\n'\n",
- " '**Please cite**: Baldi, P., P. Sadowski, and D. Whiteson. '\n",
- " 'Searching for Exotic Particles in High-energy Physics with '\n",
- " 'Deep Learning. Nature Communications 5 (July 2, 2014). \\n'\n",
- " '\\n'\n",
- " '**Note: This is the UCI Higgs dataset, same as version 1, but '\n",
- " 'it fixes the definition of the class attribute, which is '\n",
- " 'categorical, not numeric.**\\n'\n",
- " '\\n'\n",
- " 'Data Set Information:\\n'\n",
- " '\\n'\n",
- " 'The data has been produced using Monte Carlo simulations. The '\n",
- " 'first 21 features (columns 2-22) are kinematic properties '\n",
- " 'measured by the particle detectors in the accelerator. The '\n",
- " 'last seven features are functions of the first 21 features; '\n",
- " 'these are high-level features derived by physicists to help '\n",
- " 'discriminate between the two classes. There is an interest in '\n",
- " 'using deep learning methods to obviate the need for '\n",
- " 'physicists to manually develop such features. Benchmark '\n",
- " 'results using Bayesian Decision Trees from a standard physics '\n",
- " 'package and 5-layer neural networks are presented in the '\n",
- " 'original paper. The last 500,000 examples are used as a test '\n",
- " 'set.\\n'\n",
- " '\\n'\n",
- " '\\n'\n",
- " 'Attribute Information:\\n'\n",
- " '\\n'\n",
- " 'The first column is the class label (1 for signal, 0 for '\n",
- " 'background), followed by the 28 features (21 low-level '\n",
- " 'features then 7 high-level features): lepton pT, lepton eta, '\n",
- " 'lepton phi, missing energy magnitude, missing energy phi, jet '\n",
- " '1 pt, jet 1 eta, jet 1 phi, jet 1 b-tag, jet 2 pt, jet 2 eta, '\n",
- " 'jet 2 phi, jet 2 b-tag, jet 3 pt, jet 3 eta, jet 3 phi, jet 3 '\n",
- " 'b-tag, jet 4 pt, jet 4 eta, jet 4 phi, jet 4 b-tag, m_jj, '\n",
- " 'm_jjj, m_lv, m_jlv, m_bb, m_wbb, m_wwbb. For more detailed '\n",
- " 'information about each feature see the original paper.\\n'\n",
- " '\\n'\n",
- " '\\n'\n",
- " 'Relevant Papers:\\n'\n",
- " '\\n'\n",
- " 'Baldi, P., P. Sadowski, and D. Whiteson. “Searching for '\n",
- " 'Exotic Particles in High-energy Physics with Deep Learning. '\n",
- " 'Nature Communications 5 (July 2, 2014).',\n",
- " 'format': 'ARFF',\n",
- " 'id': 23512,\n",
- " 'ignore_attributes': None,\n",
- " 'language': None,\n",
- " 'licence': 'Public',\n",
- " 'md5_cheksum': '1a216f3bb3f6a961b4023179fca78452',\n",
- " 'name': 'higgs',\n",
- " 'original_data_url': None,\n",
- " 'paper_url': None,\n",
- " 'row_id_attribute': None,\n",
- " 'tag': None,\n",
- " 'update_comment': None,\n",
- " 'upload_date': '2016-06-21 15:03:06',\n",
- " 'url': 'http://www.openml.org/data/download/2063675/phpZLgL9q',\n",
- " 'version': 2,\n",
- " 'version_label': None,\n",
- " 'visibility': 'public'}\n"
- ]
- }
- ],
- "source": [
- "from pprint import pprint\n",
- "pprint(vars(dataset))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "slideshow": {
- "slide_type": "subslide"
- }
- },
- "source": [
- "Get the actual data"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 15,
- "metadata": {
- "collapsed": false
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- " lepton_pT lepton_eta lepton_phi missing_energy_magnitude \\\n",
- "0 0.907542 0.329147 0.359412 1.497970 \n",
- "1 0.798835 1.470639 -1.635975 0.453773 \n",
- "2 1.344385 -0.876626 0.935913 1.992050 \n",
- "3 1.105009 0.321356 1.522401 0.882808 \n",
- "4 1.595839 -0.607811 0.007075 1.818450 \n",
- "5 0.409391 -1.884684 -1.027292 1.672452 \n",
- "6 0.933895 0.629130 0.527535 0.238033 \n",
- "7 1.405144 0.536603 0.689554 1.179567 \n",
- "8 1.176566 0.104161 1.397002 0.479721 \n",
- "9 0.945974 1.111244 1.218337 0.907639 \n",
- "\n",
- " missing_energy_phi jet1pt jet1eta jet1phi jet1b-tag jet2pt \\\n",
- "0 -0.313010 1.095531 -0.557525 -1.588230 2.173076 0.812581 \n",
- "1 0.425629 1.104875 1.282322 1.381664 0.000000 0.851737 \n",
- "2 0.882454 1.786066 -1.646778 -0.942383 0.000000 2.423265 \n",
- "3 -1.205349 0.681466 -1.070464 -0.921871 0.000000 0.800872 \n",
- "4 -0.111906 0.847550 -0.566437 1.581239 2.173076 0.755421 \n",
- "5 -1.604598 1.338015 0.055427 0.013466 2.173076 0.509783 \n",
- "6 -0.966569 0.547811 -0.059439 -1.706866 2.173076 0.941003 \n",
- "7 -0.110061 3.202405 -1.526960 -1.576033 0.000000 2.931537 \n",
- "8 0.265513 1.135563 1.534831 -0.253291 0.000000 1.027247 \n",
- "9 0.821537 1.153243 -0.365420 -1.566055 0.000000 0.744719 \n",
- "\n",
- " ... jet4phi jet4b-tag m_jj m_jjj m_lv m_jlv \\\n",
- "0 ... -0.000819 0.000000 0.302220 0.833048 0.985700 0.978098 \n",
- "1 ... 0.900461 0.000000 0.909753 1.108330 0.985692 0.951331 \n",
- "2 ... -1.360356 0.000000 0.946652 1.028704 0.998656 0.728281 \n",
- "3 ... 0.113041 0.000000 0.755856 1.361057 0.986610 0.838085 \n",
- "4 ... -1.274345 3.101961 0.823761 0.938191 0.971758 0.789176 \n",
- "5 ... 1.377130 3.101961 0.869418 1.222083 1.000627 0.545045 \n",
- "6 ... -1.467454 0.000000 0.901837 1.083671 0.979696 0.783300 \n",
- "7 ... 1.163489 0.000000 1.667071 4.039273 1.175828 1.045352 \n",
- "8 ... 0.530334 0.000000 0.833175 0.773968 0.985750 1.103696 \n",
- "9 ... 0.063653 3.101961 0.829024 0.980648 0.994360 0.908248 \n",
- "\n",
- " m_bb m_wbb m_wwbb class \n",
- "0 0.779732 0.992356 0.798343 1 \n",
- "1 0.803252 0.865924 0.780118 1 \n",
- "2 0.869200 1.026736 0.957904 0 \n",
- "3 1.133295 0.872245 0.808487 1 \n",
- "4 0.430553 0.961357 0.957818 0 \n",
- "5 0.698653 0.977314 0.828786 1 \n",
- "6 0.849195 0.894356 0.774879 1 \n",
- "7 1.542972 3.534827 2.740754 1 \n",
- "8 0.849140 0.937104 0.812364 1 \n",
- "9 0.775879 0.783311 0.725122 1 \n",
- "\n",
- "[10 rows x 29 columns]\n"
- ]
- }
- ],
- "source": [
- "X, y, attribute_names = dataset.get_data(target=dataset.default_target_attribute, return_attribute_names=True)\n",
- "iris = pd.DataFrame(X, columns=attribute_names)\n",
- "iris['class'] = y\n",
- "print(iris[:10])"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "slideshow": {
- "slide_type": "subslide"
- }
- },
- "source": [
- "Train a scikit-learn model on the data"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 21,
- "metadata": {
- "collapsed": false
- },
- "outputs": [
- {
- "data": {
- "text/plain": [
- "RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',\n",
- " max_depth=None, max_features='auto', max_leaf_nodes=None,\n",
- " min_samples_leaf=1, min_samples_split=2,\n",
- " min_weight_fraction_leaf=0.0, n_estimators=10, n_jobs=1,\n",
- " oob_score=False, random_state=None, verbose=0,\n",
- " warm_start=False)"
- ]
- },
- "execution_count": 21,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "from sklearn import preprocessing, ensemble\n",
- "\n",
- "dataset = datasets.get_dataset(61)\n",
- "X, y = dataset.get_data(target=dataset.default_target_attribute)\n",
- "clf = ensemble.RandomForestClassifier()\n",
- "clf.fit(X, y)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "slideshow": {
- "slide_type": "subslide"
- }
- },
- "source": [
- "You can also ask which features are categorical to do your own encoding"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 20,
- "metadata": {
- "collapsed": false
- },
- "outputs": [
- {
- "data": {
- "text/plain": [
- "RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',\n",
- " max_depth=None, max_features='auto', max_leaf_nodes=None,\n",
- " min_samples_leaf=1, min_samples_split=2,\n",
- " min_weight_fraction_leaf=0.0, n_estimators=10, n_jobs=1,\n",
- " oob_score=False, random_state=None, verbose=0,\n",
- " warm_start=False)"
- ]
- },
- "execution_count": 20,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "X, y, categorical = dataset.get_data(target=dataset.default_target_attribute,return_categorical_indicator=True)\n",
- "enc = preprocessing.OneHotEncoder(categorical_features=categorical)\n",
- "X = enc.fit_transform(X)\n",
- "clf.fit(X, y)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "slideshow": {
- "slide_type": "slide"
- }
- },
- "source": [
- "# Tasks"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "To run benchmarks consistently (also across studies and tools), OpenML offers Tasks, which include specific train-test splits and other information."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "slideshow": {
- "slide_type": "subslide"
- }
- },
- "source": [
- "## List ALL the tasks"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 22,
- "metadata": {
- "collapsed": false
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "First 5 of 46562 tasks:\n",
- " tid did name task_type estimation_procedure\n",
- "0 1 1 anneal Supervised Classification 10-fold Crossvalidation\n",
- "1 2 2 anneal Supervised Classification 10-fold Crossvalidation\n",
- "2 3 3 kr-vs-kp Supervised Classification 10-fold Crossvalidation\n",
- "3 4 4 labor Supervised Classification 10-fold Crossvalidation\n",
- "4 5 5 arrhythmia Supervised Classification 10-fold Crossvalidation\n"
- ]
- }
- ],
- "source": [
- "task_list = tasks.list_tasks()\n",
- "\n",
- "tasks = pd.DataFrame(task_list)\n",
- "print(\"First 5 of %s tasks:\" % len(tasks))\n",
- "print(tasks[:5][['tid','did','name','task_type','estimation_procedure']])"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "slideshow": {
- "slide_type": "subslide"
- }
- },
- "source": [
- "## Download tasks"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 15,
- "metadata": {
- "collapsed": false
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "OpenMLTask instance.\n",
- "Task ID: 10\n",
- "Task type: Supervised Classification\n",
- "Dataset id: 10\n"
- ]
- }
- ],
- "source": [
- "task = tasks.get_task(10)\n",
- "print(task)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "slideshow": {
- "slide_type": "slide"
- }
- },
- "source": [
- "# Runs"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "slideshow": {
- "slide_type": "-"
- }
- },
- "source": [
- "Run a scikit-learn classifier on the task (using the right splits)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 16,
- "metadata": {
- "collapsed": false,
- "slideshow": {
- "slide_type": "subslide"
- }
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "2823\n",
- "RandomForest has run on the task.\n"
- ]
- }
- ],
- "source": [
- "from openml.runs import run_task\n",
- "\n",
- "clf = ensemble.RandomForestClassifier()\n",
- "run = run_task(task, clf)\n",
- "print(\"RandomForest has run on the task.\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "slideshow": {
- "slide_type": "subslide"
- }
- },
- "source": [
- "Upload the run to the OpenML server"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 17,
- "metadata": {
- "collapsed": false
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Uploaded run with id 538241\n",
- "Check it at www.openml.org/r/538241\n"
- ]
- }
- ],
- "source": [
- "import xmltodict\n",
- "\n",
- "return_code, response = run.publish()\n",
- "\n",
- "if(return_code == 200):\n",
- " response_dict = xmltodict.parse(response)\n",
- " run_id = response_dict['oml:upload_run']['oml:run_id']\n",
- " print(\"Uploaded run with id %s\" % (run_id))\n",
- " print(\"Check it at www.openml.org/r/%s\" % (run_id))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "slideshow": {
- "slide_type": "slide"
- }
- },
- "source": [
- "## TL;DR;\n",
- "You can easily run and share scikit-learn experiments on OpenML"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 24,
- "metadata": {
- "collapsed": false,
- "scrolled": true
- },
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "4074\n",
- "Uploaded run with id 595118. Check it at www.openml.org/r/595118\n"
- ]
- }
- ],
- "source": [
- "from sklearn import tree\n",
- "from openml import tasks,runs\n",
- "task = tasks.get_task(14951)\n",
- "clf = tree.DecisionTreeClassifier()\n",
- "run = runs.run_task(task, clf)\n",
- "return_code, response = run.publish()\n",
- "\n",
- "# get the run id for reference\n",
- "import xmltodict\n",
- "if(return_code == 200):\n",
- " response_dict = xmltodict.parse(response)\n",
- " run_id = response_dict['oml:upload_run']['oml:run_id']\n",
- " print(\"Uploaded run with id %s. Check it at www.openml.org/r/%s\" % (run_id,run_id))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "slideshow": {
- "slide_type": "subslide"
- }
- },
- "source": [
- "## Challenge: Build the 'best' model on the Higgs dataset together\n",
- "* Check progress on: http://www.openml.org/t/52950"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "collapsed": false
- },
- "outputs": [],
- "source": [
- "from openml import tasks,runs\n",
- "from sklearn.pipeline import Pipeline\n",
- "from sklearn.neighbors import KNeighborsClassifier\n",
- "from sklearn.preprocessing import Imputer\n",
- "task = tasks.get_task(52950)\n",
- "estimator = Pipeline([(\"imputer\", Imputer(missing_values=0,\n",
- " strategy=\"mean\",\n",
- " axis=0)),\n",
- " (\"knn\", KNeighborsClassifier())])\n",
- "run = runs.run_task(task, estimator)\n",
- "return_code, response = run.publish()\n",
- "\n",
- "# get the run id for reference\n",
- "import xmltodict\n",
- "if(return_code == 200):\n",
- " response_dict = xmltodict.parse(response)\n",
- " run_id = response_dict['oml:upload_run']['oml:run_id']\n",
- " print(\"Uploaded run with id %s. Check it at www.openml.org/r/%s\" % (run_id,run_id))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "collapsed": true
- },
- "outputs": [],
- "source": []
- }
- ],
- "metadata": {
- "celltoolbar": "Slideshow",
- "colabVersion": "0.1",
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.5.0"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 0
-}
diff --git a/examples/_external_or_deprecated/2015_neurips_feurer_example.py b/examples/_external_or_deprecated/2015_neurips_feurer_example.py
new file mode 100644
index 000000000..2dfc4bb97
--- /dev/null
+++ b/examples/_external_or_deprecated/2015_neurips_feurer_example.py
@@ -0,0 +1,91 @@
+"""
+Feurer et al. (2015)
+====================
+
+A tutorial on how to get the datasets used in the paper introducing *Auto-sklearn* by Feurer et al..
+
+Auto-sklearn website: https://automl.github.io/auto-sklearn/
+
+Publication
+~~~~~~~~~~~
+
+| Efficient and Robust Automated Machine Learning
+| Matthias Feurer, Aaron Klein, Katharina Eggensperger, Jost Springenberg, Manuel Blum and Frank Hutter
+| In *Advances in Neural Information Processing Systems 28*, 2015
+| Available at https://papers.nips.cc/paper/5872-efficient-and-robust-automated-machine-learning.pdf
+"""
+
+# License: BSD 3-Clause
+
+import openml
+
+####################################################################################################
+# List of dataset IDs given in the supplementary material of Feurer et al.:
+# https://papers.nips.cc/paper/5872-efficient-and-robust-automated-machine-learning-supplemental.zip
+# fmt: off
+dataset_ids = [
+ 3, 6, 12, 14, 16, 18, 21, 22, 23, 24, 26, 28, 30, 31, 32, 36, 38, 44, 46,
+ 57, 60, 179, 180, 181, 182, 184, 185, 273, 293, 300, 351, 354, 357, 389,
+ 390, 391, 392, 393, 395, 396, 398, 399, 401, 554, 679, 715, 718, 720, 722,
+ 723, 727, 728, 734, 735, 737, 740, 741, 743, 751, 752, 761, 772, 797, 799,
+ 803, 806, 807, 813, 816, 819, 821, 822, 823, 833, 837, 843, 845, 846, 847,
+ 849, 866, 871, 881, 897, 901, 903, 904, 910, 912, 913, 914, 917, 923, 930,
+ 934, 953, 958, 959, 962, 966, 971, 976, 977, 978, 979, 980, 991, 993, 995,
+ 1000, 1002, 1018, 1019, 1020, 1021, 1036, 1040, 1041, 1049, 1050, 1053,
+ 1056, 1067, 1068, 1069, 1111, 1112, 1114, 1116, 1119, 1120, 1128, 1130,
+ 1134, 1138, 1139, 1142, 1146, 1161, 1166,
+]
+# fmt: on
+
+####################################################################################################
+# The dataset IDs could be used directly to load the dataset and split the data into a training set
+# and a test set. However, to be reproducible, we will first obtain the respective tasks from
+# OpenML, which define both the target feature and the train/test split.
+#
+# .. note::
+# It is discouraged to work directly on datasets and only provide dataset IDs in a paper as
+# this does not allow reproducibility (unclear splitting). Please do not use datasets but the
+# respective tasks as basis for a paper and publish task IDS. This example is only given to
+# showcase the use of OpenML-Python for a published paper and as a warning on how not to do it.
+# Please check the `OpenML documentation of tasks `_ if you
+# want to learn more about them.
+
+####################################################################################################
+# This lists both active and inactive tasks (because of ``status='all'``). Unfortunately,
+# this is necessary as some of the datasets contain issues found after the publication and became
+# deactivated, which also deactivated the tasks on them. More information on active or inactive
+# datasets can be found in the `online docs `_.
+tasks = openml.tasks.list_tasks(
+ task_type=openml.tasks.TaskType.SUPERVISED_CLASSIFICATION,
+ status="all",
+ output_format="dataframe",
+)
+
+# Query only those with holdout as the resampling startegy.
+tasks = tasks.query('estimation_procedure == "33% Holdout set"')
+
+task_ids = []
+for did in dataset_ids:
+ tasks_ = list(tasks.query(f"did == {did}").tid)
+ if len(tasks_) >= 1: # if there are multiple task, take the one with lowest ID (oldest).
+ task_id = min(tasks_)
+ else:
+ raise ValueError(did)
+
+ # Optional - Check that the task has the same target attribute as the
+ # dataset default target attribute
+ # (disabled for this example as it needs to run fast to be rendered online)
+ # task = openml.tasks.get_task(task_id)
+ # dataset = task.get_dataset()
+ # if task.target_name != dataset.default_target_attribute:
+ # raise ValueError(
+ # (task.target_name, dataset.default_target_attribute)
+ # )
+
+ task_ids.append(task_id)
+
+assert len(task_ids) == 140
+task_ids.sort()
+
+# These are the tasks to work with:
+print(task_ids)
diff --git a/examples/_external_or_deprecated/2018_ida_strang_example.py b/examples/_external_or_deprecated/2018_ida_strang_example.py
new file mode 100644
index 000000000..0e180badf
--- /dev/null
+++ b/examples/_external_or_deprecated/2018_ida_strang_example.py
@@ -0,0 +1,123 @@
+"""
+Strang et al. (2018)
+====================
+
+A tutorial on how to reproduce the analysis conducted for *Don't Rule Out Simple Models
+Prematurely: A Large Scale Benchmark Comparing Linear and Non-linear Classifiers in OpenML*.
+
+Publication
+~~~~~~~~~~~
+
+| Don't Rule Out Simple Models Prematurely: A Large Scale Benchmark Comparing Linear and Non-linear Classifiers in OpenML
+| Benjamin Strang, Peter van der Putten, Jan N. van Rijn and Frank Hutter
+| In *Advances in Intelligent Data Analysis XVII 17th International Symposium*, 2018
+| Available at https://link.springer.com/chapter/10.1007%2F978-3-030-01768-2_25
+"""
+
+# License: BSD 3-Clause
+
+import matplotlib.pyplot as plt
+
+import openml
+
+##############################################################################
+# A basic step for each data-mining or machine learning task is to determine
+# which model to choose based on the problem and the data at hand. In this
+# work we investigate when non-linear classifiers outperform linear
+# classifiers by means of a large scale experiment.
+#
+# The paper is accompanied with a study object, containing all relevant tasks
+# and runs (``study_id=123``). The paper features three experiment classes:
+# Support Vector Machines (SVM), Neural Networks (NN) and Decision Trees (DT).
+# This example demonstrates how to reproduce the plots, comparing two
+# classifiers given the OpenML flow ids. Note that this allows us to reproduce
+# the SVM and NN experiment, but not the DT experiment, as this requires a bit
+# more effort to distinguish the same flow with different hyperparameter
+# values.
+
+study_id = 123
+# for comparing svms: flow_ids = [7754, 7756]
+# for comparing nns: flow_ids = [7722, 7729]
+# for comparing dts: flow_ids = [7725], differentiate on hyper-parameter value
+classifier_family = "SVM"
+flow_ids = [7754, 7756]
+measure = "predictive_accuracy"
+meta_features = ["NumberOfInstances", "NumberOfFeatures"]
+class_values = ["non-linear better", "linear better", "equal"]
+
+# Downloads all evaluation records related to this study
+evaluations = openml.evaluations.list_evaluations(
+ measure, size=None, flows=flow_ids, study=study_id, output_format="dataframe"
+)
+# gives us a table with columns data_id, flow1_value, flow2_value
+evaluations = evaluations.pivot(index="data_id", columns="flow_id", values="value").dropna()
+# downloads all data qualities (for scatter plot)
+data_qualities = openml.datasets.list_datasets(
+ data_id=list(evaluations.index.values), output_format="dataframe"
+)
+# removes irrelevant data qualities
+data_qualities = data_qualities[meta_features]
+# makes a join between evaluation table and data qualities table,
+# now we have columns data_id, flow1_value, flow2_value, meta_feature_1,
+# meta_feature_2
+evaluations = evaluations.join(data_qualities, how="inner")
+
+# adds column that indicates the difference between the two classifiers
+evaluations["diff"] = evaluations[flow_ids[0]] - evaluations[flow_ids[1]]
+
+
+##############################################################################
+# makes the s-plot
+
+fig_splot, ax_splot = plt.subplots()
+ax_splot.plot(range(len(evaluations)), sorted(evaluations["diff"]))
+ax_splot.set_title(classifier_family)
+ax_splot.set_xlabel("Dataset (sorted)")
+ax_splot.set_ylabel("difference between linear and non-linear classifier")
+ax_splot.grid(linestyle="--", axis="y")
+plt.show()
+
+
+##############################################################################
+# adds column that indicates the difference between the two classifiers,
+# needed for the scatter plot
+
+
+def determine_class(val_lin, val_nonlin):
+ if val_lin < val_nonlin:
+ return class_values[0]
+ if val_nonlin < val_lin:
+ return class_values[1]
+ return class_values[2]
+
+
+evaluations["class"] = evaluations.apply(
+ lambda row: determine_class(row[flow_ids[0]], row[flow_ids[1]]), axis=1
+)
+
+# does the plotting and formatting
+fig_scatter, ax_scatter = plt.subplots()
+for class_val in class_values:
+ df_class = evaluations[evaluations["class"] == class_val]
+ plt.scatter(df_class[meta_features[0]], df_class[meta_features[1]], label=class_val)
+ax_scatter.set_title(classifier_family)
+ax_scatter.set_xlabel(meta_features[0])
+ax_scatter.set_ylabel(meta_features[1])
+ax_scatter.legend()
+ax_scatter.set_xscale("log")
+ax_scatter.set_yscale("log")
+plt.show()
+
+##############################################################################
+# makes a scatter plot where each data point represents the performance of the
+# two algorithms on various axis (not in the paper)
+
+fig_diagplot, ax_diagplot = plt.subplots()
+ax_diagplot.grid(linestyle="--")
+ax_diagplot.plot([0, 1], ls="-", color="black")
+ax_diagplot.plot([0.2, 1.2], ls="--", color="black")
+ax_diagplot.plot([-0.2, 0.8], ls="--", color="black")
+ax_diagplot.scatter(evaluations[flow_ids[0]], evaluations[flow_ids[1]])
+ax_diagplot.set_xlabel(measure)
+ax_diagplot.set_ylabel(measure)
+plt.show()
diff --git a/examples/_external_or_deprecated/2018_kdd_rijn_example.py b/examples/_external_or_deprecated/2018_kdd_rijn_example.py
new file mode 100644
index 000000000..957281616
--- /dev/null
+++ b/examples/_external_or_deprecated/2018_kdd_rijn_example.py
@@ -0,0 +1,189 @@
+"""
+van Rijn and Hutter (2018)
+==========================
+
+A tutorial on how to reproduce the paper *Hyperparameter Importance Across Datasets*.
+
+Example Deprecation Warning!
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This example is not supported anymore by the OpenML-Python developers. The example is kept for reference purposes but not tested anymore.
+
+Publication
+~~~~~~~~~~~
+
+| Hyperparameter importance across datasets
+| Jan N. van Rijn and Frank Hutter
+| In *Proceedings of the 24th ACM SIGKDD International Conference on Knowledge Discovery & Data Mining*, 2018
+| Available at https://dl.acm.org/doi/10.1145/3219819.3220058
+
+Requirements
+~~~~~~~~~~~~
+
+This is a Unix-only tutorial, as the requirements can not be satisfied on a Windows machine (Untested on other
+systems).
+
+The following Python packages are required:
+
+pip install openml[examples,docs] fanova ConfigSpace<1.0
+"""
+
+# License: BSD 3-Clause
+
+import sys
+
+if sys.platform == "win32":
+ print(
+ "The pyrfr library (requirement of fanova) can currently not be installed on Windows systems"
+ )
+ sys.exit()
+
+# DEPRECATED EXAMPLE -- Avoid running this code in our CI/CD pipeline
+print("This example is deprecated, remove the `if False` in this code to use it manually.")
+if False:
+ import json
+
+ import fanova
+ import matplotlib.pyplot as plt
+ import pandas as pd
+ import seaborn as sns
+
+ import openml
+
+ ##############################################################################
+ # With the advent of automated machine learning, automated hyperparameter
+ # optimization methods are by now routinely used in data mining. However, this
+ # progress is not yet matched by equal progress on automatic analyses that
+ # yield information beyond performance-optimizing hyperparameter settings.
+ # In this example, we aim to answer the following two questions: Given an
+ # algorithm, what are generally its most important hyperparameters?
+ #
+ # This work is carried out on the OpenML-100 benchmark suite, which can be
+ # obtained by ``openml.study.get_suite('OpenML100')``. In this example, we
+ # conduct the experiment on the Support Vector Machine (``flow_id=7707``)
+ # with specific kernel (we will perform a post-process filter operation for
+ # this). We should set some other experimental parameters (number of results
+ # per task, evaluation measure and the number of trees of the internal
+ # functional Anova) before the fun can begin.
+ #
+ # Note that we simplify the example in several ways:
+ #
+ # 1) We only consider numerical hyperparameters
+ # 2) We consider all hyperparameters that are numerical (in reality, some
+ # hyperparameters might be inactive (e.g., ``degree``) or irrelevant
+ # (e.g., ``random_state``)
+ # 3) We assume all hyperparameters to be on uniform scale
+ #
+ # Any difference in conclusion between the actual paper and the presented
+ # results is most likely due to one of these simplifications. For example,
+ # the hyperparameter C looks rather insignificant, whereas it is quite
+ # important when it is put on a log-scale. All these simplifications can be
+ # addressed by defining a ConfigSpace. For a more elaborated example that uses
+ # this, please see:
+ # https://github.com/janvanrijn/openml-pimp/blob/d0a14f3eb480f2a90008889f00041bdccc7b9265/examples/plot/plot_fanova_aggregates.py
+
+ suite = openml.study.get_suite("OpenML100")
+ flow_id = 7707
+ parameter_filters = {"sklearn.svm.classes.SVC(17)_kernel": "sigmoid"}
+ evaluation_measure = "predictive_accuracy"
+ limit_per_task = 500
+ limit_nr_tasks = 15
+ n_trees = 16
+
+ fanova_results = []
+ # we will obtain all results from OpenML per task. Practice has shown that this places the bottleneck on the
+ # communication with OpenML, and for iterated experimenting it is better to cache the results in a local file.
+ for idx, task_id in enumerate(suite.tasks):
+ if limit_nr_tasks is not None and idx >= limit_nr_tasks:
+ continue
+ print(
+ f"Starting with task {task_id} ({idx + 1}/{len(suite.tasks) if limit_nr_tasks is None else limit_nr_tasks})"
+ )
+ # note that we explicitly only include tasks from the benchmark suite that was specified (as per the for-loop)
+ evals = openml.evaluations.list_evaluations_setups(
+ evaluation_measure,
+ flows=[flow_id],
+ tasks=[task_id],
+ size=limit_per_task,
+ output_format="dataframe",
+ )
+
+ performance_column = "value"
+ # make a DataFrame consisting of all hyperparameters (which is a dict in setup['parameters']) and the performance
+ # value (in setup['value']). The following line looks a bit complicated, but combines 2 tasks: a) combine
+ # hyperparameters and performance data in a single dict, b) cast hyperparameter values to the appropriate format
+ # Note that the ``json.loads(...)`` requires the content to be in JSON format, which is only the case for
+ # scikit-learn setups (and even there some legacy setups might violate this requirement). It will work for the
+ # setups that belong to the flows embedded in this example though.
+ try:
+ setups_evals = pd.DataFrame(
+ [
+ dict(
+ **{name: json.loads(value) for name, value in setup["parameters"].items()},
+ **{performance_column: setup[performance_column]},
+ )
+ for _, setup in evals.iterrows()
+ ]
+ )
+ except json.decoder.JSONDecodeError as e:
+ print(f"Task {task_id} error: {e}")
+ continue
+ # apply our filters, to have only the setups that comply to the hyperparameters we want
+ for filter_key, filter_value in parameter_filters.items():
+ setups_evals = setups_evals[setups_evals[filter_key] == filter_value]
+ # in this simplified example, we only display numerical and float hyperparameters. For categorical hyperparameters,
+ # the fanova library needs to be informed by using a configspace object.
+ setups_evals = setups_evals.select_dtypes(include=["int64", "float64"])
+ # drop rows with unique values. These are by definition not an interesting hyperparameter, e.g., ``axis``,
+ # ``verbose``.
+ setups_evals = setups_evals[
+ [
+ c
+ for c in list(setups_evals)
+ if len(setups_evals[c].unique()) > 1 or c == performance_column
+ ]
+ ]
+ # We are done with processing ``setups_evals``. Note that we still might have some irrelevant hyperparameters, e.g.,
+ # ``random_state``. We have dropped some relevant hyperparameters, i.e., several categoricals. Let's check it out:
+
+ # determine x values to pass to fanova library
+ parameter_names = [
+ pname for pname in setups_evals.columns.to_numpy() if pname != performance_column
+ ]
+ evaluator = fanova.fanova.fANOVA(
+ X=setups_evals[parameter_names].to_numpy(),
+ Y=setups_evals[performance_column].to_numpy(),
+ n_trees=n_trees,
+ )
+ for idx, pname in enumerate(parameter_names): # noqa: PLW2901
+ try:
+ fanova_results.append(
+ {
+ "hyperparameter": pname.split(".")[-1],
+ "fanova": evaluator.quantify_importance([idx])[(idx,)][
+ "individual importance"
+ ],
+ }
+ )
+ except RuntimeError as e:
+ # functional ANOVA sometimes crashes with a RuntimeError, e.g., on tasks where the performance is constant
+ # for all configurations (there is no variance). We will skip these tasks (like the authors did in the
+ # paper).
+ print(f"Task {task_id} error: {e}")
+ continue
+
+ # transform ``fanova_results`` from a list of dicts into a DataFrame
+ fanova_results = pd.DataFrame(fanova_results)
+
+ ##############################################################################
+ # make the boxplot of the variance contribution. Obviously, we can also use
+ # this data to make the Nemenyi plot, but this relies on the rather complex
+ # ``Orange`` dependency (``pip install Orange3``). For the complete example,
+ # the reader is referred to the more elaborate script (referred to earlier)
+ fig, ax = plt.subplots()
+ sns.boxplot(x="hyperparameter", y="fanova", data=fanova_results, ax=ax)
+ ax.set_xticklabels(ax.get_xticklabels(), rotation=45, ha="right")
+ ax.set_ylabel("Variance Contribution")
+ ax.set_xlabel(None)
+ plt.tight_layout()
+ plt.show()
diff --git a/examples/_external_or_deprecated/2018_neurips_perrone_example.py b/examples/_external_or_deprecated/2018_neurips_perrone_example.py
new file mode 100644
index 000000000..8a3c36994
--- /dev/null
+++ b/examples/_external_or_deprecated/2018_neurips_perrone_example.py
@@ -0,0 +1,260 @@
+"""
+Perrone et al. (2018)
+=====================
+
+A tutorial on how to build a surrogate model based on OpenML data as done for *Scalable
+Hyperparameter Transfer Learning* by Perrone et al..
+
+Publication
+~~~~~~~~~~~
+
+| Scalable Hyperparameter Transfer Learning
+| Valerio Perrone and Rodolphe Jenatton and Matthias Seeger and Cedric Archambeau
+| In *Advances in Neural Information Processing Systems 31*, 2018
+| Available at https://papers.nips.cc/paper/7917-scalable-hyperparameter-transfer-learning.pdf
+
+This example demonstrates how OpenML runs can be used to construct a surrogate model.
+
+In the following section, we shall do the following:
+
+* Retrieve tasks and flows as used in the experiments by Perrone et al. (2018).
+* Build a tabular data by fetching the evaluations uploaded to OpenML.
+* Impute missing values and handle categorical data before building a Random Forest model that
+ maps hyperparameter values to the area under curve score.
+"""
+
+############################################################################
+
+# License: BSD 3-Clause
+
+import numpy as np
+import pandas as pd
+from matplotlib import pyplot as plt
+from sklearn.compose import ColumnTransformer
+from sklearn.ensemble import RandomForestRegressor
+from sklearn.impute import SimpleImputer
+from sklearn.metrics import mean_squared_error
+from sklearn.pipeline import Pipeline
+from sklearn.preprocessing import OneHotEncoder
+
+import openml
+
+flow_type = "svm" # this example will use the smaller svm flow evaluations
+############################################################################
+# The subsequent functions are defined to fetch tasks, flows, evaluations and preprocess them into
+# a tabular format that can be used to build models.
+
+
+def fetch_evaluations(run_full=False, flow_type="svm", metric="area_under_roc_curve"): # noqa: FBT002
+ """
+ Fetch a list of evaluations based on the flows and tasks used in the experiments.
+
+ Parameters
+ ----------
+ run_full : boolean
+ If True, use the full list of tasks used in the paper
+ If False, use 5 tasks with the smallest number of evaluations available
+ flow_type : str, {'svm', 'xgboost'}
+ To select whether svm or xgboost experiments are to be run
+ metric : str
+ The evaluation measure that is passed to openml.evaluations.list_evaluations
+
+ Returns
+ -------
+ eval_df : dataframe
+ task_ids : list
+ flow_id : int
+ """
+ # Collecting task IDs as used by the experiments from the paper
+ # fmt: off
+ if flow_type == "svm" and run_full:
+ task_ids = [
+ 10101, 145878, 146064, 14951, 34537, 3485, 3492, 3493, 3494,
+ 37, 3889, 3891, 3899, 3902, 3903, 3913, 3918, 3950, 9889,
+ 9914, 9946, 9952, 9967, 9971, 9976, 9978, 9980, 9983,
+ ]
+ elif flow_type == "svm" and not run_full:
+ task_ids = [9983, 3485, 3902, 3903, 145878]
+ elif flow_type == "xgboost" and run_full:
+ task_ids = [
+ 10093, 10101, 125923, 145847, 145857, 145862, 145872, 145878,
+ 145953, 145972, 145976, 145979, 146064, 14951, 31, 3485,
+ 3492, 3493, 37, 3896, 3903, 3913, 3917, 3918, 3, 49, 9914,
+ 9946, 9952, 9967,
+ ]
+ else: # flow_type == 'xgboost' and not run_full:
+ task_ids = [3903, 37, 3485, 49, 3913]
+ # fmt: on
+
+ # Fetching the relevant flow
+ flow_id = 5891 if flow_type == "svm" else 6767
+
+ # Fetching evaluations
+ eval_df = openml.evaluations.list_evaluations_setups(
+ function=metric,
+ tasks=task_ids,
+ flows=[flow_id],
+ uploaders=[2702],
+ output_format="dataframe",
+ parameters_in_separate_columns=True,
+ )
+ return eval_df, task_ids, flow_id
+
+
+def create_table_from_evaluations(
+ eval_df,
+ flow_type="svm",
+ run_count=np.iinfo(np.int64).max, # noqa: B008
+ task_ids=None,
+):
+ """
+ Create a tabular data with its ground truth from a dataframe of evaluations.
+ Optionally, can filter out records based on task ids.
+
+ Parameters
+ ----------
+ eval_df : dataframe
+ Containing list of runs as obtained from list_evaluations()
+ flow_type : str, {'svm', 'xgboost'}
+ To select whether svm or xgboost experiments are to be run
+ run_count : int
+ Maximum size of the table created, or number of runs included in the table
+ task_ids : list, (optional)
+ List of integers specifying the tasks to be retained from the evaluations dataframe
+
+ Returns
+ -------
+ eval_table : dataframe
+ values : list
+ """
+ if task_ids is not None:
+ eval_df = eval_df[eval_df["task_id"].isin(task_ids)]
+ if flow_type == "svm":
+ colnames = ["cost", "degree", "gamma", "kernel"]
+ else:
+ colnames = [
+ "alpha",
+ "booster",
+ "colsample_bylevel",
+ "colsample_bytree",
+ "eta",
+ "lambda",
+ "max_depth",
+ "min_child_weight",
+ "nrounds",
+ "subsample",
+ ]
+ eval_df = eval_df.sample(frac=1) # shuffling rows
+ eval_df = eval_df.iloc[:run_count, :]
+ eval_df.columns = [column.split("_")[-1] for column in eval_df.columns]
+ eval_table = eval_df.loc[:, colnames]
+ value = eval_df.loc[:, "value"]
+ return eval_table, value
+
+
+def list_categorical_attributes(flow_type="svm"):
+ if flow_type == "svm":
+ return ["kernel"]
+ return ["booster"]
+
+
+#############################################################################
+# Fetching the data from OpenML
+# *****************************
+# Now, we read all the tasks and evaluations for them and collate into a table.
+# Here, we are reading all the tasks and evaluations for the SVM flow and
+# pre-processing all retrieved evaluations.
+
+eval_df, task_ids, flow_id = fetch_evaluations(run_full=False, flow_type=flow_type)
+X, y = create_table_from_evaluations(eval_df, flow_type=flow_type)
+print(X.head())
+print("Y : ", y[:5])
+
+#############################################################################
+# Creating pre-processing and modelling pipelines
+# ***********************************************
+# The two primary tasks are to impute the missing values, that is, account for the hyperparameters
+# that are not available with the runs from OpenML. And secondly, to handle categorical variables
+# using One-hot encoding prior to modelling.
+
+# Separating data into categorical and non-categorical (numeric for this example) columns
+cat_cols = list_categorical_attributes(flow_type=flow_type)
+num_cols = list(set(X.columns) - set(cat_cols))
+
+# Missing value imputers for numeric columns
+num_imputer = SimpleImputer(missing_values=np.nan, strategy="constant", fill_value=-1)
+
+# Creating the one-hot encoder for numerical representation of categorical columns
+enc = OneHotEncoder(handle_unknown="ignore")
+
+# Combining column transformers
+ct = ColumnTransformer([("cat", enc, cat_cols), ("num", num_imputer, num_cols)])
+
+# Creating the full pipeline with the surrogate model
+clf = RandomForestRegressor(n_estimators=50)
+model = Pipeline(steps=[("preprocess", ct), ("surrogate", clf)])
+
+
+#############################################################################
+# Building a surrogate model on a task's evaluation
+# *************************************************
+# The same set of functions can be used for a single task to retrieve a singular table which can
+# be used for the surrogate model construction. We shall use the SVM flow here to keep execution
+# time simple and quick.
+
+# Selecting a task for the surrogate
+task_id = task_ids[-1]
+print("Task ID : ", task_id)
+X, y = create_table_from_evaluations(eval_df, task_ids=[task_id], flow_type="svm")
+
+model.fit(X, y)
+y_pred = model.predict(X)
+
+print(f"Training RMSE : {mean_squared_error(y, y_pred):.5}")
+
+
+#############################################################################
+# Evaluating the surrogate model
+# ******************************
+# The surrogate model built from a task's evaluations fetched from OpenML will be put into
+# trivial action here, where we shall randomly sample configurations and observe the trajectory
+# of the area under curve (auc) we can obtain from the surrogate we've built.
+#
+# NOTE: This section is written exclusively for the SVM flow
+
+
+# Sampling random configurations
+def random_sample_configurations(num_samples=100):
+ colnames = ["cost", "degree", "gamma", "kernel"]
+ ranges = [
+ (0.000986, 998.492437),
+ (2.0, 5.0),
+ (0.000988, 913.373845),
+ (["linear", "polynomial", "radial", "sigmoid"]),
+ ]
+ X = pd.DataFrame(np.nan, index=range(num_samples), columns=colnames)
+ for i in range(len(colnames)):
+ if len(ranges[i]) == 2:
+ col_val = np.random.uniform(low=ranges[i][0], high=ranges[i][1], size=num_samples) # noqa: NPY002
+ else:
+ col_val = np.random.choice(ranges[i], size=num_samples) # noqa: NPY002
+ X.iloc[:, i] = col_val
+ return X
+
+
+configs = random_sample_configurations(num_samples=1000)
+print(configs)
+
+#############################################################################
+preds = model.predict(configs)
+
+# tracking the maximum AUC obtained over the functions evaluations
+preds = np.maximum.accumulate(preds)
+# computing regret (1 - predicted_auc)
+regret = 1 - preds
+
+# plotting the regret curve
+plt.plot(regret)
+plt.title("AUC regret for Random Search on surrogate")
+plt.xlabel("Numbe of function evaluations")
+plt.ylabel("Regret")
diff --git a/examples/_external_or_deprecated/README.md b/examples/_external_or_deprecated/README.md
new file mode 100644
index 000000000..d25a81baa
--- /dev/null
+++ b/examples/_external_or_deprecated/README.md
@@ -0,0 +1,5 @@
+# External or Deprecated Examples
+
+This directory contains examples that are either external or deprecated. They may not be maintained or updated
+regularly, and their functionality might not align with the latest version of the library. Moreover,
+they are not shown on the documentation website.
\ No newline at end of file
diff --git a/examples/_external_or_deprecated/benchmark_with_optunahub.py b/examples/_external_or_deprecated/benchmark_with_optunahub.py
new file mode 100644
index 000000000..38114bc44
--- /dev/null
+++ b/examples/_external_or_deprecated/benchmark_with_optunahub.py
@@ -0,0 +1,127 @@
+"""
+====================================================
+Hyperparameter Optimization Benchmark with OptunaHub
+====================================================
+
+In this tutorial, we walk through how to conduct hyperparameter optimization experiments using OpenML and OptunaHub.
+"""
+############################################################################
+# Please make sure to install the dependencies with:
+# ``pip install "openml>=0.15.1" plotly``
+# Then we import all the necessary modules.
+
+# License: BSD 3-Clause
+
+import logging
+
+import optuna
+from sklearn.compose import ColumnTransformer
+from sklearn.ensemble import RandomForestClassifier
+from sklearn.impute import SimpleImputer
+from sklearn.pipeline import Pipeline
+from sklearn.preprocessing import OneHotEncoder
+
+import openml
+
+logger = logging.Logger(name="Experiment Logger", level=1)
+
+#
+#
Warning
+#
+# For the rest of this tutorial, we will require the `openml-sklearn` package.
+# Install it with `pip install openml-sklearn`.
+#
+#
+
+# %%
+# Get sklearn extension to run sklearn models easily on OpenML tasks.
+from openml_sklearn import SklearnExtension, cat, cont
+
+extension = SklearnExtension()
+
+# Set your openml api key if you want to upload your results to OpenML (eg:
+# https://openml.org/search?type=run&sort=date) . To get one, simply make an
+# account (you don't need one for anything else, just to upload your results),
+# go to your profile and select the API-KEY.
+# Or log in, and navigate to https://www.openml.org/auth/api-key
+openml.config.apikey = ""
+############################################################################
+# Prepare for preprocessors and an OpenML task
+# ============================================
+
+# OpenML contains several key concepts which it needs to make machine learning research shareable.
+# A machine learning experiment consists of one or several runs, which describe the performance of
+# an algorithm (called a flow in OpenML), its hyperparameter settings (called a setup) on a task.
+# A Task is the combination of a dataset, a split and an evaluation metric We choose a dataset from
+# OpenML, (https://www.openml.org/d/1464) and a subsequent task (https://www.openml.org/t/10101) To
+# make your own dataset and task, please refer to
+# https://openml.github.io/openml-python/main/examples/30_extended/create_upload_tutorial.html
+
+# https://www.openml.org/search?type=study&study_type=task&id=218
+task_id = 10101
+seed = 42
+categorical_preproc = (
+ "categorical",
+ OneHotEncoder(sparse_output=False, handle_unknown="ignore"),
+ cat,
+)
+numerical_preproc = ("numerical", SimpleImputer(strategy="median"), cont)
+preproc = ColumnTransformer([categorical_preproc, numerical_preproc])
+
+############################################################################
+# Define a pipeline for the hyperparameter optimization (this is standark for Optuna)
+# =====================================================
+
+# Optuna explanation
+# we follow the `Optuna `__ search space design.
+
+# OpenML runs
+# We can simply pass the parametrized classifier to `run_model_on_task` to obtain the performance
+# of the pipeline
+# on the specified OpenML task.
+# Do you want to share your results along with an easily reproducible pipeline, you can set an API
+# key and just upload your results.
+# You can find more examples on https://www.openml.org/
+
+
+def objective(trial: optuna.Trial) -> Pipeline:
+ clf = RandomForestClassifier(
+ max_depth=trial.suggest_int("max_depth", 2, 32, log=True),
+ min_samples_leaf=trial.suggest_float("min_samples_leaf", 0.0, 1.0),
+ random_state=seed,
+ )
+ pipe = Pipeline(steps=[("preproc", preproc), ("model", clf)])
+ logger.log(1, f"Running pipeline - {pipe}")
+ run = openml.runs.run_model_on_task(pipe, task=task_id, avoid_duplicate_runs=False)
+
+ logger.log(1, f"Model has been trained - {run}")
+ if openml.config.apikey != "":
+ try:
+ run.publish()
+
+ logger.log(1, f"Run was uploaded to - {run.openml_url}")
+ except Exception as e: # noqa: BLE001
+ logger.log(1, f"Could not publish run - {e}")
+ else:
+ logger.log(
+ 0,
+ "If you want to publish your results to OpenML, please set an apikey",
+ )
+ accuracy = max(run.fold_evaluations["predictive_accuracy"][0].values())
+ logger.log(0, f"Accuracy {accuracy}")
+
+ return accuracy
+
+
+############################################################################
+# Optimize the pipeline
+# =====================
+study = optuna.create_study(direction="maximize")
+logger.log(0, f"Study {study}")
+study.optimize(objective, n_trials=15)
+
+############################################################################
+# Visualize the optimization history
+# ==================================
+fig = optuna.visualization.plot_optimization_history(study)
+fig.show()
diff --git a/examples/_external_or_deprecated/fetch_runtimes_tutorial.py b/examples/_external_or_deprecated/fetch_runtimes_tutorial.py
new file mode 100644
index 000000000..c8f85adc5
--- /dev/null
+++ b/examples/_external_or_deprecated/fetch_runtimes_tutorial.py
@@ -0,0 +1,480 @@
+# %% [markdown]
+# Measuring runtimes for Scikit-learn models
+#
+# The runtime of machine learning models on specific datasets can be a deciding
+# factor on the choice of algorithms, especially for benchmarking and comparison
+# purposes. OpenML's scikit-learn extension provides runtime data from runs of
+# model fit and prediction on tasks or datasets, for both the CPU-clock as well
+# as the actual wallclock-time incurred. The objective of this example is to
+# illustrate how to retrieve such timing measures, and also offer some potential
+# means of usage and interpretation of the same.
+#
+# It should be noted that there are multiple levels at which parallelism can occur.
+#
+# * At the outermost level, OpenML tasks contain fixed data splits, on which the
+# defined model/flow is executed. Thus, a model can be fit on each OpenML dataset fold
+# in parallel using the `n_jobs` parameter to `run_model_on_task` or `run_flow_on_task`
+# (illustrated under Case 2 & 3 below).
+#
+# * The model/flow specified can also include scikit-learn models that perform their own
+# parallelization. For instance, by specifying `n_jobs` in a Random Forest model definition
+# (covered under Case 2 below).
+#
+# * The sklearn model can further be an HPO estimator and contain it's own parallelization.
+# If the base estimator used also supports `parallelization`, then there's at least a 2-level nested
+# definition for parallelization possible (covered under Case 3 below).
+#
+# We shall cover these 5 representative scenarios for:
+#
+# * (Case 1) Retrieving runtimes for Random Forest training and prediction on each of the
+# cross-validation folds
+#
+# * (Case 2) Testing the above setting in a parallel setup and monitor the difference using
+# runtimes retrieved
+#
+# * (Case 3) Comparing RandomSearchCV and GridSearchCV on the above task based on runtimes
+#
+# * (Case 4) Running models that don't run in parallel or models which scikit-learn doesn't
+# parallelize
+#
+# * (Case 5) Running models that do not release the Python Global Interpreter Lock (GIL)
+
+import numpy as np
+from joblib.parallel import parallel_backend
+from matplotlib import pyplot as plt
+from sklearn.ensemble import RandomForestClassifier
+from sklearn.model_selection import GridSearchCV, RandomizedSearchCV
+from sklearn.naive_bayes import GaussianNB
+from sklearn.neural_network import MLPClassifier
+from sklearn.tree import DecisionTreeClassifier
+
+import openml
+
+# %% [markdown]
+# # Preparing tasks and scikit-learn models
+
+# %%
+task_id = 167119
+
+task = openml.tasks.get_task(task_id)
+print(task)
+
+# Viewing associated data
+n_repeats, n_folds, n_samples = task.get_split_dimensions()
+print(
+ f"Task {task_id}: number of repeats: {n_repeats}, number of folds: {n_folds}, number of samples {n_samples}."
+)
+
+
+# Creating utility function
+def print_compare_runtimes(measures):
+ for repeat, val1 in measures["usercpu_time_millis_training"].items():
+ for fold, val2 in val1.items():
+ print(
+ "Repeat #{}-Fold #{}: CPU-{:.3f} vs Wall-{:.3f}".format(
+ repeat, fold, val2, measures["wall_clock_time_millis_training"][repeat][fold]
+ )
+ )
+
+
+# %% [markdown]
+# # Case 1: Running a Random Forest model on an OpenML task
+# We'll run a Random Forest model and obtain an OpenML run object. We can
+# see the evaluations recorded per fold for the dataset and the information
+# available for this run.
+
+# %%
+clf = RandomForestClassifier(n_estimators=10)
+
+run1 = openml.runs.run_model_on_task(
+ model=clf,
+ task=task,
+ upload_flow=False,
+ avoid_duplicate_runs=False,
+)
+measures = run1.fold_evaluations
+
+print("The timing and performance metrics available: ")
+for key in measures:
+ print(key)
+print()
+
+print(
+ "The performance metric is recorded under `predictive_accuracy` per "
+ "fold and can be retrieved as: "
+)
+for repeat, val1 in measures["predictive_accuracy"].items():
+ for fold, val2 in val1.items():
+ print(f"Repeat #{repeat}-Fold #{fold}: {val2:.4f}")
+ print()
+
+# %% [markdown]
+# The remaining entries recorded in `measures` are the runtime records
+# related as:
+#
+# usercpu_time_millis = usercpu_time_millis_training + usercpu_time_millis_testing
+#
+# wall_clock_time_millis = wall_clock_time_millis_training + wall_clock_time_millis_testing
+#
+# The timing measures recorded as `*_millis_training` contain the per
+# repeat-per fold timing incurred for the execution of the `.fit()` procedure
+# of the model. For `usercpu_time_*` the time recorded using `time.process_time()`
+# is converted to `milliseconds` and stored. Similarly, `time.time()` is used
+# to record the time entry for `wall_clock_time_*`. The `*_millis_testing` entry
+# follows the same procedure but for time taken for the `.predict()` procedure.
+
+# Comparing the CPU and wall-clock training times of the Random Forest model
+
+# %%
+print_compare_runtimes(measures)
+
+# %% [markdown]
+# ## Case 2: Running Scikit-learn model on an OpenML task in parallel
+# Redefining the model to allow parallelism with `n_jobs=2` (2 cores)
+
+# %%
+clf = RandomForestClassifier(n_estimators=10, n_jobs=2)
+
+run2 = openml.runs.run_model_on_task(
+ model=clf, task=task, upload_flow=False, avoid_duplicate_runs=False
+)
+measures = run2.fold_evaluations
+# The wall-clock time recorded per fold should be lesser than Case 1 above
+print_compare_runtimes(measures)
+
+# %% [markdown]
+# Running a Random Forest model on an OpenML task in parallel (all cores available):
+
+# %%
+# Redefining the model to use all available cores with `n_jobs=-1`
+clf = RandomForestClassifier(n_estimators=10, n_jobs=-1)
+
+run3 = openml.runs.run_model_on_task(
+ model=clf, task=task, upload_flow=False, avoid_duplicate_runs=False
+)
+measures = run3.fold_evaluations
+
+# %% [markdown]
+# The wall-clock time recorded per fold should be lesser than the case above,
+# if more than 2 CPU cores are available. The speed-up is more pronounced for
+# larger datasets.
+print_compare_runtimes(measures)
+
+# %% [markdown]
+# We can now observe that the ratio of CPU time to wallclock time is lower
+# than in case 1. This happens because joblib by default spawns subprocesses
+# for the workloads for which CPU time cannot be tracked. Therefore, interpreting
+# the reported CPU and wallclock time requires knowledge of the parallelization
+# applied at runtime.
+
+# %% [markdown]
+# Running the same task with a different parallel backend. Joblib provides multiple
+# backends: {`loky` (default), `multiprocessing`, `dask`, `threading`, `sequential`}.
+# The backend can be explicitly set using a joblib context manager. The behaviour of
+# the job distribution can change and therefore the scale of runtimes recorded too.
+
+# %%
+with parallel_backend(backend="multiprocessing", n_jobs=-1):
+ run3_ = openml.runs.run_model_on_task(
+ model=clf, task=task, upload_flow=False, avoid_duplicate_runs=False
+ )
+measures = run3_.fold_evaluations
+print_compare_runtimes(measures)
+
+# %% [markdown]
+# The CPU time interpretation becomes ambiguous when jobs are distributed over an
+# unknown number of cores or when subprocesses are spawned for which the CPU time
+# cannot be tracked, as in the examples above. It is impossible for OpenML-Python
+# to capture the availability of the number of cores/threads, their eventual
+# utilisation and whether workloads are executed in subprocesses, for various
+# cases that can arise as demonstrated in the rest of the example. Therefore,
+# the final interpretation of the runtimes is left to the `user`.
+
+# %% [markdown]
+# ## Case 3: Running and benchmarking HPO algorithms with their runtimes
+# We shall now optimize a similar RandomForest model for the same task using
+# scikit-learn's HPO support by using GridSearchCV to optimize our earlier
+# RandomForest model's hyperparameter `n_estimators`. Scikit-learn also provides a
+# `refit_time_` for such HPO models, i.e., the time incurred by training
+# and evaluating the model on the best found parameter setting. This is
+# included in the `wall_clock_time_millis_training` measure recorded.
+
+# %%
+
+clf = RandomForestClassifier(n_estimators=10, n_jobs=2)
+
+# GridSearchCV model
+n_iter = 5
+grid_pipe = GridSearchCV(
+ estimator=clf,
+ param_grid={"n_estimators": np.linspace(start=1, stop=50, num=n_iter).astype(int).tolist()},
+ cv=2,
+ n_jobs=2,
+)
+
+run4 = openml.runs.run_model_on_task(
+ model=grid_pipe, task=task, upload_flow=False, avoid_duplicate_runs=False, n_jobs=2
+)
+measures = run4.fold_evaluations
+print_compare_runtimes(measures)
+
+# %% [markdown]
+# Like any optimisation problem, scikit-learn's HPO estimators also generate
+# a sequence of configurations which are evaluated, using which the best found
+# configuration is tracked throughout the trace.
+# The OpenML run object stores these traces as OpenMLRunTrace objects accessible
+# using keys of the pattern (repeat, fold, iterations). Here `fold` implies the
+# outer-cross validation fold as obtained from the task data splits in OpenML.
+# GridSearchCV here performs grid search over the inner-cross validation folds as
+# parameterized by the `cv` parameter. Since `GridSearchCV` in this example performs a
+# `2-fold` cross validation, the runtime recorded per repeat-per fold in the run object
+# is for the entire `fit()` procedure of GridSearchCV thus subsuming the runtimes of
+# the 2-fold (inner) CV search performed.
+
+# %%
+# We earlier extracted the number of repeats and folds for this task:
+print(f"# repeats: {n_repeats}\n# folds: {n_folds}")
+
+# To extract the training runtime of the first repeat, first fold:
+print(run4.fold_evaluations["wall_clock_time_millis_training"][0][0])
+
+# %% [markdown]
+# To extract the training runtime of the 1-st repeat, 4-th (outer) fold and also
+# to fetch the parameters and performance of the evaluations made during
+# the 1-st repeat, 4-th fold evaluation by the Grid Search model.
+
+# %%
+_repeat = 0
+_fold = 3
+print(
+ "Total runtime for repeat {}'s fold {}: {:4f} ms".format(
+ _repeat, _fold, run4.fold_evaluations["wall_clock_time_millis_training"][_repeat][_fold]
+ )
+)
+for i in range(n_iter):
+ key = (_repeat, _fold, i)
+ r = run4.trace.trace_iterations[key]
+ print(
+ "n_estimators: {:>2} - score: {:.3f}".format(
+ r.parameters["parameter_n_estimators"], r.evaluation
+ )
+ )
+
+# %% [markdown]
+# Scikit-learn's HPO estimators also come with an argument `refit=True` as a default.
+# In our previous model definition it was set to True by default, which meant that the best
+# found hyperparameter configuration was used to refit or retrain the model without any inner
+# cross validation. This extra refit time measure is provided by the scikit-learn model as the
+# attribute `refit_time_`.
+# This time is included in the `wall_clock_time_millis_training` measure.
+#
+# For non-HPO estimators, `wall_clock_time_millis = wall_clock_time_millis_training + wall_clock_time_millis_testing`.
+#
+# For HPO estimators, `wall_clock_time_millis = wall_clock_time_millis_training + wall_clock_time_millis_testing + refit_time`.
+#
+# This refit time can therefore be explicitly extracted in this manner:
+
+
+# %%
+
+
+def extract_refit_time(run, repeat, fold):
+ return (
+ run.fold_evaluations["wall_clock_time_millis"][repeat][fold]
+ - run.fold_evaluations["wall_clock_time_millis_training"][repeat][fold]
+ - run.fold_evaluations["wall_clock_time_millis_testing"][repeat][fold]
+ )
+
+
+for repeat in range(n_repeats):
+ for fold in range(n_folds):
+ print(f"Repeat #{repeat}-Fold #{fold}: {extract_refit_time(run4, repeat, fold):.4f}")
+
+# %% [markdown]
+# Along with the GridSearchCV already used above, we demonstrate how such
+# optimisation traces can be retrieved by showing an application of these
+# traces - comparing the speed of finding the best configuration using
+# RandomizedSearchCV and GridSearchCV available with scikit-learn.
+
+# %%
+# RandomizedSearchCV model
+rs_pipe = RandomizedSearchCV(
+ estimator=clf,
+ param_distributions={
+ "n_estimators": np.linspace(start=1, stop=50, num=15).astype(int).tolist()
+ },
+ cv=2,
+ n_iter=n_iter,
+ n_jobs=2,
+)
+run5 = openml.runs.run_model_on_task(
+ model=rs_pipe, task=task, upload_flow=False, avoid_duplicate_runs=False, n_jobs=2
+)
+
+# %% [markdown]
+# Since for the call to ``openml.runs.run_model_on_task`` the parameter
+# ``n_jobs`` is set to its default ``None``, the evaluations across the OpenML folds
+# are not parallelized. Hence, the time recorded is agnostic to the ``n_jobs``
+# being set at both the HPO estimator ``GridSearchCV`` as well as the base
+# estimator ``RandomForestClassifier`` in this case. The OpenML extension only records the
+# time taken for the completion of the complete ``fit()`` call, per-repeat per-fold.
+#
+# This notion can be used to extract and plot the best found performance per
+# fold by the HPO model and the corresponding time taken for search across
+# that fold. Moreover, since ``n_jobs=None`` for ``openml.runs.run_model_on_task``
+# the runtimes per fold can be cumulatively added to plot the trace against time.
+
+
+# %%
+def extract_trace_data(run, n_repeats, n_folds, n_iter, key=None):
+ key = "wall_clock_time_millis_training" if key is None else key
+ data = {"score": [], "runtime": []}
+ for i_r in range(n_repeats):
+ for i_f in range(n_folds):
+ data["runtime"].append(run.fold_evaluations[key][i_r][i_f])
+ for i_i in range(n_iter):
+ r = run.trace.trace_iterations[(i_r, i_f, i_i)]
+ if r.selected:
+ data["score"].append(r.evaluation)
+ break
+ return data
+
+
+def get_incumbent_trace(trace):
+ best_score = 1
+ inc_trace = []
+ for i, r in enumerate(trace):
+ if i == 0 or (1 - r) < best_score:
+ best_score = 1 - r
+ inc_trace.append(best_score)
+ return inc_trace
+
+
+grid_data = extract_trace_data(run4, n_repeats, n_folds, n_iter)
+rs_data = extract_trace_data(run5, n_repeats, n_folds, n_iter)
+
+plt.clf()
+plt.plot(
+ np.cumsum(grid_data["runtime"]), get_incumbent_trace(grid_data["score"]), label="Grid Search"
+)
+plt.plot(
+ np.cumsum(rs_data["runtime"]), get_incumbent_trace(rs_data["score"]), label="Random Search"
+)
+plt.xscale("log")
+plt.yscale("log")
+plt.xlabel("Wallclock time (in milliseconds)")
+plt.ylabel("1 - Accuracy")
+plt.title("Optimisation Trace Comparison")
+plt.legend()
+plt.show()
+
+# %% [markdown]
+# ## Case 4: Running models that scikit-learn doesn't parallelize
+# Both scikit-learn and OpenML depend on parallelism implemented through `joblib`.
+# However, there can be cases where either models cannot be parallelized or don't
+# depend on joblib for its parallelism. 2 such cases are illustrated below.
+#
+# Running a Decision Tree model that doesn't support parallelism implicitly, but
+# using OpenML to parallelize evaluations for the outer-cross validation folds.
+
+# %%
+dt = DecisionTreeClassifier()
+
+run6 = openml.runs.run_model_on_task(
+ model=dt, task=task, upload_flow=False, avoid_duplicate_runs=False, n_jobs=2
+)
+measures = run6.fold_evaluations
+print_compare_runtimes(measures)
+
+# %% [markdown]
+# Although the decision tree does not run in parallel, it can release the
+# `Python GIL `_.
+# This can result in surprising runtime measures as demonstrated below:
+
+# %%
+with parallel_backend("threading", n_jobs=-1):
+ run7 = openml.runs.run_model_on_task(
+ model=dt, task=task, upload_flow=False, avoid_duplicate_runs=False
+ )
+measures = run7.fold_evaluations
+print_compare_runtimes(measures)
+
+# %% [markdown]
+# Running a Neural Network from scikit-learn that uses scikit-learn independent
+# parallelism using libraries such as
+# [MKL, OpenBLAS or BLIS](https://scikit-learn.org/stable/computing/parallelism.html#parallel-numpy-and-scipy-routines-from-numerical-libraries>).
+
+# %%
+mlp = MLPClassifier(max_iter=10)
+
+run8 = openml.runs.run_model_on_task(
+ model=mlp, task=task, upload_flow=False, avoid_duplicate_runs=False
+)
+measures = run8.fold_evaluations
+print_compare_runtimes(measures)
+
+# %% [markdown]
+# ## Case 5: Running Scikit-learn models that don't release GIL
+# Certain Scikit-learn models do not release the
+# [Python GIL](https://docs.python.org/dev/glossary.html#term-global-interpreter-lock) and
+# are also not executed in parallel via a BLAS library. In such cases, the
+# CPU times and wallclock times are most likely trustworthy. Note however
+# that only very few models such as naive Bayes models are of this kind.
+
+# %%
+clf = GaussianNB()
+
+with parallel_backend("multiprocessing", n_jobs=-1):
+ run9 = openml.runs.run_model_on_task(
+ model=clf, task=task, upload_flow=False, avoid_duplicate_runs=False
+ )
+measures = run9.fold_evaluations
+print_compare_runtimes(measures)
+
+# %% [markdown]
+# ## Summmary
+# The scikit-learn extension for OpenML-Python records model runtimes for the
+# CPU-clock and the wall-clock times. The above examples illustrated how these
+# recorded runtimes can be extracted when using a scikit-learn model and under
+# parallel setups too. To summarize, the scikit-learn extension measures the:
+#
+# * `CPU-time` & `wallclock-time` for the whole run
+#
+# * A run here corresponds to a call to `run_model_on_task` or `run_flow_on_task`
+# * The recorded time is for the model fit for each of the outer-cross validations folds,
+# i.e., the OpenML data splits
+#
+# * Python's `time` module is used to compute the runtimes
+#
+# * `CPU-time` is recorded using the responses of `time.process_time()`
+# * `wallclock-time` is recorded using the responses of `time.time()`
+#
+# * The timings recorded by OpenML per outer-cross validation fold is agnostic to
+# model parallelisation
+#
+# * The wallclock times reported in Case 2 above highlights the speed-up on using `n_jobs=-1`
+# in comparison to `n_jobs=2`, since the timing recorded by OpenML is for the entire
+# `fit()` procedure, whereas the parallelisation is performed inside `fit()` by scikit-learn
+# * The CPU-time for models that are run in parallel can be difficult to interpret
+#
+# * `CPU-time` & `wallclock-time` for each search per outer fold in an HPO run
+#
+# * Reports the total time for performing search on each of the OpenML data split, subsuming
+# any sort of parallelism that happened as part of the HPO estimator or the underlying
+# base estimator
+# * Also allows extraction of the `refit_time` that scikit-learn measures using `time.time()`
+# for retraining the model per outer fold, for the best found configuration
+#
+# * `CPU-time` & `wallclock-time` for models that scikit-learn doesn't parallelize
+#
+# * Models like Decision Trees or naive Bayes don't parallelize and thus both the wallclock and
+# CPU times are similar in runtime for the OpenML call
+# * However, models implemented in Cython, such as the Decision Trees can release the GIL and
+# still run in parallel if a `threading` backend is used by joblib.
+# * Scikit-learn Neural Networks can undergo parallelization implicitly owing to thread-level
+# parallelism involved in the linear algebraic operations and thus the wallclock-time and
+# CPU-time can differ.
+#
+# Because of all the cases mentioned above it is crucial to understand which case is triggered
+# when reporting runtimes for scikit-learn models measured with OpenML-Python!
+# License: BSD 3-Clause
diff --git a/examples/_external_or_deprecated/flow_id_tutorial.py b/examples/_external_or_deprecated/flow_id_tutorial.py
new file mode 100644
index 000000000..19190cf0b
--- /dev/null
+++ b/examples/_external_or_deprecated/flow_id_tutorial.py
@@ -0,0 +1,85 @@
+# %% [markdown]
+# # Obtaining Flow IDs
+# This tutorial discusses different ways to obtain the ID of a flow in order to perform further
+# analysis.
+
+
+# %%
+import sklearn.tree
+
+import openml
+
+# %% [markdown]
+# .. warning::
+# .. include:: ../../test_server_usage_warning.txt
+
+# %%
+openml.config.start_using_configuration_for_example()
+openml.config.server = "https://api.openml.org/api/v1/xml"
+
+# %%
+# Defining a classifier
+clf = sklearn.tree.DecisionTreeClassifier()
+
+# %% [markdown]
+# ## 1. Obtaining a flow given a classifier
+
+# %%
+flow = openml.extensions.get_extension_by_model(clf).model_to_flow(clf).publish()
+flow_id = flow.flow_id
+print(flow_id)
+
+# %% [markdown]
+# This piece of code is rather involved. First, it retrieves a
+# :class:`~openml.extensions.Extension` which is registered and can handle the given model,
+# in our case it is :class:`openml.extensions.sklearn.SklearnExtension`. Second, the extension
+# converts the classifier into an instance of :class:`openml.OpenMLFlow`. Third and finally,
+# the publish method checks whether the current flow is already present on OpenML. If not,
+# it uploads the flow, otherwise, it updates the current instance with all information computed
+# by the server (which is obviously also done when uploading/publishing a flow).
+#
+# To simplify the usage we have created a helper function which automates all these steps:
+
+# %%
+flow_id = openml.flows.get_flow_id(model=clf)
+print(flow_id)
+
+# %% [markdown]
+# ## 2. Obtaining a flow given its name
+# The schema of a flow is given in XSD (
+# [here](https://github.com/openml/OpenML/blob/master/openml_OS/views/pages/api_new/v1/xsd/openml.implementation.upload.xsd)).
+# Only two fields are required, a unique name, and an external version. While it should be pretty
+# obvious why we need a name, the need for the additional external version information might not
+# be immediately clear. However, this information is very important as it allows to have multiple
+# flows with the same name for different versions of a software. This might be necessary if an
+# algorithm or implementation introduces, renames or drop hyperparameters over time.
+
+# %%
+print(flow.name, flow.external_version)
+
+# %% [markdown]
+# The name and external version are automatically added to a flow when constructing it from a
+# model. We can then use them to retrieve the flow id as follows:
+
+# %%
+flow_id = openml.flows.flow_exists(name=flow.name, external_version=flow.external_version)
+print(flow_id)
+
+# %% [markdown]
+# We can also retrieve all flows for a given name:
+
+# %%
+flow_ids = openml.flows.get_flow_id(name=flow.name)
+print(flow_ids)
+
+# %% [markdown]
+# This also works with the actual model (generalizing the first part of this example):
+
+# %%
+flow_ids = openml.flows.get_flow_id(model=clf, exact_version=False)
+print(flow_ids)
+
+# %%
+# Deactivating test configuration
+openml.config.stop_using_configuration_for_example()
+# License: BSD 3-Clause
diff --git a/examples/_external_or_deprecated/flows_and_runs_tutorial.py b/examples/_external_or_deprecated/flows_and_runs_tutorial.py
new file mode 100644
index 000000000..71d6960bd
--- /dev/null
+++ b/examples/_external_or_deprecated/flows_and_runs_tutorial.py
@@ -0,0 +1,250 @@
+# %% [markdown]
+# #Flows and Runs
+# This tutorial covers how to train/run a model and how to upload the results.
+
+# %%
+from sklearn import compose, ensemble, impute, neighbors, pipeline, preprocessing, tree
+
+import openml
+
+# %% [markdown]
+# We'll use the test server for the rest of this tutorial.
+#
+# .. warning::
+# .. include:: ../../test_server_usage_warning.txt
+
+# %%
+openml.config.start_using_configuration_for_example()
+
+# %% [markdown]
+# ## Train machine learning models
+#
+# Train a scikit-learn model on the data manually.
+
+# %%
+# NOTE: We are using dataset 68 from the test server: https://test.openml.org/d/68
+dataset = openml.datasets.get_dataset(dataset_id="eeg-eye-state", version=1)
+X, y, categorical_indicator, attribute_names = dataset.get_data(
+ target=dataset.default_target_attribute
+)
+clf = neighbors.KNeighborsClassifier(n_neighbors=1)
+clf.fit(X, y)
+
+# %% [markdown]
+# You can also ask for meta-data to automatically preprocess the data.
+#
+# * e.g. categorical features -> do feature encoding
+
+# %%
+dataset = openml.datasets.get_dataset(17)
+X, y, categorical_indicator, attribute_names = dataset.get_data(
+ target=dataset.default_target_attribute
+)
+print(f"Categorical features: {categorical_indicator}")
+transformer = compose.ColumnTransformer(
+ [("one_hot_encoder", preprocessing.OneHotEncoder(categories="auto"), categorical_indicator)]
+)
+X = transformer.fit_transform(X)
+clf.fit(X, y)
+
+# %% [markdown]
+# ## Runs: Easily explore models
+# We can run (many) scikit-learn algorithms on (many) OpenML tasks.
+
+# %%
+# Get a task
+task = openml.tasks.get_task(403)
+
+# Build any classifier or pipeline
+clf = tree.DecisionTreeClassifier()
+
+# Run the flow
+run = openml.runs.run_model_on_task(clf, task)
+
+print(run)
+
+# %% [markdown]
+# Share the run on the OpenML server
+#
+# So far the run is only available locally. By calling the publish function,
+# the run is sent to the OpenML server:
+
+# %%
+myrun = run.publish()
+# For this tutorial, our configuration publishes to the test server
+# as to not pollute the main server.
+print(f"Uploaded to {myrun.openml_url}")
+
+# %% [markdown]
+# We can now also inspect the flow object which was automatically created:
+
+# %%
+flow = openml.flows.get_flow(run.flow_id)
+print(flow)
+
+# %% [markdown]
+# ## It also works with pipelines
+#
+# When you need to handle 'dirty' data, build pipelines to model then automatically.
+# To demonstrate this using the dataset `credit-a `_ via
+# `task `_ as it contains both numerical and categorical
+# variables and missing values in both.
+
+# %%
+task = openml.tasks.get_task(96)
+
+# OpenML helper functions for sklearn can be plugged in directly for complicated pipelines
+from openml.extensions.sklearn import cat, cont
+
+pipe = pipeline.Pipeline(
+ steps=[
+ (
+ "Preprocessing",
+ compose.ColumnTransformer(
+ [
+ (
+ "categorical",
+ preprocessing.OneHotEncoder(handle_unknown="ignore"),
+ cat, # returns the categorical feature indices
+ ),
+ (
+ "continuous",
+ impute.SimpleImputer(strategy="median"),
+ cont,
+ ), # returns the numeric feature indices
+ ]
+ ),
+ ),
+ ("Classifier", ensemble.RandomForestClassifier(n_estimators=10)),
+ ]
+)
+
+run = openml.runs.run_model_on_task(pipe, task, avoid_duplicate_runs=False)
+myrun = run.publish()
+print(f"Uploaded to {myrun.openml_url}")
+
+
+# %% [markdown]
+# The above pipeline works with the helper functions that internally deal with pandas DataFrame.
+# In the case, pandas is not available, or a NumPy based data processing is the requirement, the
+# above pipeline is presented below to work with NumPy.
+
+# %%
+# Extracting the indices of the categorical columns
+features = task.get_dataset().features
+categorical_feature_indices = []
+numeric_feature_indices = []
+for i in range(len(features)):
+ if features[i].name == task.target_name:
+ continue
+ if features[i].data_type == "nominal":
+ categorical_feature_indices.append(i)
+ else:
+ numeric_feature_indices.append(i)
+
+pipe = pipeline.Pipeline(
+ steps=[
+ (
+ "Preprocessing",
+ compose.ColumnTransformer(
+ [
+ (
+ "categorical",
+ preprocessing.OneHotEncoder(handle_unknown="ignore"),
+ categorical_feature_indices,
+ ),
+ (
+ "continuous",
+ impute.SimpleImputer(strategy="median"),
+ numeric_feature_indices,
+ ),
+ ]
+ ),
+ ),
+ ("Classifier", ensemble.RandomForestClassifier(n_estimators=10)),
+ ]
+)
+
+run = openml.runs.run_model_on_task(pipe, task, avoid_duplicate_runs=False)
+myrun = run.publish()
+print(f"Uploaded to {myrun.openml_url}")
+
+# %% [markdown]
+# ## Running flows on tasks offline for later upload
+# For those scenarios where there is no access to internet, it is possible to run
+# a model on a task without uploading results or flows to the server immediately.
+
+# To perform the following line offline, it is required to have been called before
+# such that the task is cached on the local openml cache directory:
+
+# %%
+task = openml.tasks.get_task(96)
+
+# The following lines can then be executed offline:
+run = openml.runs.run_model_on_task(
+ pipe,
+ task,
+ avoid_duplicate_runs=False,
+ upload_flow=False,
+)
+
+# The run may be stored offline, and the flow will be stored along with it:
+run.to_filesystem(directory="myrun")
+
+# They may be loaded and uploaded at a later time
+run = openml.runs.OpenMLRun.from_filesystem(directory="myrun")
+run.publish()
+
+# Publishing the run will automatically upload the related flow if
+# it does not yet exist on the server.
+
+# %% [markdown]
+# Alternatively, one can also directly run flows.
+
+# %%
+# Get a task
+task = openml.tasks.get_task(403)
+
+# Build any classifier or pipeline
+clf = tree.ExtraTreeClassifier()
+
+# Obtain the scikit-learn extension interface to convert the classifier
+# into a flow object.
+extension = openml.extensions.get_extension_by_model(clf)
+flow = extension.model_to_flow(clf)
+
+run = openml.runs.run_flow_on_task(flow, task)
+
+# %% [markdown]
+# ## Challenge
+#
+# Try to build the best possible models on several OpenML tasks,
+# compare your results with the rest of the class and learn from
+# them. Some tasks you could try (or browse openml.org):
+#
+# * EEG eye state: data_id:`1471 `_,
+# task_id:`14951 `_
+# * Volcanoes on Venus: data_id:`1527 `_,
+# task_id:`10103 `_
+# * Walking activity: data_id:`1509 `_,
+# task_id:`9945 `_, 150k instances.
+# * Covertype (Satellite): data_id:`150 `_,
+# task_id:`218 `_, 500k instances.
+# * Higgs (Physics): data_id:`23512 `_,
+# task_id:`52950 `_, 100k instances, missing values.
+
+# %%
+# Easy benchmarking:
+for task_id in [115]: # Add further tasks. Disclaimer: they might take some time
+ task = openml.tasks.get_task(task_id)
+ data = openml.datasets.get_dataset(task.dataset_id)
+ clf = neighbors.KNeighborsClassifier(n_neighbors=5)
+
+ run = openml.runs.run_model_on_task(clf, task, avoid_duplicate_runs=False)
+ myrun = run.publish()
+ print(f"kNN on {data.name}: {myrun.openml_url}")
+
+
+# %%
+openml.config.stop_using_configuration_for_example()
+# License: BSD 3-Clause
diff --git a/examples/_external_or_deprecated/plot_svm_hyperparameters_tutorial.py b/examples/_external_or_deprecated/plot_svm_hyperparameters_tutorial.py
new file mode 100644
index 000000000..7bb72db5a
--- /dev/null
+++ b/examples/_external_or_deprecated/plot_svm_hyperparameters_tutorial.py
@@ -0,0 +1,83 @@
+# %% [markdown]
+# # Plotting hyperparameter surfaces
+
+# %%
+import numpy as np
+
+import openml
+
+# %% [markdown]
+# # First step - obtaining the data
+# First, we need to choose an SVM flow, for example 8353, and a task. Finding the IDs of them are
+# not part of this tutorial, this could for example be done via the website.
+#
+# For this we use the function ``list_evaluations_setup`` which can automatically join
+# evaluations conducted by the server with the hyperparameter settings extracted from the
+# uploaded runs (called *setup*).
+
+# %%
+df = openml.evaluations.list_evaluations_setups(
+ function="predictive_accuracy",
+ flows=[8353],
+ tasks=[6],
+ # Using this flag incorporates the hyperparameters into the returned dataframe. Otherwise,
+ # the dataframe would contain a field ``paramaters`` containing an unparsed dictionary.
+ parameters_in_separate_columns=True,
+)
+print(df.head(n=10))
+
+# %% [markdown]
+# We can see all the hyperparameter names in the columns of the dataframe:
+
+# %%
+for name in df.columns:
+ print(name)
+
+# %% [markdown]
+# Next, we cast and transform the hyperparameters of interest (``C`` and ``gamma``) so that we
+# can nicely plot them.
+
+# %%
+hyperparameters = ["sklearn.svm.classes.SVC(16)_C", "sklearn.svm.classes.SVC(16)_gamma"]
+df[hyperparameters] = df[hyperparameters].astype(float).apply(np.log10)
+
+# %% [markdown]
+# ## Option 1 - plotting via the pandas helper functions
+
+# %%
+df.plot.hexbin(
+ x="sklearn.svm.classes.SVC(16)_C",
+ y="sklearn.svm.classes.SVC(16)_gamma",
+ C="value",
+ reduce_C_function=np.mean,
+ gridsize=25,
+ title="SVM performance landscape",
+)
+
+# %% [markdown]
+# ## Option 2 - plotting via matplotlib
+
+# %%
+import matplotlib.pyplot as plt
+
+fig, ax = plt.subplots()
+
+C = df["sklearn.svm.classes.SVC(16)_C"]
+gamma = df["sklearn.svm.classes.SVC(16)_gamma"]
+score = df["value"]
+
+# Plotting all evaluations:
+ax.plot(C, gamma, "ko", ms=1)
+# Create a contour plot
+cntr = ax.tricontourf(C, gamma, score, levels=12, cmap="RdBu_r")
+# Adjusting the colorbar
+fig.colorbar(cntr, ax=ax, label="accuracy")
+# Adjusting the axis limits
+ax.set(
+ xlim=(min(C), max(C)),
+ ylim=(min(gamma), max(gamma)),
+ xlabel="C (log10)",
+ ylabel="gamma (log10)",
+)
+ax.set_title("SVM performance landscape")
+# License: BSD 3-Clause
diff --git a/examples/_external_or_deprecated/run_setup_tutorial.py b/examples/_external_or_deprecated/run_setup_tutorial.py
new file mode 100644
index 000000000..25591bb58
--- /dev/null
+++ b/examples/_external_or_deprecated/run_setup_tutorial.py
@@ -0,0 +1,119 @@
+# %% [markdown]
+# # Run Setup
+# One of the key features of the openml-python library is that is allows to
+# reinstantiate flows with hyperparameter settings that were uploaded before.
+# This tutorial uses the concept of setups. Although setups are not extensively
+# described in the OpenML documentation (because most users will not directly
+# use them), they form a important concept within OpenML distinguishing between
+# hyperparameter configurations.
+# A setup is the combination of a flow with all its hyperparameters set.
+#
+# A key requirement for reinstantiating a flow is to have the same scikit-learn
+# version as the flow that was uploaded. However, this tutorial will upload the
+# flow (that will later be reinstantiated) itself, so it can be ran with any
+# scikit-learn version that is supported by this library. In this case, the
+# requirement of the corresponding scikit-learn versions is automatically met.
+#
+# In this tutorial we will
+# 1) Create a flow and use it to solve a task;
+# 2) Download the flow, reinstantiate the model with same hyperparameters,
+# and solve the same task again;
+# 3) We will verify that the obtained results are exactly the same.
+
+# %%
+
+import numpy as np
+from sklearn.compose import ColumnTransformer
+from sklearn.decomposition import TruncatedSVD
+from sklearn.ensemble import RandomForestClassifier
+from sklearn.impute import SimpleImputer
+from sklearn.pipeline import Pipeline, make_pipeline
+from sklearn.preprocessing import OneHotEncoder
+
+import openml
+from openml.extensions.sklearn import cat, cont
+
+# %% [markdown]
+# .. warning::
+# .. include:: ../../test_server_usage_warning.txt
+
+# %%
+openml.config.start_using_configuration_for_example()
+
+# %% [markdown]
+# 1) Create a flow and use it to solve a task
+
+# First, let's download the task that we are interested in
+
+# %%
+task = openml.tasks.get_task(6)
+
+# %% [markdown]
+# we will create a fairly complex model, with many preprocessing components and
+# many potential hyperparameters. Of course, the model can be as complex and as
+# easy as you want it to be
+
+
+# %%
+cat_imp = make_pipeline(
+ OneHotEncoder(handle_unknown="ignore"),
+ TruncatedSVD(),
+)
+cont_imp = SimpleImputer(strategy="median")
+ct = ColumnTransformer([("cat", cat_imp, cat), ("cont", cont_imp, cont)])
+model_original = Pipeline(
+ steps=[
+ ("transform", ct),
+ ("estimator", RandomForestClassifier()),
+ ]
+)
+
+# %% [markdown]
+# Let's change some hyperparameters. Of course, in any good application we
+# would tune them using, e.g., Random Search or Bayesian Optimization, but for
+# the purpose of this tutorial we set them to some specific values that might
+# or might not be optimal
+
+# %%
+hyperparameters_original = {
+ "estimator__criterion": "gini",
+ "estimator__n_estimators": 50,
+ "estimator__max_depth": 10,
+ "estimator__min_samples_leaf": 1,
+}
+model_original.set_params(**hyperparameters_original)
+
+# solve the task and upload the result (this implicitly creates the flow)
+run = openml.runs.run_model_on_task(model_original, task, avoid_duplicate_runs=False)
+run_original = run.publish() # this implicitly uploads the flow
+
+# %% [markdown]
+# ## 2) Download the flow and solve the same task again.
+
+# %%
+# obtain setup id (note that the setup id is assigned by the OpenML server -
+# therefore it was not yet available in our local copy of the run)
+run_downloaded = openml.runs.get_run(run_original.run_id)
+setup_id = run_downloaded.setup_id
+
+# after this, we can easily reinstantiate the model
+model_duplicate = openml.setups.initialize_model(setup_id)
+# it will automatically have all the hyperparameters set
+
+# and run the task again
+run_duplicate = openml.runs.run_model_on_task(model_duplicate, task, avoid_duplicate_runs=False)
+
+
+# %% [markdown]
+# ## 3) We will verify that the obtained results are exactly the same.
+
+# %%
+# the run has stored all predictions in the field data content
+np.testing.assert_array_equal(run_original.data_content, run_duplicate.data_content)
+
+
+# %%
+openml.config.stop_using_configuration_for_example()
+
+# By: Jan N. van Rijn
+# License: BSD 3-Clause
diff --git a/examples/_external_or_deprecated/upload_amlb_flows_and_runs.py b/examples/_external_or_deprecated/upload_amlb_flows_and_runs.py
new file mode 100644
index 000000000..b43926d4e
--- /dev/null
+++ b/examples/_external_or_deprecated/upload_amlb_flows_and_runs.py
@@ -0,0 +1,222 @@
+# %% [markdown]
+# # Creating and Using a Custom Flow
+
+# The most convenient way to create a flow for your machine learning workflow is to generate it
+# automatically as described in the
+# ["Obtaining Flow IDs"](../../30_extended/flow_id_tutorial) tutorial.
+# However, there are scenarios where this is not possible, such
+# as when the flow uses a framework without an extension or when the flow is described by a script.
+
+# In those cases you can still create a custom flow by following the steps of this tutorial.
+# As an example we will use the flows generated for the
+# [AutoML Benchmark](https://openml.github.io/automlbenchmark/),
+# and also show how to link runs to the custom flow.
+
+# %%
+from collections import OrderedDict
+
+import numpy as np
+
+import openml
+from openml.runs.functions import format_prediction
+
+# %% [markdown]
+# .. warning::
+# .. include:: ../../test_server_usage_warning.txt
+
+# %%
+openml.config.start_using_configuration_for_example()
+
+# %% [markdown]
+# ## 1. Defining the flow
+# The first step is to define all the hyperparameters of your flow.
+# The API pages feature a descriptions of each variable of the :class:`openml.flows.OpenMLFlow`.
+# Note that `external version` and `name` together uniquely identify a flow.
+#
+# The AutoML Benchmark runs AutoML systems across a range of tasks.
+# OpenML stores Flows for each AutoML system. However, the AutoML benchmark adds
+# preprocessing to the flow, so should be described in a new flow.
+#
+# We will break down the flow arguments into several groups, for the tutorial.
+# First we will define the name and version information.
+# Make sure to leave enough information so others can determine exactly which
+# version of the package/script is used. Use tags so users can find your flow easily.
+
+# %%
+general = {
+ "name": "automlbenchmark_autosklearn",
+ "description": (
+ "Auto-sklearn as set up by the AutoML Benchmark"
+ "Source: https://github.com/openml/automlbenchmark/releases/tag/v0.9"
+ ),
+ "external_version": "amlb==0.9",
+ "language": "English",
+ "tags": ["amlb", "benchmark", "study_218"],
+ "dependencies": "amlb==0.9",
+}
+
+# %% [markdown]
+# Next we define the flow hyperparameters. We define their name and default value in `parameters`,
+# and provide meta-data for each hyperparameter through `parameters_meta_info`.
+# Note that even though the argument name is `parameters` they describe the hyperparameters.
+# The use of ordered dicts is required.
+
+# %%
+flow_hyperparameters = {
+ "parameters": OrderedDict(time="240", memory="32", cores="8"),
+ "parameters_meta_info": OrderedDict(
+ cores=OrderedDict(description="number of available cores", data_type="int"),
+ memory=OrderedDict(description="memory in gigabytes", data_type="int"),
+ time=OrderedDict(description="time in minutes", data_type="int"),
+ ),
+}
+
+# %% [markdown]
+# It is possible to build a flow which uses other flows.
+# For example, the Random Forest Classifier is a flow, but you could also construct a flow
+# which uses a Random Forest Classifier in a ML pipeline. When constructing the pipeline flow,
+# you can use the Random Forest Classifier flow as a *subflow*. It allows for
+# all hyperparameters of the Random Classifier Flow to also be specified in your pipeline flow.
+#
+# Note: you can currently only specific one subflow as part of the components.
+#
+# In this example, the auto-sklearn flow is a subflow: the auto-sklearn flow is entirely executed as part of this flow.
+# This allows people to specify auto-sklearn hyperparameters used in this flow.
+# In general, using a subflow is not required.
+#
+# Note: flow 9313 is not actually the right flow on the test server,
+# but that does not matter for this demonstration.
+
+# %%
+autosklearn_flow = openml.flows.get_flow(9313) # auto-sklearn 0.5.1
+subflow = {
+ "components": OrderedDict(automl_tool=autosklearn_flow),
+ # If you do not want to reference a subflow, you can use the following:
+ # components=OrderedDict(),
+}
+
+# %% [markdown]
+# With all parameters of the flow defined, we can now initialize the OpenMLFlow and publish.
+# Because we provided all the details already, we do not need to provide a `model` to the flow.
+#
+# In our case, we don't even have a model. It is possible to have a model but still require
+# to follow these steps when the model (python object) does not have an extensions from which
+# to automatically extract the hyperparameters.
+# So whether you have a model with no extension or no model at all, explicitly set
+# the model of the flow to `None`.
+
+# %%
+autosklearn_amlb_flow = openml.flows.OpenMLFlow(
+ **general,
+ **flow_hyperparameters,
+ **subflow,
+ model=None,
+)
+autosklearn_amlb_flow.publish()
+print(f"autosklearn flow created: {autosklearn_amlb_flow.flow_id}")
+
+# %% [markdown]
+# ## 2. Using the flow
+# This Section will show how to upload run data for your custom flow.
+# Take care to change the values of parameters as well as the task id,
+# to reflect the actual run.
+# Task and parameter values in the example are fictional.
+
+# %%
+flow_id = autosklearn_amlb_flow.flow_id
+
+parameters = [
+ OrderedDict([("oml:name", "cores"), ("oml:value", 4), ("oml:component", flow_id)]),
+ OrderedDict([("oml:name", "memory"), ("oml:value", 16), ("oml:component", flow_id)]),
+ OrderedDict([("oml:name", "time"), ("oml:value", 120), ("oml:component", flow_id)]),
+]
+
+task_id = 1200 # Iris Task
+task = openml.tasks.get_task(task_id)
+dataset_id = task.get_dataset().dataset_id
+
+
+# %% [markdown]
+# The last bit of information for the run we need are the predicted values.
+# The exact format of the predictions will depend on the task.
+#
+# The predictions should always be a list of lists, each list should contain:
+#
+# - the repeat number: for repeated evaluation strategies. (e.g. repeated cross-validation)
+# - the fold number: for cross-validation. (what should this be for holdout?)
+# - 0: this field is for backward compatibility.
+# - index: the row (of the original dataset) for which the prediction was made.
+# - p_1, ..., p_c: for each class the predicted probability of the sample
+# belonging to that class. (no elements for regression tasks)
+# Make sure the order of these elements follows the order of `task.class_labels`.
+# - the predicted class/value for the sample
+# - the true class/value for the sample
+#
+# When using openml-python extensions (such as through `run_model_on_task`),
+# all of this formatting is automatic.
+# Unfortunately we can not automate this procedure for custom flows,
+# which means a little additional effort is required.
+#
+# Here we generated some random predictions in place.
+# You can ignore this code, or use it to better understand the formatting of the predictions.
+#
+# Find the repeats/folds for this task:
+
+# %%
+n_repeats, n_folds, _ = task.get_split_dimensions()
+all_test_indices = [
+ (repeat, fold, index)
+ for repeat in range(n_repeats)
+ for fold in range(n_folds)
+ for index in task.get_train_test_split_indices(fold, repeat)[1]
+]
+
+# random class probabilities (Iris has 150 samples and 3 classes):
+r = np.random.rand(150 * n_repeats, 3) # noqa: NPY002
+# scale the random values so that the probabilities of each sample sum to 1:
+y_proba = r / r.sum(axis=1).reshape(-1, 1)
+y_pred = y_proba.argmax(axis=1)
+
+class_map = dict(zip(range(3), task.class_labels, strict=False))
+_, y_true = task.get_X_and_y()
+y_true = [class_map[y] for y in y_true]
+
+# We format the predictions with the utility function `format_prediction`.
+# It will organize the relevant data in the expected format/order.
+predictions = []
+for where, y, yp, proba in zip(all_test_indices, y_true, y_pred, y_proba, strict=False):
+ repeat, fold, index = where
+
+ prediction = format_prediction(
+ task=task,
+ repeat=repeat,
+ fold=fold,
+ index=index,
+ prediction=class_map[yp],
+ truth=y,
+ proba=dict(zip(task.class_labels, proba, strict=False)),
+ )
+ predictions.append(prediction)
+
+# %% [markdown]
+# Finally we can create the OpenMLRun object and upload.
+# We use the argument setup_string because the used flow was a script.
+
+# %%
+benchmark_command = "python3 runbenchmark.py auto-sklearn medium -m aws -t 119"
+my_run = openml.runs.OpenMLRun(
+ task_id=task_id,
+ flow_id=flow_id,
+ dataset_id=dataset_id,
+ parameter_settings=parameters,
+ setup_string=benchmark_command,
+ data_content=predictions,
+ tags=["study_218"],
+ description_text="Run generated by the Custom Flow tutorial.",
+)
+my_run.publish()
+print("run created:", my_run.run_id)
+
+# %%
+openml.config.stop_using_configuration_for_example()
+# License: BSD 3-Clause
diff --git a/examples/introduction.py b/examples/introduction.py
new file mode 100644
index 000000000..630c72f9d
--- /dev/null
+++ b/examples/introduction.py
@@ -0,0 +1,22 @@
+# %% [markdown]
+#
+# We provide a set of examples here to get started with OpenML-Python. These examples cover various aspects of using the
+# OpenML API, including downloading datasets, uploading results, and working with tasks.
+#
+# ## Basics
+#
+# 1. [Installing and setting up OpenML-Python](../Basics/introduction_tutorial/)
+# 2. [Downloading datasets](../Basics/simple_datasets_tutorial/)
+# 3. [Using tasks](../Basics/simple_tasks_tutorial/)
+# 3. [Uploading experiment results](../Basics/simple_flows_and_runs_tutorial/)
+# 4. [Working with collections of tasks](../Basics/simple_suites_tutorial/)
+#
+# ## Advanced
+# 1. [Getting splits for datasets from tasks](../Advanced/task_manual_iteration_tutorial/)
+# 2. [Creating and uploading datasets](../Advanced/create_upload_tutorial/)
+# 3. [Searching and editing datasets](../Advanced/datasets_tutorial/)
+# 4. [Searching and creating tasks](../Advanced/task_tutorial/)
+# 5. [Listing, downloading, and uploading suites](../Advanced/suites_tutorial/)
+# 6. [Listing, downloading, and uploading studies](../Advanced/study_tutorial/)
+# 7. [Downloading evaluation results](../Advanced/fetch_evaluations_tutorial/)
+# 8. [Configuring logging](../Advanced/configure_logging/)
diff --git a/examples/sklearn/openml_run_example.py b/examples/sklearn/openml_run_example.py
deleted file mode 100644
index 5eb6f577b..000000000
--- a/examples/sklearn/openml_run_example.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from openml.apiconnector import APIConnector
-from openml.autorun import run_task
-from sklearn import ensemble
-import xmltodict
-import os
-"""
-An example of an automated machine learning experiment using run_task
-"""
-
-key_file_path = "apikey.txt"
-with open(key_file_path, 'r') as fh:
- key = fh.readline()
-
-task_id = 59
-
-clf = ensemble.RandomForestClassifier()
-connector = APIConnector(apikey = key)
-task = connector.get_task(task_id)
-
-prediction_path, description_path = run_task(task, clf)
-
-prediction_abspath = os.path.abspath(prediction_path)
-description_abspath = os.path.abspath(description_path)
-
-return_code, response = connector.upload_run(prediction_abspath, description_abspath)
-
-if(return_code == 200):
- response_dict = xmltodict.parse(response.content)
- run_id = response_dict['oml:upload_run']['oml:run_id']
- print("Uploaded run with id %s" % (run_id))
diff --git a/mkdocs.yml b/mkdocs.yml
new file mode 100644
index 000000000..419cc249e
--- /dev/null
+++ b/mkdocs.yml
@@ -0,0 +1,165 @@
+site_name: openml-python
+repo_url: https://github.com/openml/openml-python
+repo_name: openml/openml-python
+theme:
+ logo: images/openml_icon.png
+ favicon: images/openml_icon.png
+ name: material
+ features:
+ - content.code.annotate
+ - content.code.copy
+ - navigation.footer
+ - navigation.sections
+ - toc.follow
+ - toc.integrate
+ - navigation.tabs
+ - navigation.tabs.sticky
+ - header.autohide
+ - header.social
+ - search.suggest
+ - search.highlight
+ - search.share
+ palette:
+ - scheme: slate
+ media: "(prefers-color-scheme: dark)"
+ primary: indigo
+ accent: deep purple
+ toggle:
+ icon: material/eye-outline
+ name: Switch to light mode
+
+ # Palette toggle for light mode
+ - scheme: default
+ media: "(prefers-color-scheme: light)"
+ primary: indigo
+ accent: deep purple
+ toggle:
+ icon: material/eye
+ name: Switch to dark mode
+
+extra_css:
+ - stylesheets/extra.css
+
+nav:
+ - index.md
+ - Examples:
+ - Overview: examples/introduction.py
+ - Basics:
+ - Setup: examples/Basics/introduction_tutorial.py
+ - Datasets: examples/Basics/simple_datasets_tutorial.py
+ - Tasks: examples/Basics/simple_tasks_tutorial.py
+ - Flows and Runs: examples/Basics/simple_flows_and_runs_tutorial.py
+ - Suites: examples/Basics/simple_suites_tutorial.py
+ - Advanced:
+ - Dataset Splits from Tasks: examples/Advanced/task_manual_iteration_tutorial.py
+ - Creating and Uploading Datasets: examples/Advanced/create_upload_tutorial.py
+ - Searching and Editing Datasets: examples/Advanced/datasets_tutorial.py
+ - Searching and Creating Tasks: examples/Advanced/tasks_tutorial.py
+ - List, Download, and Upload Suites: examples/Advanced/suites_tutorial.py
+ - List, Download, and Upload Studies: examples/Advanced/study_tutorial.py
+ - Downloading Evaluation Results: examples/Advanced/fetch_evaluations_tutorial.py
+ - Configuring Logging: examples/Advanced/configure_logging.py
+
+
+ - Extensions: extensions.md
+ - Advanced User Guide: details.md
+ - API: reference/
+ - Contributing: contributing.md
+ - Developer Setup: developer_setup.md
+
+markdown_extensions:
+ - pymdownx.highlight:
+ anchor_linenums: true
+ - pymdownx.superfences
+ - attr_list
+ - admonition
+ - tables
+ - attr_list
+ - md_in_html
+ - toc:
+ permalink: "#"
+ - pymdownx.highlight:
+ anchor_linenums: true
+ - pymdownx.magiclink:
+ hide_protocol: true
+ repo_url_shortener: true
+ repo_url_shorthand: true
+ user: openml
+ repo: openml-python
+ - pymdownx.highlight
+ - pymdownx.inlinehilite
+ - pymdownx.snippets
+ - pymdownx.details
+ - pymdownx.tabbed:
+ alternate_style: true
+ - pymdownx.superfences:
+ custom_fences:
+ - name: mermaid
+ class: mermaid
+ format: !!python/name:pymdownx.superfences.fence_code_format
+ - pymdownx.emoji:
+ emoji_index: !!python/name:material.extensions.emoji.twemoji
+ emoji_generator: !!python/name:material.extensions.emoji.to_svg
+ - pymdownx.tabbed:
+ alternate_style: true
+
+extra:
+ version:
+ provider: mike
+ social:
+ - icon: fontawesome/brands/github
+ link: https://github.com/openml
+ - icon: fontawesome/brands/twitter
+ link: https://x.com/open_ml
+
+plugins:
+ - search
+ - autorefs
+ - section-index
+ # - mkdocstrings:
+ - mkdocstrings:
+ default_handler: python
+ enable_inventory: true
+ handlers:
+ python:
+ # paths: [openml]
+ options: # https://mkdocstrings.github.io/python/usage/
+ docstring_section_style: spacy
+ docstring_options:
+ ignore_init_summary: true
+ trim_doctest_flags: true
+ show_docstring_attributes: true
+ show_docstring_description: true
+ show_root_heading: true
+ show_root_toc_entry: true
+ show_object_full_path: false
+ show_root_members_full_path: false
+ signature_crossrefs: true
+ merge_init_into_class: true
+ show_symbol_type_heading: true
+ show_symbol_type_toc: true
+ docstring_style: numpy
+ inherited_members: true
+ show_if_no_docstring: false
+ show_bases: true
+ show_source: true
+ members_order: "alphabetical"
+ group_by_category: true
+ show_signature: true
+ separate_signature: true
+ show_signature_annotations: true
+ filters:
+ - "!^_[^_]"
+
+ - gen-files:
+ scripts:
+ - scripts/gen_ref_pages.py
+ - literate-nav:
+ nav_file: SUMMARY.md
+ - mkdocs-jupyter:
+ theme: light
+ - mike:
+ version_selector: true
+ css_dir: css
+ javascript_dir: js
+ canonical_version: latest
diff --git a/openml/__init__.py b/openml/__init__.py
index d51e6adcc..ae5db261f 100644
--- a/openml/__init__.py
+++ b/openml/__init__.py
@@ -9,22 +9,115 @@
* analyze experiments (uploaded by you and other collaborators) and conduct
meta studies
-In particular, this module implemts a python interface for the
+In particular, this module implements a python interface for the
`OpenML REST API `_
(`REST on wikipedia
-`_).
+`_).
"""
-from . import config
-from .datasets import OpenMLDataset
-from . import datasets
-from . import runs
-from .runs import OpenMLRun
-from .tasks import OpenMLTask, OpenMLSplit
+# License: BSD 3-Clause
+from __future__ import annotations
+
+from . import (
+ _api_calls,
+ config,
+ datasets,
+ evaluations,
+ exceptions,
+ extensions,
+ flows,
+ runs,
+ setups,
+ study,
+ tasks,
+ utils,
+)
+from .__version__ import __version__
+from .datasets import OpenMLDataFeature, OpenMLDataset
+from .evaluations import OpenMLEvaluation
from .flows import OpenMLFlow
+from .runs import OpenMLRun
+from .setups import OpenMLParameter, OpenMLSetup
+from .study import OpenMLBenchmarkSuite, OpenMLStudy
+from .tasks import (
+ OpenMLClassificationTask,
+ OpenMLClusteringTask,
+ OpenMLLearningCurveTask,
+ OpenMLRegressionTask,
+ OpenMLSplit,
+ OpenMLSupervisedTask,
+ OpenMLTask,
+)
+
+
+def populate_cache(
+ task_ids: list[int] | None = None,
+ dataset_ids: list[int | str] | None = None,
+ flow_ids: list[int] | None = None,
+ run_ids: list[int] | None = None,
+) -> None:
+ """
+ Populate a cache for offline and parallel usage of the OpenML connector.
+
+ Parameters
+ ----------
+ task_ids : iterable
+
+ dataset_ids : iterable
+
+ flow_ids : iterable
+
+ run_ids : iterable
+
+ Returns
+ -------
+ None
+ """
+ if task_ids is not None:
+ for task_id in task_ids:
+ tasks.functions.get_task(task_id)
+
+ if dataset_ids is not None:
+ for dataset_id in dataset_ids:
+ datasets.functions.get_dataset(dataset_id)
+
+ if flow_ids is not None:
+ for flow_id in flow_ids:
+ flows.functions.get_flow(flow_id)
+ if run_ids is not None:
+ for run_id in run_ids:
+ runs.functions.get_run(run_id)
-__version__ = "0.2.1"
-__all__ = ['OpenMLDataset', 'OpenMLRun', 'OpenMLSplit',
- 'datasets', 'OpenMLTask', 'OpenMLFlow', 'config', 'runs']
+__all__ = [
+ "OpenMLBenchmarkSuite",
+ "OpenMLClassificationTask",
+ "OpenMLClusteringTask",
+ "OpenMLDataFeature",
+ "OpenMLDataset",
+ "OpenMLEvaluation",
+ "OpenMLFlow",
+ "OpenMLLearningCurveTask",
+ "OpenMLParameter",
+ "OpenMLRegressionTask",
+ "OpenMLRun",
+ "OpenMLSetup",
+ "OpenMLSplit",
+ "OpenMLStudy",
+ "OpenMLSupervisedTask",
+ "OpenMLTask",
+ "__version__",
+ "_api_calls",
+ "config",
+ "datasets",
+ "evaluations",
+ "exceptions",
+ "extensions",
+ "flows",
+ "runs",
+ "setups",
+ "study",
+ "tasks",
+ "utils",
+]
diff --git a/openml/__version__.py b/openml/__version__.py
new file mode 100644
index 000000000..cf5a8535d
--- /dev/null
+++ b/openml/__version__.py
@@ -0,0 +1,8 @@
+"""Version information."""
+
+# License: BSD 3-Clause
+
+# The following line *must* be the last in the module, exactly as formatted:
+from __future__ import annotations
+
+__version__ = "0.16.0"
diff --git a/openml/_api_calls.py b/openml/_api_calls.py
index 2596f2f1c..5da635c70 100644
--- a/openml/_api_calls.py
+++ b/openml/_api_calls.py
@@ -1,98 +1,521 @@
-import io
-import os
+# License: BSD 3-Clause
+from __future__ import annotations
+
+import contextlib
+import hashlib
+import logging
+import math
+import random
+import shutil
+import time
+import urllib.parse
+import xml
+import zipfile
+from pathlib import Path
+
+import minio
import requests
-import arff
-import warnings
+import requests.utils
+import xmltodict
+from urllib3 import ProxyManager
from . import config
-from .exceptions import OpenMLServerError
+from .__version__ import __version__
+from .exceptions import (
+ OpenMLAuthenticationError,
+ OpenMLHashException,
+ OpenMLServerError,
+ OpenMLServerException,
+ OpenMLServerNoResult,
+)
+from .utils import ProgressBar
+
+_HEADERS = {"user-agent": f"openml-python/{__version__}"}
+
+DATA_TYPE = dict[str, str | int]
+FILE_ELEMENTS_TYPE = dict[str, str | tuple[str, str]]
+DATABASE_CONNECTION_ERRCODE = 107
+
+API_TOKEN_HELP_LINK = "https://openml.github.io/openml-python/latest/examples/Basics/introduction_tutorial/#authentication" # noqa: S105
+
+
+def _robot_delay(n: int) -> float:
+ wait = (1 / (1 + math.exp(-(n * 0.5 - 4)))) * 60
+ variation = random.gauss(0, wait / 10)
+ return max(1.0, wait + variation)
-def _perform_api_call(call, file_dictionary=None,
- file_elements=None, add_authentication=True):
+def _human_delay(n: int) -> float:
+ return max(1.0, n)
+
+
+def resolve_env_proxies(url: str) -> str | None:
+ """Attempt to find a suitable proxy for this url.
+
+ Relies on ``requests`` internals to remain consistent. To disable this from the
+ environment, please set the enviornment varialbe ``no_proxy="*"``.
+
+ Parameters
+ ----------
+ url : str
+ The url endpoint
+
+ Returns
+ -------
+ Optional[str]
+ The proxy url if found, else None
+ """
+ resolved_proxies = requests.utils.get_environ_proxies(url)
+ return requests.utils.select_proxy(url, resolved_proxies) # type: ignore
+
+
+def _create_url_from_endpoint(endpoint: str) -> str:
+ url = config.server
+ if not url.endswith("/"):
+ url += "/"
+ url += endpoint
+ return url.replace("=", "%3d")
+
+
+def _perform_api_call(
+ call: str,
+ request_method: str,
+ data: DATA_TYPE | None = None,
+ file_elements: FILE_ELEMENTS_TYPE | None = None,
+) -> str:
"""
Perform an API call at the OpenML server.
- return self._read_url(url, data=data, filePath=filePath,
- def _read_url(self, url, add_authentication=False, data=None, filePath=None):
Parameters
----------
call : str
The API call. For example data/list
- file_dictionary : dict
- Mapping of {filename: path} of files which should be uploaded to the
- server.
+ request_method : str
+ The HTTP request method to perform the API call with. Legal values:
+ - get (reading functions, api key optional)
+ - post (writing functions, generaly require api key)
+ - delete (deleting functions, require api key)
+ See REST api documentation which request method is applicable.
+ data : dict
+ Dictionary with post-request payload.
file_elements : dict
Mapping of {filename: str} of strings which should be uploaded as
files to the server.
- add_authentication : bool
- Whether to add authentication (api key) to the request.
Returns
-------
- return_code : int
- HTTP return code
return_value : str
Return value of the OpenML server
"""
- url = config.server
- if not url.endswith("/"):
- url += "/"
- url += call
- if file_dictionary is not None or file_elements is not None:
- return _read_url_files(url, file_dictionary=file_dictionary,
- file_elements=file_elements)
- return _read_url(url)
+ url = _create_url_from_endpoint(call)
+ logging.info("Starting [%s] request for the URL %s", request_method, url)
+ start = time.time()
+
+ if file_elements is not None:
+ if request_method != "post":
+ raise ValueError("request method must be post when file elements are present")
+ response = _read_url_files(url, data=data, file_elements=file_elements)
+ else:
+ response = __read_url(url, request_method, data)
+
+ __check_response(response, url, file_elements)
+
+ logging.info(
+ "%.7fs taken for [%s] request for the URL %s",
+ time.time() - start,
+ request_method,
+ url,
+ )
+ return response.text
+
+
+def _download_minio_file(
+ source: str,
+ destination: str | Path,
+ exists_ok: bool = True, # noqa: FBT002
+ proxy: str | None = "auto",
+) -> None:
+ """Download file ``source`` from a MinIO Bucket and store it at ``destination``.
+
+ Parameters
+ ----------
+ source : str
+ URL to a file in a MinIO bucket.
+ destination : str | Path
+ Path to store the file to, if a directory is provided the original filename is used.
+ exists_ok : bool, optional (default=True)
+ If False, raise FileExists if a file already exists in ``destination``.
+ proxy: str, optional (default = "auto")
+ The proxy server to use. By default it's "auto" which uses ``requests`` to
+ automatically find the proxy to use. Pass None or the environment variable
+ ``no_proxy="*"`` to disable proxies.
+ """
+ destination = Path(destination)
+ parsed_url = urllib.parse.urlparse(source)
+
+ # expect path format: /BUCKET/path/to/file.ext
+ bucket, object_name = parsed_url.path[1:].split("/", maxsplit=1)
+ if destination.is_dir():
+ destination = Path(destination, object_name)
+ if destination.is_file() and not exists_ok:
+ raise FileExistsError(f"File already exists in {destination}.")
+
+ if proxy == "auto":
+ proxy = resolve_env_proxies(parsed_url.geturl())
+
+ proxy_client = ProxyManager(proxy) if proxy else None
+
+ client = minio.Minio(endpoint=parsed_url.netloc, secure=False, http_client=proxy_client)
+ try:
+ client.fget_object(
+ bucket_name=bucket,
+ object_name=object_name,
+ file_path=str(destination),
+ progress=ProgressBar() if config.show_progress else None,
+ request_headers=_HEADERS,
+ )
+ if destination.is_file() and destination.suffix == ".zip":
+ with zipfile.ZipFile(destination, "r") as zip_ref:
+ zip_ref.extractall(destination.parent)
+
+ except minio.error.S3Error as e:
+ if e.message is not None and e.message.startswith("Object does not exist"):
+ raise FileNotFoundError(f"Object at '{source}' does not exist.") from e
+ # e.g. permission error, or a bucket does not exist (which is also interpreted as a
+ # permission error on minio level).
+ raise FileNotFoundError("Bucket does not exist or is private.") from e
+
+
+def _download_minio_bucket(source: str, destination: str | Path) -> None:
+ """Download file ``source`` from a MinIO Bucket and store it at ``destination``.
+
+ Does not redownload files which already exist.
+
+ Parameters
+ ----------
+ source : str
+ URL to a MinIO bucket.
+ destination : str | Path
+ Path to a directory to store the bucket content in.
+ """
+ destination = Path(destination)
+ parsed_url = urllib.parse.urlparse(source)
+
+ # expect path format: /BUCKET/path/to/file.ext
+ _, bucket, *prefixes, _file = parsed_url.path.split("/")
+ prefix = "/".join(prefixes)
+ client = minio.Minio(endpoint=parsed_url.netloc, secure=False)
-def _read_url_files(url, file_dictionary=None, file_elements=None):
- """do a post request to url with data None, file content of
- file_dictionary and sending file_elements as files"""
+ for file_object in client.list_objects(bucket, prefix=prefix, recursive=True):
+ if file_object.object_name is None:
+ raise ValueError(f"Object name is None for object {file_object!r}")
+ if file_object.etag is None:
+ raise ValueError(f"Object etag is None for object {file_object!r}")
- data = {}
- data['api_key'] = config.apikey
+ marker = destination / file_object.etag
+ if marker.exists():
+ continue
+
+ file_destination = destination / file_object.object_name.rsplit("/", 1)[1]
+ if (file_destination.parent / file_destination.stem).exists():
+ # Marker is missing but archive exists means the server archive changed, force a refresh
+ shutil.rmtree(file_destination.parent / file_destination.stem)
+
+ with contextlib.suppress(FileExistsError):
+ _download_minio_file(
+ source=source.rsplit("/", 1)[0] + "/" + file_object.object_name.rsplit("/", 1)[1],
+ destination=file_destination,
+ exists_ok=False,
+ )
+
+ if file_destination.is_file() and file_destination.suffix == ".zip":
+ file_destination.unlink()
+ marker.touch()
+
+
+def _download_text_file(
+ source: str,
+ output_path: str | Path | None = None,
+ md5_checksum: str | None = None,
+ exists_ok: bool = True, # noqa: FBT002
+ encoding: str = "utf8",
+) -> str | None:
+ """Download the text file at `source` and store it in `output_path`.
+
+ By default, do nothing if a file already exists in `output_path`.
+ The downloaded file can be checked against an expected md5 checksum.
+
+ Parameters
+ ----------
+ source : str
+ url of the file to be downloaded
+ output_path : str | Path | None (default=None)
+ full path, including filename, of where the file should be stored. If ``None``,
+ this function returns the downloaded file as string.
+ md5_checksum : str, optional (default=None)
+ If not None, should be a string of hexidecimal digits of the expected digest value.
+ exists_ok : bool, optional (default=True)
+ If False, raise an FileExistsError if there already exists a file at `output_path`.
+ encoding : str, optional (default='utf8')
+ The encoding with which the file should be stored.
+ """
+ if isinstance(output_path, str):
+ output_path = Path(output_path)
+
+ if output_path is not None and output_path.exists():
+ if not exists_ok:
+ raise FileExistsError
+
+ return None
+
+ logging.info("Starting [%s] request for the URL %s", "get", source)
+ start = time.time()
+ response = __read_url(source, request_method="get", md5_checksum=md5_checksum)
+ downloaded_file = response.text
+
+ if output_path is None:
+ logging.info(
+ "%.7fs taken for [%s] request for the URL %s",
+ time.time() - start,
+ "get",
+ source,
+ )
+ return downloaded_file
+
+ with output_path.open("w", encoding=encoding) as fh:
+ fh.write(downloaded_file)
+
+ logging.info(
+ "%.7fs taken for [%s] request for the URL %s",
+ time.time() - start,
+ "get",
+ source,
+ )
+ return None
+
+
+def _file_id_to_url(file_id: int, filename: str | None = None) -> str:
+ """
+ Presents the URL how to download a given file id
+ filename is optional
+ """
+ openml_url = config.server.split("/api/")
+ url = openml_url[0] + f"/data/download/{file_id!s}"
+ if filename is not None:
+ url += "/" + filename
+ return url
+
+
+def _read_url_files(
+ url: str,
+ data: DATA_TYPE | None = None,
+ file_elements: FILE_ELEMENTS_TYPE | None = None,
+) -> requests.Response:
+ """Do a post request to url with data
+ and sending file_elements as files
+ """
+ data = {} if data is None else data
+ data["api_key"] = config.apikey
if file_elements is None:
file_elements = {}
- if file_dictionary is not None:
- for key, path in file_dictionary.items():
- path = os.path.abspath(path)
- if os.path.exists(path):
- try:
- if key is 'dataset':
- # check if arff is valid?
- decoder = arff.ArffDecoder()
- with io.open(path, encoding='utf8') as fh:
- decoder.decode(fh, encode_nominal=True)
- except:
- raise ValueError("The file you have provided is not a valid arff file")
-
- file_elements[key] = open(path, 'rb')
-
- else:
- raise ValueError("File doesn't exist")
-
# Using requests.post sets header 'Accept-encoding' automatically to
# 'gzip,deflate'
- response = requests.post(url, data=data, files=file_elements)
- if response.status_code != 200:
- raise OpenMLServerError(response.text)
- if 'Content-Encoding' not in response.headers or \
- response.headers['Content-Encoding'] != 'gzip':
- warnings.warn('Received uncompressed content from OpenML for %s.' % url)
- return response.status_code, response.text
+ return _send_request(
+ request_method="post",
+ url=url,
+ data=data,
+ files=file_elements,
+ )
-def _read_url(url):
+def __read_url(
+ url: str,
+ request_method: str,
+ data: DATA_TYPE | None = None,
+ md5_checksum: str | None = None,
+) -> requests.Response:
+ data = {} if data is None else data
+ if config.apikey:
+ data["api_key"] = config.apikey
+ return _send_request(
+ request_method=request_method,
+ url=url,
+ data=data,
+ md5_checksum=md5_checksum,
+ )
- data = {}
- data['api_key'] = config.apikey
- # Using requests.post sets header 'Accept-encoding' automatically to
- # 'gzip,deflate'
- response = requests.post(url, data=data)
+def __is_checksum_equal(downloaded_file_binary: bytes, md5_checksum: str | None = None) -> bool:
+ if md5_checksum is None:
+ return True
+ md5 = hashlib.md5() # noqa: S324
+ md5.update(downloaded_file_binary)
+ md5_checksum_download = md5.hexdigest()
+ return md5_checksum == md5_checksum_download
+
+
+def _send_request( # noqa: C901, PLR0912
+ request_method: str,
+ url: str,
+ data: DATA_TYPE,
+ files: FILE_ELEMENTS_TYPE | None = None,
+ md5_checksum: str | None = None,
+) -> requests.Response:
+ n_retries = max(1, config.connection_n_retries)
+
+ response: requests.Response | None = None
+ delay_method = _human_delay if config.retry_policy == "human" else _robot_delay
+
+ # Error to raise in case of retrying too often. Will be set to the last observed exception.
+ retry_raise_e: Exception | None = None
+
+ with requests.Session() as session:
+ # Start at one to have a non-zero multiplier for the sleep
+ for retry_counter in range(1, n_retries + 1):
+ try:
+ if request_method == "get":
+ response = session.get(url, params=data, headers=_HEADERS)
+ elif request_method == "delete":
+ response = session.delete(url, params=data, headers=_HEADERS)
+ elif request_method == "post":
+ response = session.post(url, data=data, files=files, headers=_HEADERS)
+ else:
+ raise NotImplementedError()
+
+ __check_response(response=response, url=url, file_elements=files)
+
+ if request_method == "get" and not __is_checksum_equal(
+ response.text.encode("utf-8"), md5_checksum
+ ):
+ # -- Check if encoding is not UTF-8 perhaps
+ if __is_checksum_equal(response.content, md5_checksum):
+ raise OpenMLHashException(
+ f"Checksum of downloaded file is unequal to the expected checksum"
+ f"{md5_checksum} because the text encoding is not UTF-8 when "
+ f"downloading {url}. There might be a sever-sided issue with the file, "
+ "see: https://github.com/openml/openml-python/issues/1180.",
+ )
+
+ raise OpenMLHashException(
+ f"Checksum of downloaded file is unequal to the expected checksum "
+ f"{md5_checksum} when downloading {url}.",
+ )
+
+ return response
+ except OpenMLServerException as e:
+ # Propagate all server errors to the calling functions, except
+ # for 107 which represents a database connection error.
+ # These are typically caused by high server load,
+ # which means trying again might resolve the issue.
+ if e.code != DATABASE_CONNECTION_ERRCODE:
+ raise e
+ retry_raise_e = e
+ except xml.parsers.expat.ExpatError as e:
+ if request_method != "get" or retry_counter >= n_retries:
+ if response is not None:
+ extra = f"Status code: {response.status_code}\n{response.text}"
+ else:
+ extra = "No response retrieved."
+
+ raise OpenMLServerError(
+ f"Unexpected server error when calling {url}. Please contact the "
+ f"developers!\n{extra}"
+ ) from e
+ retry_raise_e = e
+ except (
+ requests.exceptions.ChunkedEncodingError,
+ requests.exceptions.ConnectionError,
+ requests.exceptions.SSLError,
+ OpenMLHashException,
+ ) as e:
+ retry_raise_e = e
+
+ # We can only be here if there was an exception
+ assert retry_raise_e is not None
+ if retry_counter >= n_retries:
+ raise retry_raise_e
+ delay = delay_method(retry_counter)
+ time.sleep(delay)
+
+ assert response is not None
+ return response
+
+
+def __check_response(
+ response: requests.Response,
+ url: str,
+ file_elements: FILE_ELEMENTS_TYPE | None,
+) -> None:
if response.status_code != 200:
- raise OpenMLServerError(response.text)
- if 'Content-Encoding' not in response.headers or \
- response.headers['Content-Encoding'] != 'gzip':
- warnings.warn('Received uncompressed content from OpenML for %s.' % url)
- return response.status_code, response.text
+ raise __parse_server_exception(response, url, file_elements=file_elements)
+ if "Content-Encoding" not in response.headers or response.headers["Content-Encoding"] != "gzip":
+ logging.warning(f"Received uncompressed content from OpenML for {url}.")
+
+
+def __parse_server_exception(
+ response: requests.Response,
+ url: str,
+ file_elements: FILE_ELEMENTS_TYPE | None,
+) -> OpenMLServerError:
+ if response.status_code == requests.codes.URI_TOO_LONG:
+ raise OpenMLServerError(f"URI too long! ({url})")
+
+ # OpenML has a sophisticated error system where information about failures is provided,
+ # in the response body itself.
+ # First, we need to parse it out.
+ try:
+ server_exception = xmltodict.parse(response.text)
+ except xml.parsers.expat.ExpatError as e:
+ raise e
+ except Exception as e:
+ # If we failed to parse it out, then something has gone wrong in the body we have sent back
+ # from the server and there is little extra information we can capture.
+ raise OpenMLServerError(
+ f"Unexpected server error when calling {url}. Please contact the developers!\n"
+ f"Status code: {response.status_code}\n{response.text}",
+ ) from e
+
+ # Now we can parse out the specific error codes that we return. These
+ # are in addition to the typical HTTP error codes, but encode more
+ # specific informtion. You can find these codes here:
+ # https://github.com/openml/OpenML/blob/develop/openml_OS/views/pages/api_new/v1/xml/pre.php
+ server_error = server_exception["oml:error"]
+ code = int(server_error["oml:code"])
+ message = server_error["oml:message"]
+ additional_information = server_error.get("oml:additional_information")
+ if code in [111, 372, 512, 500, 482, 542, 674]:
+ if additional_information:
+ full_message = f"{message} - {additional_information}"
+ else:
+ full_message = message
+
+ # 512 for runs, 372 for datasets, 500 for flows
+ # 482 for tasks, 542 for evaluations, 674 for setups
+ # 111 for dataset descriptions
+ return OpenMLServerNoResult(code=code, message=full_message, url=url)
+
+ # 163: failure to validate flow XML (https://www.openml.org/api_docs#!/flow/post_flow)
+ if code in [163] and file_elements is not None and "description" in file_elements:
+ # file_elements['description'] is the XML file description of the flow
+ full_message = "\n{}\n{} - {}".format(
+ file_elements["description"],
+ message,
+ additional_information,
+ )
+ else:
+ full_message = f"{message} - {additional_information}"
+
+ if code in [
+ 102, # flow/exists post
+ 137, # dataset post
+ 350, # dataset/42 delete
+ 310, # flow/ post
+ 320, # flow/42 delete
+ 400, # run/42 delete
+ 460, # task/42 delete
+ ]:
+ msg = f"The API call {url} requires authentication via an API key."
+ return OpenMLAuthenticationError(message=msg)
+
+ return OpenMLServerException(code=code, message=full_message, url=url)
diff --git a/openml/base.py b/openml/base.py
new file mode 100644
index 000000000..a282be8eb
--- /dev/null
+++ b/openml/base.py
@@ -0,0 +1,172 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+import re
+import webbrowser
+from abc import ABC, abstractmethod
+from collections.abc import Iterable, Sequence
+
+import xmltodict
+
+import openml._api_calls
+import openml.config
+
+from .utils import _get_rest_api_type_alias, _tag_openml_base
+
+
+class OpenMLBase(ABC):
+ """Base object for functionality that is shared across entities."""
+
+ def __repr__(self) -> str:
+ body_fields = self._get_repr_body_fields()
+ return self._apply_repr_template(body_fields)
+
+ @property
+ @abstractmethod
+ def id(self) -> int | None:
+ """The id of the entity, it is unique for its entity type."""
+
+ @property
+ def openml_url(self) -> str | None:
+ """The URL of the object on the server, if it was uploaded, else None."""
+ if self.id is None:
+ return None
+ return self.__class__.url_for_id(self.id)
+
+ @classmethod
+ def url_for_id(cls, id_: int) -> str:
+ """Return the OpenML URL for the object of the class entity with the given id."""
+ # Sample url for a flow: openml.org/f/123
+ return f"{openml.config.get_server_base_url()}/{cls._entity_letter()}/{id_}"
+
+ @classmethod
+ def _entity_letter(cls) -> str:
+ """Return the letter which represents the entity type in urls, e.g. 'f' for flow."""
+ # We take advantage of the class naming convention (OpenMLX),
+ # which holds for all entities except studies and tasks, which overwrite this method.
+ return cls.__name__.lower()[len("OpenML") :][0]
+
+ # TODO(eddiebergman): This would be much cleaner as an iterator...
+ @abstractmethod
+ def _get_repr_body_fields(self) -> Sequence[tuple[str, str | int | list[str] | None]]:
+ """Collect all information to display in the __repr__ body.
+
+ Returns
+ -------
+ body_fields : List[Tuple[str, Union[str, int, List[str]]]]
+ A list of (name, value) pairs to display in the body of the __repr__.
+ E.g.: [('metric', 'accuracy'), ('dataset', 'iris')]
+ If value is a List of str, then each item of the list will appear in a separate row.
+ """
+ # Should be implemented in the base class.
+
+ def _apply_repr_template(
+ self,
+ body_fields: Iterable[tuple[str, str | int | list[str] | None]],
+ ) -> str:
+ """Generates the header and formats the body for string representation of the object.
+
+ Parameters
+ ----------
+ body_fields: List[Tuple[str, str]]
+ A list of (name, value) pairs to display in the body of the __repr__.
+ """
+ # We add spaces between capitals, e.g. ClassificationTask -> Classification Task
+ name_with_spaces = re.sub(
+ r"(\w)([A-Z])",
+ r"\1 \2",
+ self.__class__.__name__[len("OpenML") :],
+ )
+ header_text = f"OpenML {name_with_spaces}"
+ header = f"{header_text}\n{'=' * len(header_text)}\n"
+
+ _body_fields: list[tuple[str, str | int | list[str]]] = [
+ (k, "None" if v is None else v) for k, v in body_fields
+ ]
+ longest_field_name_length = max(len(name) for name, _ in _body_fields)
+ field_line_format = f"{{:.<{longest_field_name_length}}}: {{}}"
+ body = "\n".join(field_line_format.format(name, value) for name, value in _body_fields)
+ return header + body
+
+ @abstractmethod
+ def _to_dict(self) -> dict[str, dict]:
+ """Creates a dictionary representation of self.
+
+ The return value will be used to create the upload xml file.
+ The xml file must have the tags in exactly the order of the object's xsd.
+ (see https://github.com/openml/OpenML/blob/master/openml_OS/views/pages/api_new/v1/xsd/).
+
+ Returns
+ -------
+ Thing represented as dict.
+ """
+ # Should be implemented in the base class.
+
+ def _to_xml(self) -> str:
+ """Generate xml representation of self for upload to server."""
+ dict_representation = self._to_dict()
+ xml_representation = xmltodict.unparse(dict_representation, pretty=True)
+
+ # A task may not be uploaded with the xml encoding specification:
+ #
+ _encoding_specification, xml_body = xml_representation.split("\n", 1)
+ return str(xml_body)
+
+ def _get_file_elements(self) -> openml._api_calls.FILE_ELEMENTS_TYPE:
+ """Get file_elements to upload to the server, called during Publish.
+
+ Derived child classes should overwrite this method as necessary.
+ The description field will be populated automatically if not provided.
+ """
+ return {}
+
+ @abstractmethod
+ def _parse_publish_response(self, xml_response: dict[str, str]) -> None:
+ """Parse the id from the xml_response and assign it to self."""
+
+ def publish(self) -> OpenMLBase:
+ """Publish the object on the OpenML server."""
+ file_elements = self._get_file_elements()
+
+ if "description" not in file_elements:
+ file_elements["description"] = self._to_xml()
+
+ call = f"{_get_rest_api_type_alias(self)}/"
+ response_text = openml._api_calls._perform_api_call(
+ call,
+ "post",
+ file_elements=file_elements,
+ )
+ xml_response = xmltodict.parse(response_text)
+
+ self._parse_publish_response(xml_response)
+ return self
+
+ def open_in_browser(self) -> None:
+ """Opens the OpenML web page corresponding to this object in your default browser."""
+ if self.openml_url is None:
+ raise ValueError(
+ "Cannot open element on OpenML.org when attribute `openml_url` is `None`",
+ )
+
+ webbrowser.open(self.openml_url)
+
+ def push_tag(self, tag: str) -> None:
+ """Annotates this entity with a tag on the server.
+
+ Parameters
+ ----------
+ tag : str
+ Tag to attach to the flow.
+ """
+ _tag_openml_base(self, tag)
+
+ def remove_tag(self, tag: str) -> None:
+ """Removes a tag from this entity on the server.
+
+ Parameters
+ ----------
+ tag : str
+ Tag to attach to the flow.
+ """
+ _tag_openml_base(self, tag, untag=True)
diff --git a/openml/cli.py b/openml/cli.py
new file mode 100644
index 000000000..c33578f6e
--- /dev/null
+++ b/openml/cli.py
@@ -0,0 +1,376 @@
+"""Command Line Interface for `openml` to configure its settings."""
+
+from __future__ import annotations
+
+import argparse
+import string
+import sys
+from collections.abc import Callable
+from pathlib import Path
+from urllib.parse import urlparse
+
+from openml import config
+from openml.__version__ import __version__
+
+
+def is_hex(string_: str) -> bool:
+ return all(c in string.hexdigits for c in string_)
+
+
+def looks_like_url(url: str) -> bool:
+ # There's no thorough url parser, but we only seem to use netloc.
+ try:
+ return bool(urlparse(url).netloc)
+ except Exception: # noqa: BLE001
+ return False
+
+
+def wait_until_valid_input(
+ prompt: str,
+ check: Callable[[str], str],
+ sanitize: Callable[[str], str] | None,
+) -> str:
+ """Asks `prompt` until an input is received which returns True for `check`.
+
+ Parameters
+ ----------
+ prompt: str
+ message to display
+ check: Callable[[str], str]
+ function to call with the given input, that provides an error message if the input is not
+ valid otherwise, and False-like otherwise.
+ sanitize: Callable[[str], str], optional
+ A function which attempts to sanitize the user input (e.g. auto-complete).
+
+ Returns
+ -------
+ valid input
+
+ """
+ while True:
+ response = input(prompt)
+ if sanitize:
+ response = sanitize(response)
+ error_message = check(response)
+ if error_message:
+ print(error_message, end="\n\n")
+ else:
+ return response
+
+
+def print_configuration() -> None:
+ file = config.determine_config_file_path()
+ header = f"File '{file}' contains (or defaults to):"
+ print(header)
+
+ max_key_length = max(map(len, config.get_config_as_dict()))
+ for field, value in config.get_config_as_dict().items():
+ print(f"{field.ljust(max_key_length)}: {value}")
+
+
+def verbose_set(field: str, value: str) -> None:
+ config.set_field_in_config_file(field, value)
+ print(f"{field} set to '{value}'.")
+
+
+def configure_apikey(value: str) -> None:
+ def check_apikey(apikey: str) -> str:
+ if len(apikey) != 32:
+ return f"The key should contain 32 characters but contains {len(apikey)}."
+ if not is_hex(apikey):
+ return "Some characters are not hexadecimal."
+ return ""
+
+ instructions = (
+ f"Your current API key is set to: '{config.apikey}'. "
+ "You can get an API key at https://new.openml.org. "
+ "You must create an account if you don't have one yet:\n"
+ " 1. Log in with the account.\n"
+ " 2. Navigate to the profile page (top right circle > Your Profile). \n"
+ " 3. Click the API Key button to reach the page with your API key.\n"
+ "If you have any difficulty following these instructions, let us know on Github."
+ )
+
+ configure_field(
+ field="apikey",
+ value=value,
+ check_with_message=check_apikey,
+ intro_message=instructions,
+ input_message="Please enter your API key:",
+ )
+
+
+def configure_server(value: str) -> None:
+ def check_server(server: str) -> str:
+ is_shorthand = server in ["test", "production_server"]
+ if is_shorthand or looks_like_url(server):
+ return ""
+ return "Must be 'test', 'production_server' or a url."
+
+ def replace_shorthand(server: str) -> str:
+ if server == "test":
+ return f"{config.TEST_SERVER_URL}/api/v1/xml"
+ if server == "production_server":
+ return "https://www.openml.org/api/v1/xml"
+ return server
+
+ configure_field(
+ field="server",
+ value=value,
+ check_with_message=check_server,
+ intro_message="Specify which server you wish to connect to.",
+ input_message="Specify a url or use 'test' or 'production_server' as a shorthand: ",
+ sanitize=replace_shorthand,
+ )
+
+
+def configure_cachedir(value: str) -> None:
+ def check_cache_dir(path: str) -> str:
+ _path = Path(path)
+ if _path.is_file():
+ return f"'{_path}' is a file, not a directory."
+
+ expanded = _path.expanduser()
+ if not expanded.is_absolute():
+ return f"'{_path}' is not absolute (even after expanding '~')."
+
+ if not expanded.exists():
+ try:
+ expanded.mkdir()
+ except PermissionError:
+ return f"'{path}' does not exist and there are not enough permissions to create it."
+
+ return ""
+
+ configure_field(
+ field="cachedir",
+ value=value,
+ check_with_message=check_cache_dir,
+ intro_message="Configuring the cache directory. It can not be a relative path.",
+ input_message="Specify the directory to use (or create) as cache directory: ",
+ )
+
+
+def configure_connection_n_retries(value: str) -> None:
+ def valid_connection_retries(n: str) -> str:
+ if not n.isdigit():
+ return f"'{n}' is not a valid positive integer."
+ if int(n) <= 0:
+ return "connection_n_retries must be positive."
+ return ""
+
+ configure_field(
+ field="connection_n_retries",
+ value=value,
+ check_with_message=valid_connection_retries,
+ intro_message="Configuring the number of times to attempt to connect to the OpenML Server",
+ input_message="Enter a positive integer: ",
+ )
+
+
+def configure_avoid_duplicate_runs(value: str) -> None:
+ def is_python_bool(bool_: str) -> str:
+ if bool_ in ["True", "False"]:
+ return ""
+ return "Must be 'True' or 'False' (mind the capital)."
+
+ def autocomplete_bool(bool_: str) -> str:
+ if bool_.lower() in ["n", "no", "f", "false", "0"]:
+ return "False"
+ if bool_.lower() in ["y", "yes", "t", "true", "1"]:
+ return "True"
+ return bool_
+
+ intro_message = (
+ "If set to True, when `run_flow_on_task` or similar methods are called a lookup is "
+ "performed to see if there already exists such a run on the server. "
+ "If so, download those results instead. "
+ "If set to False, runs will always be executed."
+ )
+
+ configure_field(
+ field="avoid_duplicate_runs",
+ value=value,
+ check_with_message=is_python_bool,
+ intro_message=intro_message,
+ input_message="Enter 'True' or 'False': ",
+ sanitize=autocomplete_bool,
+ )
+
+
+def configure_verbosity(value: str) -> None:
+ def is_zero_through_two(verbosity: str) -> str:
+ if verbosity in ["0", "1", "2"]:
+ return ""
+ return "Must be '0', '1' or '2'."
+
+ intro_message = (
+ "Set the verbosity of log messages which should be shown by openml-python."
+ " 0: normal output (warnings and errors)"
+ " 1: info output (some high-level progress output)"
+ " 2: debug output (detailed information (for developers))"
+ )
+
+ configure_field(
+ field="verbosity",
+ value=value,
+ check_with_message=is_zero_through_two,
+ intro_message=intro_message,
+ input_message="Enter '0', '1' or '2': ",
+ )
+
+
+def configure_retry_policy(value: str) -> None:
+ def is_known_policy(policy: str) -> str:
+ if policy in ["human", "robot"]:
+ return ""
+ return "Must be 'human' or 'robot'."
+
+ def autocomplete_policy(policy: str) -> str:
+ for option in ["human", "robot"]:
+ if option.startswith(policy.lower()):
+ return option
+ return policy
+
+ intro_message = (
+ "Set the retry policy which determines how to react if the server is unresponsive."
+ "We recommend 'human' for interactive usage and 'robot' for scripts."
+ "'human': try a few times in quick succession, less reliable but quicker response."
+ "'robot': try many times with increasing intervals, more reliable but slower response."
+ )
+
+ configure_field(
+ field="retry_policy",
+ value=value,
+ check_with_message=is_known_policy,
+ intro_message=intro_message,
+ input_message="Enter 'human' or 'robot': ",
+ sanitize=autocomplete_policy,
+ )
+
+
+def configure_field( # noqa: PLR0913
+ field: str,
+ value: None | str,
+ check_with_message: Callable[[str], str],
+ intro_message: str,
+ input_message: str,
+ sanitize: Callable[[str], str] | None = None,
+) -> None:
+ """Configure `field` with `value`. If `value` is None ask the user for input.
+
+ `value` and user input are first corrected/auto-completed with `convert_value` if provided,
+ then validated with `check_with_message` function.
+ If the user input a wrong value in interactive mode, the user gets to input a new value.
+ The new valid value is saved in the openml configuration file.
+ In case an invalid `value` is supplied directly (non-interactive), no changes are made.
+
+ Parameters
+ ----------
+ field: str
+ Field to set.
+ value: str, None
+ Value to field to. If `None` will ask user for input.
+ check_with_message: Callable[[str], str]
+ Function which validates `value` or user input, and returns either an error message if it
+ is invalid, or a False-like value if `value` is valid.
+ intro_message: str
+ Message that is printed once if user input is requested (e.g. instructions).
+ input_message: str
+ Message that comes with the input prompt.
+ sanitize: Union[Callable[[str], str], None]
+ A function to convert user input to 'more acceptable' input, e.g. for auto-complete.
+ If no correction of user input is possible, return the original value.
+ If no function is provided, don't attempt to correct/auto-complete input.
+ """
+ if value is not None:
+ if sanitize:
+ value = sanitize(value)
+ malformed_input = check_with_message(value)
+ if malformed_input:
+ print(malformed_input)
+ sys.exit()
+ else:
+ print(intro_message)
+ value = wait_until_valid_input(
+ prompt=input_message,
+ check=check_with_message,
+ sanitize=sanitize,
+ )
+ verbose_set(field, value)
+
+
+def configure(args: argparse.Namespace) -> None:
+ """Calls the right submenu(s) to edit `args.field` in the configuration file."""
+ set_functions = {
+ "apikey": configure_apikey,
+ "server": configure_server,
+ "cachedir": configure_cachedir,
+ "retry_policy": configure_retry_policy,
+ "connection_n_retries": configure_connection_n_retries,
+ "avoid_duplicate_runs": configure_avoid_duplicate_runs,
+ "verbosity": configure_verbosity,
+ }
+
+ def not_supported_yet(_: str) -> None:
+ print(f"Setting '{args.field}' is not supported yet.")
+
+ if args.field not in ["all", "none"]:
+ set_functions.get(args.field, not_supported_yet)(args.value)
+ else:
+ if args.value is not None:
+ print(f"Can not set value ('{args.value}') when field is specified as '{args.field}'.")
+ sys.exit()
+ print_configuration()
+
+ if args.field == "all":
+ for set_field_function in set_functions.values():
+ set_field_function(args.value)
+
+
+def main() -> None:
+ subroutines = {"configure": configure}
+
+ parser = argparse.ArgumentParser()
+ # Add a global --version flag to display installed version and exit
+ parser.add_argument(
+ "--version",
+ action="version",
+ version=f"%(prog)s {__version__}",
+ help="Show the OpenML version and exit",
+ )
+ subparsers = parser.add_subparsers(dest="subroutine")
+
+ parser_configure = subparsers.add_parser(
+ "configure",
+ description="Set or read variables in your configuration file. For more help also see "
+ "'https://openml.github.io/openml-python/main/usage.html#configuration'.",
+ )
+
+ configurable_fields = [f for f in config._defaults if f not in ["max_retries"]]
+
+ parser_configure.add_argument(
+ "field",
+ type=str,
+ choices=[*configurable_fields, "all", "none"],
+ default="all",
+ nargs="?",
+ help="The field you wish to edit. "
+ "Choosing 'all' lets you configure all fields one by one. "
+ "Choosing 'none' will print out the current configuration.",
+ )
+
+ parser_configure.add_argument(
+ "value",
+ type=str,
+ default=None,
+ nargs="?",
+ help="The value to set the FIELD to.",
+ )
+
+ args = parser.parse_args()
+ subroutines.get(args.subroutine, lambda _: parser.print_help())(args)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/openml/config.py b/openml/config.py
index 3a38e4f0b..638b45650 100644
--- a/openml/config.py
+++ b/openml/config.py
@@ -1,172 +1,529 @@
-"""
-Stores module level information like the API key, cache director, private
-directory and the server.
-"""
-import os
-import sys
+"""Store module level information like the API key, cache directory and the server"""
+
+# License: BSD 3-Clause
+from __future__ import annotations
+
+import configparser
import logging
+import logging.handlers
+import os
+import platform
+import shutil
+import warnings
+from collections.abc import Iterator
+from contextlib import contextmanager
+from io import StringIO
+from pathlib import Path
+from typing import Any, Literal, cast
+from typing_extensions import TypedDict
+from urllib.parse import urlparse
logger = logging.getLogger(__name__)
-logging.basicConfig(
- format='[%(levelname)s] [%(asctime)s:%(name)s] %('
- 'message)s', datefmt='%H:%M:%S')
+openml_logger = logging.getLogger("openml")
+console_handler: logging.StreamHandler | None = None
+file_handler: logging.handlers.RotatingFileHandler | None = None
+
+OPENML_CACHE_DIR_ENV_VAR = "OPENML_CACHE_DIR"
+OPENML_SKIP_PARQUET_ENV_VAR = "OPENML_SKIP_PARQUET"
+OPENML_TEST_SERVER_ADMIN_KEY_ENV_VAR = "OPENML_TEST_SERVER_ADMIN_KEY"
+_TEST_SERVER_NORMAL_USER_KEY = "normaluser"
+
+TEST_SERVER_URL = "https://test.openml.org"
-server = "https://www.openml.org/api/v1/xml"
-apikey = ""
-cachedir = ""
-privatedir = ""
+
+class _Config(TypedDict):
+ apikey: str
+ server: str
+ cachedir: Path
+ avoid_duplicate_runs: bool
+ retry_policy: Literal["human", "robot"]
+ connection_n_retries: int
+ show_progress: bool
+
+
+def _create_log_handlers(create_file_handler: bool = True) -> None: # noqa: FBT002
+ """Creates but does not attach the log handlers."""
+ global console_handler, file_handler # noqa: PLW0603
+ if console_handler is not None or file_handler is not None:
+ logger.debug("Requested to create log handlers, but they are already created.")
+ return
+
+ message_format = "[%(levelname)s] [%(asctime)s:%(name)s] %(message)s"
+ output_formatter = logging.Formatter(message_format, datefmt="%H:%M:%S")
+
+ console_handler = logging.StreamHandler()
+ console_handler.setFormatter(output_formatter)
+
+ if create_file_handler:
+ one_mb = 2**20
+ log_path = _root_cache_directory / "openml_python.log"
+ file_handler = logging.handlers.RotatingFileHandler(
+ log_path,
+ maxBytes=one_mb,
+ backupCount=1,
+ delay=True,
+ )
+ file_handler.setFormatter(output_formatter)
+
+
+def _convert_log_levels(log_level: int) -> tuple[int, int]:
+ """Converts a log level that's either defined by OpenML/Python to both specifications."""
+ # OpenML verbosity level don't match Python values directly:
+ openml_to_python = {0: logging.WARNING, 1: logging.INFO, 2: logging.DEBUG}
+ python_to_openml = {
+ logging.DEBUG: 2,
+ logging.INFO: 1,
+ logging.WARNING: 0,
+ logging.CRITICAL: 0,
+ logging.ERROR: 0,
+ }
+ # Because the dictionaries share no keys, we use `get` to convert as necessary:
+ openml_level = python_to_openml.get(log_level, log_level)
+ python_level = openml_to_python.get(log_level, log_level)
+ return openml_level, python_level
+
+
+def _set_level_register_and_store(handler: logging.Handler, log_level: int) -> None:
+ """Set handler log level, register it if needed, save setting to config file if specified."""
+ _oml_level, py_level = _convert_log_levels(log_level)
+ handler.setLevel(py_level)
+
+ if openml_logger.level > py_level or openml_logger.level == logging.NOTSET:
+ openml_logger.setLevel(py_level)
+
+ if handler not in openml_logger.handlers:
+ openml_logger.addHandler(handler)
+
+
+def set_console_log_level(console_output_level: int) -> None:
+ """Set console output to the desired level and register it with openml logger if needed."""
+ global console_handler # noqa: PLW0602
+ assert console_handler is not None
+ _set_level_register_and_store(console_handler, console_output_level)
+
+
+def set_file_log_level(file_output_level: int) -> None:
+ """Set file output to the desired level and register it with openml logger if needed."""
+ global file_handler # noqa: PLW0602
+ assert file_handler is not None
+ _set_level_register_and_store(file_handler, file_output_level)
+
+
+# Default values (see also https://github.com/openml/OpenML/wiki/Client-API-Standards)
+_user_path = Path("~").expanduser().absolute()
+
+
+def _resolve_default_cache_dir() -> Path:
+ user_defined_cache_dir = os.environ.get(OPENML_CACHE_DIR_ENV_VAR)
+ if user_defined_cache_dir is not None:
+ return Path(user_defined_cache_dir)
+
+ if platform.system().lower() != "linux":
+ return _user_path / ".openml"
+
+ xdg_cache_home = os.environ.get("XDG_CACHE_HOME")
+ if xdg_cache_home is None:
+ return Path("~", ".cache", "openml")
+
+ # This is the proper XDG_CACHE_HOME directory, but
+ # we unfortunately had a problem where we used XDG_CACHE_HOME/org,
+ # we check heuristically if this old directory still exists and issue
+ # a warning if it does. There's too much data to move to do this for the user.
+
+ # The new cache directory exists
+ cache_dir = Path(xdg_cache_home) / "openml"
+ if cache_dir.exists():
+ return cache_dir
+
+ # The old cache directory *does not* exist
+ heuristic_dir_for_backwards_compat = Path(xdg_cache_home) / "org" / "openml"
+ if not heuristic_dir_for_backwards_compat.exists():
+ return cache_dir
+
+ root_dir_to_delete = Path(xdg_cache_home) / "org"
+ openml_logger.warning(
+ "An old cache directory was found at '%s'. This directory is no longer used by "
+ "OpenML-Python. To silence this warning you would need to delete the old cache "
+ "directory. The cached files will then be located in '%s'.",
+ root_dir_to_delete,
+ cache_dir,
+ )
+ return Path(xdg_cache_home)
+
+
+_defaults: _Config = {
+ "apikey": "",
+ "server": "https://www.openml.org/api/v1/xml",
+ "cachedir": _resolve_default_cache_dir(),
+ "avoid_duplicate_runs": False,
+ "retry_policy": "human",
+ "connection_n_retries": 5,
+ "show_progress": False,
+}
+
+# Default values are actually added here in the _setup() function which is
+# called at the end of this module
+server = _defaults["server"]
-if sys.version_info[0] < 3:
- import ConfigParser as configparser
- from StringIO import StringIO
-else:
- import configparser
- from io import StringIO
+def get_server_base_url() -> str:
+ """Return the base URL of the currently configured server.
+
+ Turns ``"https://api.openml.org/api/v1/xml"`` in ``"https://www.openml.org/"``
+ and ``"https://test.openml.org/api/v1/xml"`` in ``"https://test.openml.org/"``
+ Returns
+ -------
+ str
+ """
+ domain, _path = server.split("/api", maxsplit=1)
+ return domain.replace("api", "www")
+
+
+apikey: str = _defaults["apikey"]
+show_progress: bool = _defaults["show_progress"]
+# The current cache directory (without the server name)
+_root_cache_directory: Path = Path(_defaults["cachedir"])
+avoid_duplicate_runs = _defaults["avoid_duplicate_runs"]
+
+retry_policy: Literal["human", "robot"] = _defaults["retry_policy"]
+connection_n_retries: int = _defaults["connection_n_retries"]
+
+
+def set_retry_policy(value: Literal["human", "robot"], n_retries: int | None = None) -> None:
+ global retry_policy # noqa: PLW0603
+ global connection_n_retries # noqa: PLW0603
+ default_retries_by_policy = {"human": 5, "robot": 50}
+
+ if value not in default_retries_by_policy:
+ raise ValueError(
+ f"Detected retry_policy '{value}' but must be one of "
+ f"{list(default_retries_by_policy.keys())}",
+ )
+ if n_retries is not None and not isinstance(n_retries, int):
+ raise TypeError(f"`n_retries` must be of type `int` or `None` but is `{type(n_retries)}`.")
+
+ if isinstance(n_retries, int) and n_retries < 1:
+ raise ValueError(f"`n_retries` is '{n_retries}' but must be positive.")
+
+ retry_policy = value
+ connection_n_retries = default_retries_by_policy[value] if n_retries is None else n_retries
+
+
+class ConfigurationForExamples:
+ """Allows easy switching to and from a test configuration, used for examples."""
+
+ _last_used_server = None
+ _last_used_key = None
+ _start_last_called = False
+ _test_server = f"{TEST_SERVER_URL}/api/v1/xml"
+ _test_apikey = _TEST_SERVER_NORMAL_USER_KEY
+
+ @classmethod
+ def start_using_configuration_for_example(cls) -> None:
+ """Sets the configuration to connect to the test server with valid apikey.
+
+ To configuration as was before this call is stored, and can be recovered
+ by using the `stop_use_example_configuration` method.
+ """
+ global server # noqa: PLW0603
+ global apikey # noqa: PLW0603
+
+ if cls._start_last_called and server == cls._test_server and apikey == cls._test_apikey:
+ # Method is called more than once in a row without modifying the server or apikey.
+ # We don't want to save the current test configuration as a last used configuration.
+ return
+
+ cls._last_used_server = server
+ cls._last_used_key = apikey
+ cls._start_last_called = True
+
+ # Test server key for examples
+ server = cls._test_server
+ apikey = cls._test_apikey
+ warnings.warn(
+ f"Switching to the test server {server} to not upload results to the live server. "
+ "Using the test server may result in reduced performance of the API!",
+ stacklevel=2,
+ )
+
+ @classmethod
+ def stop_using_configuration_for_example(cls) -> None:
+ """Return to configuration as it was before `start_use_example_configuration`."""
+ if not cls._start_last_called:
+ # We don't want to allow this because it will (likely) result in the `server` and
+ # `apikey` variables being set to None.
+ raise RuntimeError(
+ "`stop_use_example_configuration` called without a saved config."
+ "`start_use_example_configuration` must be called first.",
+ )
+
+ global server # noqa: PLW0603
+ global apikey # noqa: PLW0603
+
+ server = cast("str", cls._last_used_server)
+ apikey = cast("str", cls._last_used_key)
+ cls._start_last_called = False
+
+
+def _handle_xdg_config_home_backwards_compatibility(
+ xdg_home: str,
+) -> Path:
+ # NOTE(eddiebergman): A previous bug results in the config
+ # file being located at `${XDG_CONFIG_HOME}/config` instead
+ # of `${XDG_CONFIG_HOME}/openml/config`. As to maintain backwards
+ # compatibility, where users may already may have had a configuration,
+ # we copy it over an issue a warning until it's deleted.
+ # As a heurisitic to ensure that it's "our" config file, we try parse it first.
+ config_dir = Path(xdg_home) / "openml"
+
+ backwards_compat_config_file = Path(xdg_home) / "config"
+ if not backwards_compat_config_file.exists():
+ return config_dir
+
+ # If it errors, that's a good sign it's not ours and we can
+ # safely ignore it, jumping out of this block. This is a heurisitc
+ try:
+ _parse_config(backwards_compat_config_file)
+ except Exception: # noqa: BLE001
+ return config_dir
-def _setup():
+ # Looks like it's ours, lets try copy it to the correct place
+ correct_config_location = config_dir / "config"
+ try:
+ # We copy and return the new copied location
+ shutil.copy(backwards_compat_config_file, correct_config_location)
+ openml_logger.warning(
+ "An openml configuration file was found at the old location "
+ f"at {backwards_compat_config_file}. We have copied it to the new "
+ f"location at {correct_config_location}. "
+ "\nTo silence this warning please verify that the configuration file "
+ f"at {correct_config_location} is correct and delete the file at "
+ f"{backwards_compat_config_file}."
+ )
+ return config_dir
+ except Exception as e: # noqa: BLE001
+ # We failed to copy and its ours, return the old one.
+ openml_logger.warning(
+ "While attempting to perform a backwards compatible fix, we "
+ f"failed to copy the openml config file at "
+ f"{backwards_compat_config_file}' to {correct_config_location}"
+ f"\n{type(e)}: {e}",
+ "\n\nTo silence this warning, please copy the file "
+ "to the new location and delete the old file at "
+ f"{backwards_compat_config_file}.",
+ )
+ return backwards_compat_config_file
+
+
+def determine_config_file_path() -> Path:
+ if platform.system().lower() == "linux":
+ xdg_home = os.environ.get("XDG_CONFIG_HOME")
+ if xdg_home is not None:
+ config_dir = _handle_xdg_config_home_backwards_compatibility(xdg_home)
+ else:
+ config_dir = Path("~", ".config", "openml")
+ else:
+ config_dir = Path("~") / ".openml"
+
+ # Still use os.path.expanduser to trigger the mock in the unit test
+ config_dir = Path(config_dir).expanduser().resolve()
+ return config_dir / "config"
+
+
+def _setup(config: _Config | None = None) -> None:
"""Setup openml package. Called on first import.
Reads the config file and sets up apikey, server, cache appropriately.
key and server can be set by the user simply using
openml.config.apikey = THEIRKEY
openml.config.server = SOMESERVER
- The cache dir needs to be set up calling set_cache_directory
- because it needs some setup.
We could also make it a property but that's less clear.
"""
- global apikey
- global server
- # read config file, create cache directory
- try:
- os.mkdir(os.path.expanduser('~/.openml'))
- except (IOError, OSError):
- # TODO add debug information
- pass
- config = _parse_config()
- apikey = config.get('FAKE_SECTION', 'apikey')
- server = config.get('FAKE_SECTION', 'server')
- private_dir = config.get('FAKE_SECTION', 'private_directory')
- cache_dir = config.get('FAKE_SECTION', 'cachedir')
- set_cache_directory(cache_dir, private_dir)
+ global apikey # noqa: PLW0603
+ global server # noqa: PLW0603
+ global _root_cache_directory # noqa: PLW0603
+ global avoid_duplicate_runs # noqa: PLW0603
+ global show_progress # noqa: PLW0603
+ config_file = determine_config_file_path()
+ config_dir = config_file.parent
-def set_cache_directory(cachedir, privatedir=None):
- """Set module-wide cache directory.
-
- Sets the cache directory into which to download datasets, tasks etc.
- Also sets the private directory for storing local datasets.
-
- Parameters
- ----------
- cachedir : string
- Path to use as cache directory.
-
- privatedir : string
- Path containing private datasets, tasks, etc.
-
- See also
- --------
- get_cache_directory
- get_private_directory
- """
- if privatedir is None:
- privatedir = cachedir
-
- global _cachedir
- global _privatedir
- _cachedir = cachedir
- _privatedir = privatedir
-
- # Set up the cache directories
- dataset_cache_dir = os.path.join(cachedir, "datasets")
- task_cache_dir = os.path.join(cachedir, "tasks")
- run_cache_dir = os.path.join(cachedir, 'runs')
-
- # Set up the private directory
- _private_directory_datasets = os.path.join(
- privatedir, "datasets")
- _private_directory_tasks = os.path.join(
- privatedir, "tasks")
- _private_directory_runs = os.path.join(
- privatedir, "runs")
-
- for dir_ in [cachedir, dataset_cache_dir,
- task_cache_dir, run_cache_dir,
- privatedir,
- _private_directory_datasets,
- _private_directory_tasks,
- _private_directory_runs]:
- if not os.path.exists(dir_) and not os.path.isdir(dir_):
- os.mkdir(dir_)
-
-
-def _parse_config():
- """Parse the config file, set up defaults.
- """
- defaults = {'apikey': apikey,
- 'server': server,
- 'verbosity': 0,
- 'cachedir': os.path.expanduser('~/.openml/cache'),
- 'private_directory': os.path.expanduser('~/.openml/private')}
-
- config_file = os.path.expanduser('~/.openml/config')
- config = configparser.RawConfigParser(defaults=defaults)
-
- if not os.path.exists(config_file):
- # Create an empty config file if there was none so far
- fh = open(config_file, "w")
- fh.close()
- logger.info("Could not find a configuration file at %s. Going to "
- "create an empty file there." % config_file)
+ # read config file, create directory for config file
+ try:
+ if not config_dir.exists():
+ config_dir.mkdir(exist_ok=True, parents=True)
+ except PermissionError:
+ openml_logger.warning(
+ f"No permission to create OpenML directory at {config_dir}!"
+ " This can result in OpenML-Python not working properly."
+ )
+
+ if config is None:
+ config = _parse_config(config_file)
+
+ avoid_duplicate_runs = config["avoid_duplicate_runs"]
+ apikey = config["apikey"]
+ server = config["server"]
+ show_progress = config["show_progress"]
+ n_retries = int(config["connection_n_retries"])
+
+ set_retry_policy(config["retry_policy"], n_retries)
+
+ user_defined_cache_dir = os.environ.get(OPENML_CACHE_DIR_ENV_VAR)
+ if user_defined_cache_dir is not None:
+ short_cache_dir = Path(user_defined_cache_dir)
+ else:
+ short_cache_dir = Path(config["cachedir"])
+ _root_cache_directory = short_cache_dir.expanduser().resolve()
try:
- # Cheat the ConfigParser module by adding a fake section header
- config_file_ = StringIO()
- config_file_.write("[FAKE_SECTION]\n")
- with open(config_file) as fh:
+ cache_exists = _root_cache_directory.exists()
+ # create the cache subdirectory
+ if not cache_exists:
+ _root_cache_directory.mkdir(exist_ok=True, parents=True)
+ _create_log_handlers()
+ except PermissionError:
+ openml_logger.warning(
+ f"No permission to create OpenML directory at {_root_cache_directory}!"
+ " This can result in OpenML-Python not working properly."
+ )
+ _create_log_handlers(create_file_handler=False)
+
+
+def set_field_in_config_file(field: str, value: Any) -> None:
+ """Overwrites the `field` in the configuration file with the new `value`."""
+ if field not in _defaults:
+ raise ValueError(f"Field '{field}' is not valid and must be one of '{_defaults.keys()}'.")
+
+ # TODO(eddiebergman): This use of globals has gone too far
+ globals()[field] = value
+ config_file = determine_config_file_path()
+ config = _parse_config(config_file)
+ with config_file.open("w") as fh:
+ for f in _defaults:
+ # We can't blindly set all values based on globals() because when the user
+ # sets it through config.FIELD it should not be stored to file.
+ # There doesn't seem to be a way to avoid writing defaults to file with configparser,
+ # because it is impossible to distinguish from an explicitly set value that matches
+ # the default value, to one that was set to its default because it was omitted.
+ value = globals()[f] if f == field else config.get(f) # type: ignore
+ if value is not None:
+ fh.write(f"{f} = {value}\n")
+
+
+def _parse_config(config_file: str | Path) -> _Config:
+ """Parse the config file, set up defaults."""
+ config_file = Path(config_file)
+ config = configparser.RawConfigParser(defaults=_defaults) # type: ignore
+
+ # The ConfigParser requires a [SECTION_HEADER], which we do not expect in our config file.
+ # Cheat the ConfigParser module by adding a fake section header
+ config_file_ = StringIO()
+ config_file_.write("[FAKE_SECTION]\n")
+ try:
+ with config_file.open("r") as fh:
for line in fh:
config_file_.write(line)
- config_file_.seek(0)
- config.readfp(config_file_)
+ except FileNotFoundError:
+ logger.info("No config file found at %s, using default configuration.", config_file)
except OSError as e:
- logging.info("Error opening file %s: %s" %
- config_file, e.message)
- return config
-
-
-def get_cache_directory():
+ logger.info("Error opening file %s: %s", config_file, e.args[0])
+ config_file_.seek(0)
+ config.read_file(config_file_)
+ configuration = dict(config.items("FAKE_SECTION"))
+ for boolean_field in ["avoid_duplicate_runs", "show_progress"]:
+ if isinstance(config["FAKE_SECTION"][boolean_field], str):
+ configuration[boolean_field] = config["FAKE_SECTION"].getboolean(boolean_field) # type: ignore
+ return configuration # type: ignore
+
+
+def get_config_as_dict() -> _Config:
+ return {
+ "apikey": apikey,
+ "server": server,
+ "cachedir": _root_cache_directory,
+ "avoid_duplicate_runs": avoid_duplicate_runs,
+ "connection_n_retries": connection_n_retries,
+ "retry_policy": retry_policy,
+ "show_progress": show_progress,
+ }
+
+
+# NOTE: For backwards compatibility, we keep the `str`
+def get_cache_directory() -> str:
"""Get the current cache directory.
+ This gets the cache directory for the current server relative
+ to the root cache directory that can be set via
+ ``set_root_cache_directory()``. The cache directory is the
+ ``root_cache_directory`` with additional information on which
+ subdirectory to use based on the server name. By default it is
+ ``root_cache_directory / org / openml / www`` for the standard
+ OpenML.org server and is defined as
+ ``root_cache_directory / top-level domain / second-level domain /
+ hostname``
+ ```
+
Returns
-------
cachedir : string
The current cache directory.
- See also
- --------
- set_cache_directory
- get_private_directory
"""
- return _cachedir
+ url_suffix = urlparse(server).netloc
+ url_parts = url_suffix.replace(":", "_").split(".")[::-1]
+ reversed_url_suffix = os.sep.join(url_parts) # noqa: PTH118
+ return os.path.join(_root_cache_directory, reversed_url_suffix) # noqa: PTH118
-def get_private_directory():
- """Get the current private directory.
+def set_root_cache_directory(root_cache_directory: str | Path) -> None:
+ """Set module-wide base cache directory.
- Returns
- -------
- privatecir : string
- The current private directory.
+ Sets the root cache directory, wherin the cache directories are
+ created to store content from different OpenML servers. For example,
+ by default, cached data for the standard OpenML.org server is stored
+ at ``root_cache_directory / org / openml / www``, and the general
+ pattern is ``root_cache_directory / top-level domain / second-level
+ domain / hostname``.
- See also
+ Parameters
+ ----------
+ root_cache_directory : string
+ Path to use as cache directory.
+
+ See Also
--------
- set_cache_directory
get_cache_directory
"""
- return _privatedir
+ global _root_cache_directory # noqa: PLW0603
+ _root_cache_directory = Path(root_cache_directory)
+
+
+start_using_configuration_for_example = (
+ ConfigurationForExamples.start_using_configuration_for_example
+)
+stop_using_configuration_for_example = ConfigurationForExamples.stop_using_configuration_for_example
+
+
+@contextmanager
+def overwrite_config_context(config: dict[str, Any]) -> Iterator[_Config]:
+ """A context manager to temporarily override variables in the configuration."""
+ existing_config = get_config_as_dict()
+ merged_config = {**existing_config, **config}
+
+ _setup(merged_config) # type: ignore
+ yield merged_config # type: ignore
+
+ _setup(existing_config)
+
-__all__ = ["set_cache_directory", 'get_cache_directory', 'get_private_directory']
+__all__ = [
+ "get_cache_directory",
+ "get_config_as_dict",
+ "set_root_cache_directory",
+ "start_using_configuration_for_example",
+ "stop_using_configuration_for_example",
+]
_setup()
diff --git a/openml/datasets/__init__.py b/openml/datasets/__init__.py
index 6bfbd18a3..eb0932652 100644
--- a/openml/datasets/__init__.py
+++ b/openml/datasets/__init__.py
@@ -1,6 +1,33 @@
-from .functions import (list_datasets, check_datasets_active,
- get_datasets, get_dataset)
+# License: BSD 3-Clause
+
+from .data_feature import OpenMLDataFeature
from .dataset import OpenMLDataset
+from .functions import (
+ attributes_arff_from_df,
+ check_datasets_active,
+ create_dataset,
+ delete_dataset,
+ edit_dataset,
+ fork_dataset,
+ get_dataset,
+ get_datasets,
+ list_datasets,
+ list_qualities,
+ status_update,
+)
-__all__ = ['check_datasets_active', 'get_dataset', 'get_datasets',
- 'OpenMLDataset', 'list_datasets']
+__all__ = [
+ "OpenMLDataFeature",
+ "OpenMLDataset",
+ "attributes_arff_from_df",
+ "check_datasets_active",
+ "create_dataset",
+ "delete_dataset",
+ "edit_dataset",
+ "fork_dataset",
+ "get_dataset",
+ "get_datasets",
+ "list_datasets",
+ "list_qualities",
+ "status_update",
+]
diff --git a/openml/datasets/data_feature.py b/openml/datasets/data_feature.py
new file mode 100644
index 000000000..0598763b0
--- /dev/null
+++ b/openml/datasets/data_feature.py
@@ -0,0 +1,84 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+from collections.abc import Sequence
+from typing import TYPE_CHECKING, Any, ClassVar
+
+if TYPE_CHECKING:
+ from IPython.lib import pretty
+
+
+class OpenMLDataFeature: # noqa: PLW1641
+ """
+ Data Feature (a.k.a. Attribute) object.
+
+ Parameters
+ ----------
+ index : int
+ The index of this feature
+ name : str
+ Name of the feature
+ data_type : str
+ can be nominal, numeric, string, date (corresponds to arff)
+ nominal_values : list(str)
+ list of the possible values, in case of nominal attribute
+ number_missing_values : int
+ Number of rows that have a missing value for this feature.
+ ontologies : list(str)
+ list of ontologies attached to this feature. An ontology describes the
+ concept that are described in a feature. An ontology is defined by an
+ URL where the information is provided.
+ """
+
+ LEGAL_DATA_TYPES: ClassVar[Sequence[str]] = ["nominal", "numeric", "string", "date"]
+
+ def __init__( # noqa: PLR0913
+ self,
+ index: int,
+ name: str,
+ data_type: str,
+ nominal_values: list[str],
+ number_missing_values: int,
+ ontologies: list[str] | None = None,
+ ):
+ if not isinstance(index, int):
+ raise TypeError(f"Index must be `int` but is {type(index)}")
+
+ if data_type not in self.LEGAL_DATA_TYPES:
+ raise ValueError(
+ f"data type should be in {self.LEGAL_DATA_TYPES!s}, found: {data_type}",
+ )
+
+ if data_type == "nominal":
+ if nominal_values is None:
+ raise TypeError(
+ "Dataset features require attribute `nominal_values` for nominal feature type.",
+ )
+
+ if not isinstance(nominal_values, list):
+ raise TypeError(
+ "Argument `nominal_values` is of wrong datatype, should be list, "
+ f"but is {type(nominal_values)}",
+ )
+ elif nominal_values is not None:
+ raise TypeError("Argument `nominal_values` must be None for non-nominal feature.")
+
+ if not isinstance(number_missing_values, int):
+ msg = f"number_missing_values must be int but is {type(number_missing_values)}"
+ raise TypeError(msg)
+
+ self.index = index
+ self.name = str(name)
+ self.data_type = str(data_type)
+ self.nominal_values = nominal_values
+ self.number_missing_values = number_missing_values
+ self.ontologies = ontologies
+
+ def __repr__(self) -> str:
+ return f"[{self.index} - {self.name} ({self.data_type})]"
+
+ def __eq__(self, other: Any) -> bool:
+ return isinstance(other, OpenMLDataFeature) and self.__dict__ == other.__dict__
+
+ def _repr_pretty_(self, pp: pretty.PrettyPrinter, cycle: bool) -> None: # noqa: ARG002
+ pp.text(str(self))
diff --git a/openml/datasets/dataset.py b/openml/datasets/dataset.py
index 2cc98aa66..d9eee278d 100644
--- a/openml/datasets/dataset.py
+++ b/openml/datasets/dataset.py
@@ -1,59 +1,215 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
import gzip
-import io
import logging
import os
-import sys
+import pickle
+import re
+import warnings
+from collections.abc import Iterable, Sequence
+from pathlib import Path
+from typing import Any, Literal
import arff
-
import numpy as np
+import pandas as pd
import scipy.sparse
import xmltodict
-from ..exceptions import PyOpenMLError
+from openml.base import OpenMLBase
+from openml.config import OPENML_SKIP_PARQUET_ENV_VAR
-if sys.version_info[0] >= 3:
- import pickle
-else:
- try:
- import cPickle as pickle
- except:
- import pickle
+from .data_feature import OpenMLDataFeature
+logger = logging.getLogger(__name__)
-from ..util import is_string
-from .._api_calls import _perform_api_call
-logger = logging.getLogger(__name__)
+def _ensure_dataframe(
+ data: pd.DataFrame | pd.Series | np.ndarray | scipy.sparse.spmatrix,
+ attribute_names: list | None = None,
+) -> pd.DataFrame:
+ if isinstance(data, pd.DataFrame):
+ return data
+ if scipy.sparse.issparse(data):
+ return pd.DataFrame.sparse.from_spmatrix(data, columns=attribute_names)
+ if isinstance(data, np.ndarray):
+ return pd.DataFrame(data, columns=attribute_names) # type: ignore
+ if isinstance(data, pd.Series):
+ return data.to_frame()
+
+ raise TypeError(f"Data type {type(data)} not supported.")
-class OpenMLDataset(object):
+class OpenMLDataset(OpenMLBase): # noqa: PLW1641
"""Dataset object.
Allows fetching and uploading datasets to OpenML.
Parameters
----------
- name : string
- Name of the dataset
- description : string
- Description of the dataset
- FIXME : which of these do we actually nee?
+ name : str
+ Name of the dataset.
+ description : str
+ Description of the dataset.
+ data_format : str
+ Format of the dataset which can be either 'arff' or 'sparse_arff'.
+ cache_format : str
+ Format for caching the dataset which can be either 'feather' or 'pickle'.
+ dataset_id : int, optional
+ Id autogenerated by the server.
+ version : int, optional
+ Version of this dataset. '1' for original version.
+ Auto-incremented by server.
+ creator : str, optional
+ The person who created the dataset.
+ contributor : str, optional
+ People who contributed to the current version of the dataset.
+ collection_date : str, optional
+ The date the data was originally collected, given by the uploader.
+ upload_date : str, optional
+ The date-time when the dataset was uploaded, generated by server.
+ language : str, optional
+ Language in which the data is represented.
+ Starts with 1 upper case letter, rest lower case, e.g. 'English'.
+ licence : str, optional
+ License of the data.
+ url : str, optional
+ Valid URL, points to actual data file.
+ The file can be on the OpenML server or another dataset repository.
+ default_target_attribute : str, optional
+ The default target attribute, if it exists.
+ Can have multiple values, comma separated.
+ row_id_attribute : str, optional
+ The attribute that represents the row-id column,
+ if present in the dataset.
+ ignore_attribute : str | list, optional
+ Attributes that should be excluded in modelling,
+ such as identifiers and indexes.
+ version_label : str, optional
+ Version label provided by user.
+ Can be a date, hash, or some other type of id.
+ citation : str, optional
+ Reference(s) that should be cited when building on this data.
+ tag : str, optional
+ Tags, describing the algorithms.
+ visibility : str, optional
+ Who can see the dataset.
+ Typical values: 'Everyone','All my friends','Only me'.
+ Can also be any of the user's circles.
+ original_data_url : str, optional
+ For derived data, the url to the original dataset.
+ paper_url : str, optional
+ Link to a paper describing the dataset.
+ update_comment : str, optional
+ An explanation for when the dataset is uploaded.
+ md5_checksum : str, optional
+ MD5 checksum to check if the dataset is downloaded without corruption.
+ data_file : str, optional
+ Path to where the dataset is located.
+ features_file : dict, optional
+ A dictionary of dataset features,
+ which maps a feature index to a OpenMLDataFeature.
+ qualities_file : dict, optional
+ A dictionary of dataset qualities,
+ which maps a quality name to a quality value.
+ dataset: string, optional
+ Serialized arff dataset string.
+ parquet_url: string, optional
+ This is the URL to the storage location where the dataset files are hosted.
+ This can be a MinIO bucket URL. If specified, the data will be accessed
+ from this URL when reading the files.
+ parquet_file: string, optional
+ Path to the local file.
"""
- def __init__(self, dataset_id=None, name=None, version=None, description=None,
- format=None, creator=None, contributor=None,
- collection_date=None, upload_date=None, language=None,
- licence=None, url=None, default_target_attribute=None,
- row_id_attribute=None, ignore_attribute=None,
- version_label=None, citation=None, tag=None, visibility=None,
- original_data_url=None, paper_url=None, update_comment=None,
- md5_checksum=None, data_file=None, features=None):
+
+ def __init__( # noqa: C901, PLR0912, PLR0913, PLR0915
+ self,
+ name: str,
+ description: str | None,
+ data_format: Literal["arff", "sparse_arff"] = "arff",
+ cache_format: Literal["feather", "pickle"] = "pickle",
+ dataset_id: int | None = None,
+ version: int | None = None,
+ creator: str | None = None,
+ contributor: str | None = None,
+ collection_date: str | None = None,
+ upload_date: str | None = None,
+ language: str | None = None,
+ licence: str | None = None,
+ url: str | None = None,
+ default_target_attribute: str | None = None,
+ row_id_attribute: str | None = None,
+ ignore_attribute: str | list[str] | None = None,
+ version_label: str | None = None,
+ citation: str | None = None,
+ tag: str | None = None,
+ visibility: str | None = None,
+ original_data_url: str | None = None,
+ paper_url: str | None = None,
+ update_comment: str | None = None,
+ md5_checksum: str | None = None,
+ data_file: str | None = None,
+ features_file: str | None = None,
+ qualities_file: str | None = None,
+ dataset: str | None = None,
+ parquet_url: str | None = None,
+ parquet_file: str | None = None,
+ ):
+ if cache_format not in ["feather", "pickle"]:
+ raise ValueError(
+ "cache_format must be one of 'feather' or 'pickle. "
+ f"Invalid format specified: {cache_format}",
+ )
+
+ def find_invalid_characters(string: str, pattern: str) -> str:
+ invalid_chars = set()
+ regex = re.compile(pattern)
+ for char in string:
+ if not regex.match(char):
+ invalid_chars.add(char)
+ return ",".join(
+ [f"'{char}'" if char != "'" else f'"{char}"' for char in invalid_chars],
+ )
+
+ if dataset_id is None:
+ pattern = "^[\x00-\x7f]*$"
+ if description and not re.match(pattern, description):
+ # not basiclatin (XSD complains)
+ invalid_characters = find_invalid_characters(description, pattern)
+ raise ValueError(
+ f"Invalid symbols {invalid_characters} in description: {description}",
+ )
+ pattern = "^[\x00-\x7f]*$"
+ if citation and not re.match(pattern, citation):
+ # not basiclatin (XSD complains)
+ invalid_characters = find_invalid_characters(citation, pattern)
+ raise ValueError(
+ f"Invalid symbols {invalid_characters} in citation: {citation}",
+ )
+ pattern = "^[a-zA-Z0-9_\\-\\.\\(\\),]+$"
+ if not re.match(pattern, name):
+ # regex given by server in error message
+ invalid_characters = find_invalid_characters(name, pattern)
+ raise ValueError(f"Invalid symbols {invalid_characters} in name: {name}")
+
+ self.ignore_attribute: list[str] | None = None
+ if isinstance(ignore_attribute, str):
+ self.ignore_attribute = [ignore_attribute]
+ elif isinstance(ignore_attribute, list) or ignore_attribute is None:
+ self.ignore_attribute = ignore_attribute
+ else:
+ raise ValueError("Wrong data type for ignore_attribute. Should be list.")
+
+ # TODO add function to check if the name is casual_string128
# Attributes received by querying the RESTful API
self.dataset_id = int(dataset_id) if dataset_id is not None else None
self.name = name
- self.version = int(version)
+ self.version = int(version) if version is not None else None
self.description = description
- self.format = format
+ self.cache_format = cache_format
+ # Has to be called format, otherwise there will be an XML upload error
+ self.format = data_format
self.creator = creator
self.contributor = contributor
self.collection_date = collection_date
@@ -63,7 +219,7 @@ def __init__(self, dataset_id=None, name=None, version=None, description=None,
self.url = url
self.default_target_attribute = default_target_attribute
self.row_id_attribute = row_id_attribute
- self.ignore_attributes = ignore_attribute
+
self.version_label = version_label
self.citation = citation
self.tag = tag
@@ -71,196 +227,616 @@ def __init__(self, dataset_id=None, name=None, version=None, description=None,
self.original_data_url = original_data_url
self.paper_url = paper_url
self.update_comment = update_comment
- self.md5_cheksum = md5_checksum
+ self.md5_checksum = md5_checksum
self.data_file = data_file
- self.features = features
+ self.parquet_file = parquet_file
+ self._dataset = dataset
+ self._parquet_url = parquet_url
+
+ self._features: dict[int, OpenMLDataFeature] | None = None
+ self._qualities: dict[str, float] | None = None
+ self._no_qualities_found = False
+
+ if features_file is not None:
+ self._features = _read_features(Path(features_file))
+
+ # "" was the old default value by `get_dataset` and maybe still used by some
+ if qualities_file == "":
+ # TODO(0.15): to switch to "qualities_file is not None" below and remove warning
+ warnings.warn(
+ "Starting from Version 0.15 `qualities_file` must be None and not an empty string "
+ "to avoid reading the qualities from file. Set `qualities_file` to None to avoid "
+ "this warning.",
+ FutureWarning,
+ stacklevel=2,
+ )
+ qualities_file = None
+
+ if qualities_file is not None:
+ self._qualities = _read_qualities(Path(qualities_file))
if data_file is not None:
- if self._data_features_supported():
- self.data_pickle_file = data_file.replace('.arff', '.pkl')
-
- if os.path.exists(self.data_pickle_file):
- logger.debug("Data pickle file already exists.")
- else:
- try:
- data = self._get_arff(self.format)
- except OSError as e:
- logger.critical("Please check that the data file %s is there "
- "and can be read.", self.data_file)
- raise e
-
- categorical = [False if type(type_) != list else True
- for name, type_ in data['attributes']]
- attribute_names = [name for name, type_ in data['attributes']]
-
- if isinstance(data['data'], tuple):
- X = data['data']
- X_shape = (max(X[1]) + 1, max(X[2]) + 1)
- X = scipy.sparse.coo_matrix(
- (X[0], (X[1], X[2])), shape=X_shape, dtype=np.float32)
- X = X.tocsr()
- elif isinstance(data['data'], list):
- X = np.array(data['data'], dtype=np.float32)
- else:
- raise Exception()
-
- with open(self.data_pickle_file, "wb") as fh:
- pickle.dump((X, categorical, attribute_names), fh, -1)
- logger.debug("Saved dataset %d: %s to file %s" %
- (self.dataset_id, self.name, self.data_pickle_file))
-
- def __eq__(self, other):
- if type(other) != OpenMLDataset:
- return False
- elif self.id == other._id or \
- (self.name == other._name and self.version == other._version):
- return True
+ data_pickle, data_feather, feather_attribute = self._compressed_cache_file_paths(
+ Path(data_file)
+ )
+ self.data_pickle_file = data_pickle if Path(data_pickle).exists() else None
+ self.data_feather_file = data_feather if Path(data_feather).exists() else None
+ self.feather_attribute_file = feather_attribute if Path(feather_attribute) else None
else:
+ self.data_pickle_file = None
+ self.data_feather_file = None
+ self.feather_attribute_file = None
+
+ @property
+ def features(self) -> dict[int, OpenMLDataFeature]:
+ """Get the features of this dataset."""
+ if self._features is None:
+ # TODO(eddiebergman): These should return a value so we can set it to be not None
+ self._load_features()
+
+ assert self._features is not None
+ return self._features
+
+ @property
+ def qualities(self) -> dict[str, float] | None:
+ """Get the qualities of this dataset."""
+ # TODO(eddiebergman): Better docstring, I don't know what qualities means
+
+ # We have to check `_no_qualities_found` as there might not be qualities for a dataset
+ if self._qualities is None and (not self._no_qualities_found):
+ self._load_qualities()
+
+ return self._qualities
+
+ @property
+ def id(self) -> int | None:
+ """Get the dataset numeric id."""
+ return self.dataset_id
+
+ def _get_repr_body_fields(self) -> Sequence[tuple[str, str | int | None]]:
+ """Collect all information to display in the __repr__ body."""
+ # Obtain number of features in accordance with lazy loading.
+ n_features: int | None = None
+ if self._qualities is not None and self._qualities["NumberOfFeatures"] is not None:
+ n_features = int(self._qualities["NumberOfFeatures"])
+ elif self._features is not None:
+ n_features = len(self._features)
+
+ fields: dict[str, int | str | None] = {
+ "Name": self.name,
+ "Version": self.version,
+ "Format": self.format,
+ "Licence": self.licence,
+ "Download URL": self.url,
+ "Data file": str(self.data_file) if self.data_file is not None else None,
+ "Pickle file": (
+ str(self.data_pickle_file) if self.data_pickle_file is not None else None
+ ),
+ "# of features": n_features,
+ }
+ if self.upload_date is not None:
+ fields["Upload Date"] = self.upload_date.replace("T", " ")
+ if self.dataset_id is not None:
+ fields["OpenML URL"] = self.openml_url
+ if self._qualities is not None and self._qualities["NumberOfInstances"] is not None:
+ fields["# of instances"] = int(self._qualities["NumberOfInstances"])
+
+ # determines the order in which the information will be printed
+ order = [
+ "Name",
+ "Version",
+ "Format",
+ "Upload Date",
+ "Licence",
+ "Download URL",
+ "OpenML URL",
+ "Data File",
+ "Pickle File",
+ "# of features",
+ "# of instances",
+ ]
+ return [(key, fields[key]) for key in order if key in fields]
+
+ def __eq__(self, other: Any) -> bool:
+ if not isinstance(other, OpenMLDataset):
return False
- def _get_arff(self, format):
+ server_fields = {
+ "dataset_id",
+ "version",
+ "upload_date",
+ "url",
+ "_parquet_url",
+ "dataset",
+ "data_file",
+ "format",
+ "cache_format",
+ }
+
+ cache_fields = {
+ "_dataset",
+ "data_file",
+ "data_pickle_file",
+ "data_feather_file",
+ "feather_attribute_file",
+ "parquet_file",
+ }
+
+ # check that common keys and values are identical
+ ignore_fields = server_fields | cache_fields
+ self_keys = set(self.__dict__.keys()) - ignore_fields
+ other_keys = set(other.__dict__.keys()) - ignore_fields
+ return self_keys == other_keys and all(
+ self.__dict__[key] == other.__dict__[key] for key in self_keys
+ )
+
+ def _download_data(self) -> None:
+ """Download ARFF data file to standard cache directory. Set `self.data_file`."""
+ # import required here to avoid circular import.
+ from .functions import _get_dataset_arff, _get_dataset_parquet
+
+ skip_parquet = os.environ.get(OPENML_SKIP_PARQUET_ENV_VAR, "false").casefold() == "true"
+ if self._parquet_url is not None and not skip_parquet:
+ parquet_file = _get_dataset_parquet(self)
+ self.parquet_file = None if parquet_file is None else str(parquet_file)
+ if self.parquet_file is None:
+ self.data_file = str(_get_dataset_arff(self))
+
+ def _get_arff(self, format: str) -> dict: # noqa: A002
"""Read ARFF file and return decoded arff.
Reads the file referenced in self.data_file.
+ Parameters
+ ----------
+ format : str
+ Format of the ARFF file.
+ Must be one of 'arff' or 'sparse_arff' or a string that will be either of those
+ when converted to lower case.
+
+
+
Returns
-------
- arff_string :
+ dict
Decoded arff.
"""
-
# TODO: add a partial read method which only returns the attribute
# headers of the corresponding .arff file!
-
- # A random number after which we consider a file for too large on a
- # 32 bit system...currently 120mb (just a little bit more than covtype)
import struct
- if not self._data_features_supported():
- raise PyOpenMLError('Dataset not compatible, PyOpenML cannot handle string features')
-
filename = self.data_file
- bits = (8 * struct.calcsize("P"))
- if bits != 64 and os.path.getsize(filename) > 120000000:
- return NotImplementedError("File too big")
-
- if format.lower() == 'arff':
+ assert filename is not None
+ filepath = Path(filename)
+
+ bits = 8 * struct.calcsize("P")
+
+ # Files can be considered too large on a 32-bit system,
+ # if it exceeds 120mb (slightly more than covtype dataset size)
+ # This number is somewhat arbitrary.
+ if bits != 64:
+ MB_120 = 120_000_000
+ file_size = filepath.stat().st_size
+ if file_size > MB_120:
+ raise NotImplementedError(
+ f"File '{filename}' ({file_size / 1e6:.1f} MB)"
+ f"exceeds the maximum supported size of 120 MB. "
+ f"This limitation applies to {bits}-bit systems. "
+ f"Large dataset handling is currently not fully supported. "
+ f"Please consider using a smaller dataset"
+ )
+
+ if format.lower() == "arff":
return_type = arff.DENSE
- elif format.lower() == 'sparse_arff':
+ elif format.lower() == "sparse_arff":
return_type = arff.COO
else:
- raise ValueError('Unknown data format %s' % format)
+ raise ValueError(f"Unknown data format {format}")
- def decode_arff(fh):
+ def decode_arff(fh: Any) -> dict:
decoder = arff.ArffDecoder()
- return decoder.decode(fh, encode_nominal=True,
- return_type=return_type)
+ return decoder.decode(fh, encode_nominal=True, return_type=return_type) # type: ignore
- if filename[-3:] == ".gz":
- with gzip.open(filename) as fh:
- return decode_arff(fh)
+ if filepath.suffix.endswith(".gz"):
+ with gzip.open(filename) as zipfile:
+ return decode_arff(zipfile)
else:
- with io.open(filename, encoding='utf8') as fh:
+ with filepath.open(encoding="utf8") as fh:
return decode_arff(fh)
- def get_data(self, target=None, target_dtype=int, include_row_id=False,
- include_ignore_attributes=False,
- return_categorical_indicator=False,
- return_attribute_names=False):
- """Returns dataset content as numpy arrays / sparse matrices.
+ def _parse_data_from_arff( # noqa: C901, PLR0912, PLR0915
+ self,
+ arff_file_path: Path,
+ ) -> tuple[pd.DataFrame | scipy.sparse.csr_matrix, list[bool], list[str]]:
+ """Parse all required data from arff file.
Parameters
----------
-
+ arff_file_path : str
+ Path to the file on disk.
Returns
-------
-
+ Tuple[Union[pd.DataFrame, scipy.sparse.csr_matrix], List[bool], List[str]]
+ DataFrame or csr_matrix: dataset
+ List[bool]: List indicating which columns contain categorical variables.
+ List[str]: List of column names.
"""
- rval = []
+ try:
+ data = self._get_arff(self.format)
+ except OSError as e:
+ logger.critical(
+ f"Please check that the data file {arff_file_path} is there and can be read.",
+ )
+ raise e
+
+ ARFF_DTYPES_TO_PD_DTYPE = {
+ "INTEGER": "integer",
+ "REAL": "floating",
+ "NUMERIC": "floating",
+ "STRING": "string",
+ }
+ attribute_dtype = {}
+ attribute_names = []
+ categories_names = {}
+ categorical = []
+ for name, type_ in data["attributes"]:
+ # if the feature is nominal and a sparse matrix is
+ # requested, the categories need to be numeric
+ if isinstance(type_, list) and self.format.lower() == "sparse_arff":
+ try:
+ # checks if the strings which should be the class labels
+ # can be encoded into integers
+ pd.factorize(np.array(type_))[0]
+ except ValueError as e:
+ raise ValueError(
+ "Categorical data needs to be numeric when using sparse ARFF."
+ ) from e
+
+ # string can only be supported with pandas DataFrame
+ elif type_ == "STRING" and self.format.lower() == "sparse_arff":
+ raise ValueError("Dataset containing strings is not supported with sparse ARFF.")
+
+ # infer the dtype from the ARFF header
+ if isinstance(type_, list):
+ categorical.append(True)
+ categories_names[name] = type_
+ if len(type_) == 2:
+ type_norm = [cat.lower().capitalize() for cat in type_]
+ if {"True", "False"} == set(type_norm):
+ categories_names[name] = [cat == "True" for cat in type_norm]
+ attribute_dtype[name] = "boolean"
+ else:
+ attribute_dtype[name] = "categorical"
+ else:
+ attribute_dtype[name] = "categorical"
+ else:
+ categorical.append(False)
+ attribute_dtype[name] = ARFF_DTYPES_TO_PD_DTYPE[type_]
+ attribute_names.append(name)
+
+ if self.format.lower() == "sparse_arff":
+ X = data["data"]
+ X_shape = (max(X[1]) + 1, max(X[2]) + 1)
+ X = scipy.sparse.coo_matrix((X[0], (X[1], X[2])), shape=X_shape, dtype=np.float32)
+ X = X.tocsr()
+ elif self.format.lower() == "arff":
+ X = pd.DataFrame(data["data"], columns=attribute_names)
+
+ col = []
+ for column_name in X.columns:
+ if attribute_dtype[column_name] in ("categorical", "boolean"):
+ categories = self._unpack_categories(
+ X[column_name], # type: ignore
+ categories_names[column_name],
+ )
+ col.append(categories)
+ elif attribute_dtype[column_name] in ("floating", "integer"):
+ X_col = X[column_name]
+ if X_col.min() >= 0 and X_col.max() <= 255:
+ try:
+ X_col_uint = X_col.astype("uint8")
+ if (X_col == X_col_uint).all():
+ col.append(X_col_uint)
+ continue
+ except ValueError:
+ pass
+ col.append(X[column_name])
+ else:
+ col.append(X[column_name])
+ X = pd.concat(col, axis=1)
+ else:
+ raise ValueError(f"Dataset format '{self.format}' is not a valid format.")
- if not self._data_features_supported():
- raise PyOpenMLError('Dataset not compatible, PyOpenML cannot handle string features')
+ return X, categorical, attribute_names # type: ignore
- path = self.data_pickle_file
- if not os.path.exists(path):
- raise ValueError("Cannot find a ndarray file for dataset %s at"
- "location %s " % (self.name, path))
- else:
- with open(path, "rb") as fh:
- data, categorical, attribute_names = pickle.load(fh)
+ def _compressed_cache_file_paths(self, data_file: Path) -> tuple[Path, Path, Path]:
+ data_pickle_file = data_file.with_suffix(".pkl.py3")
+ data_feather_file = data_file.with_suffix(".feather")
+ feather_attribute_file = data_file.with_suffix(".feather.attributes.pkl.py3")
+ return data_pickle_file, data_feather_file, feather_attribute_file
- to_exclude = []
- if include_row_id is False:
- if not self.row_id_attribute:
- pass
- else:
- if is_string(self.row_id_attribute):
- to_exclude.append(self.row_id_attribute)
- else:
- to_exclude.extend(self.row_id_attribute)
+ def _cache_compressed_file_from_file(
+ self,
+ data_file: Path,
+ ) -> tuple[pd.DataFrame | scipy.sparse.csr_matrix, list[bool], list[str]]:
+ """Store data from the local file in compressed format.
- if include_ignore_attributes is False:
- if not self.ignore_attributes:
- pass
- else:
- if is_string(self.ignore_attributes):
- to_exclude.append(self.ignore_attributes)
- else:
- to_exclude.extend(self.ignore_attributes)
+ If a local parquet file is present it will be used instead of the arff file.
+ Sets cache_format to 'pickle' if data is sparse.
+ """
+ (
+ data_pickle_file,
+ data_feather_file,
+ feather_attribute_file,
+ ) = self._compressed_cache_file_paths(data_file)
- if len(to_exclude) > 0:
- logger.info("Going to remove the following attributes:"
- " %s" % to_exclude)
- keep = np.array([True if column not in to_exclude else False
- for column in attribute_names])
- data = data[:, keep]
- categorical = [cat for cat, k in zip(categorical, keep) if k]
- attribute_names = [att for att, k in
- zip(attribute_names, keep) if k]
+ attribute_names, categorical, data = self._parse_data_from_file(data_file)
+
+ # Feather format does not work for sparse datasets, so we use pickle for sparse datasets
+ if scipy.sparse.issparse(data):
+ self.cache_format = "pickle"
+
+ logger.info(f"{self.cache_format} write {self.name}")
+ if self.cache_format == "feather":
+ assert isinstance(data, pd.DataFrame)
+
+ data.to_feather(data_feather_file)
+ with open(feather_attribute_file, "wb") as fh: # noqa: PTH123
+ pickle.dump((categorical, attribute_names), fh, pickle.HIGHEST_PROTOCOL)
+ self.data_feather_file = data_feather_file
+ self.feather_attribute_file = feather_attribute_file
- if target is None:
- rval.append(data)
else:
- if is_string(target):
- target = [target]
- targets = np.array([True if column in target else False
- for column in attribute_names])
+ with open(data_pickle_file, "wb") as fh: # noqa: PTH123
+ pickle.dump((data, categorical, attribute_names), fh, pickle.HIGHEST_PROTOCOL)
+ self.data_pickle_file = data_pickle_file
+
+ data_file = data_pickle_file if self.cache_format == "pickle" else data_feather_file
+ logger.debug(f"Saved dataset {int(self.dataset_id or -1)}: {self.name} to file {data_file}")
+
+ return data, categorical, attribute_names
+
+ def _parse_data_from_file(
+ self,
+ data_file: Path,
+ ) -> tuple[list[str], list[bool], pd.DataFrame | scipy.sparse.csr_matrix]:
+ if data_file.suffix == ".arff":
+ data, categorical, attribute_names = self._parse_data_from_arff(data_file)
+ elif data_file.suffix == ".pq":
+ attribute_names, categorical, data = self._parse_data_from_pq(data_file)
+ else:
+ raise ValueError(f"Unknown file type for file '{data_file}'.")
+
+ return attribute_names, categorical, data
+
+ def _parse_data_from_pq(self, data_file: Path) -> tuple[list[str], list[bool], pd.DataFrame]:
+ try:
+ data = pd.read_parquet(data_file)
+ except Exception as e:
+ raise Exception(f"File: {data_file}") from e
+ categorical = [data[c].dtype.name == "category" for c in data.columns]
+ attribute_names = list(data.columns)
+ return attribute_names, categorical, data
+
+ def _load_data(self) -> tuple[pd.DataFrame, list[bool], list[str]]: # noqa: PLR0912, C901, PLR0915
+ """Load data from compressed format or arff. Download data if not present on disk."""
+ need_to_create_pickle = self.cache_format == "pickle" and self.data_pickle_file is None
+ need_to_create_feather = self.cache_format == "feather" and self.data_feather_file is None
+
+ if need_to_create_pickle or need_to_create_feather:
+ if self.data_file is None:
+ self._download_data()
+
+ file_to_load = self.data_file if self.parquet_file is None else self.parquet_file
+ assert file_to_load is not None
+ data, cats, attrs = self._cache_compressed_file_from_file(Path(file_to_load))
+ return _ensure_dataframe(data, attrs), cats, attrs
+
+ # helper variable to help identify where errors occur
+ fpath = self.data_feather_file if self.cache_format == "feather" else self.data_pickle_file
+ logger.info(f"{self.cache_format} load data {self.name}")
+ try:
+ if self.cache_format == "feather":
+ assert self.data_feather_file is not None
+ assert self.feather_attribute_file is not None
+
+ data = pd.read_feather(self.data_feather_file)
+ fpath = self.feather_attribute_file
+ with self.feather_attribute_file.open("rb") as fh:
+ categorical, attribute_names = pickle.load(fh) # noqa: S301
+ else:
+ assert self.data_pickle_file is not None
+ with self.data_pickle_file.open("rb") as fh:
+ data, categorical, attribute_names = pickle.load(fh) # noqa: S301
+
+ except FileNotFoundError as e:
+ raise ValueError(
+ f"Cannot find file for dataset {self.name} at location '{fpath}'."
+ ) from e
+ except (EOFError, ModuleNotFoundError, ValueError, AttributeError) as e:
+ error_message = getattr(e, "message", e.args[0])
+ hint = ""
+
+ if isinstance(e, EOFError):
+ readable_error = "Detected a corrupt cache file"
+ elif isinstance(e, (ModuleNotFoundError, AttributeError)):
+ readable_error = "Detected likely dependency issues"
+ hint = (
+ "This can happen if the cache was constructed with a different pandas version "
+ "than the one that is used to load the data. See also "
+ )
+ if isinstance(e, ModuleNotFoundError):
+ hint += "https://github.com/openml/openml-python/issues/918. "
+ elif isinstance(e, AttributeError):
+ hint += "https://github.com/openml/openml-python/pull/1121. "
+
+ elif isinstance(e, ValueError) and "unsupported pickle protocol" in e.args[0]:
+ readable_error = "Encountered unsupported pickle protocol"
+ else:
+ raise e
+ logger.warning(
+ f"{readable_error} when loading dataset {self.id} from '{fpath}'. "
+ f"{hint}"
+ f"Error message was: {error_message}. "
+ "We will continue loading data from the arff-file, "
+ "but this will be much slower for big datasets. "
+ "Please manually delete the cache file if you want OpenML-Python "
+ "to attempt to reconstruct it.",
+ )
+ file_to_load = self.data_file if self.parquet_file is None else self.parquet_file
+ assert file_to_load is not None
+ attr, cat, df = self._parse_data_from_file(Path(file_to_load))
+ return _ensure_dataframe(df), cat, attr
+
+ data_up_to_date = isinstance(data, pd.DataFrame) or scipy.sparse.issparse(data)
+ if self.cache_format == "pickle" and not data_up_to_date:
+ logger.info("Updating outdated pickle file.")
+ file_to_load = self.data_file if self.parquet_file is None else self.parquet_file
+ assert file_to_load is not None
+
+ data, cats, attrs = self._cache_compressed_file_from_file(Path(file_to_load))
+
+ return _ensure_dataframe(data, attribute_names), categorical, attribute_names
+
+ @staticmethod
+ def _unpack_categories(series: pd.Series, categories: list) -> pd.Series:
+ # nan-likes can not be explicitly specified as a category
+ def valid_category(cat: Any) -> bool:
+ return isinstance(cat, str) or (cat is not None and not np.isnan(cat))
+
+ filtered_categories = [c for c in categories if valid_category(c)]
+ col = []
+ for x in series:
try:
- x = data[:, ~targets]
- y = data[:, targets].astype(target_dtype)
-
- if len(y.shape) == 2 and y.shape[1] == 1:
- y = y[:, 0]
-
- categorical = [cat for cat, t in
- zip(categorical, targets) if not t]
- attribute_names = [att for att, k in
- zip(attribute_names, targets) if not k]
- except KeyError as e:
- import sys
- sys.stdout.flush()
- raise e
+ col.append(categories[int(x)])
+ except (TypeError, ValueError):
+ col.append(np.nan)
+
+ # We require two lines to create a series of categories as detailed here:
+ # https://pandas.pydata.org/pandas-docs/version/0.24/user_guide/categorical.html#series-creation
+ raw_cat = pd.Categorical(col, ordered=True, categories=filtered_categories)
+ return pd.Series(raw_cat, index=series.index, name=series.name)
+
+ def get_data( # noqa: C901
+ self,
+ target: list[str] | str | None = None,
+ include_row_id: bool = False, # noqa: FBT002
+ include_ignore_attribute: bool = False, # noqa: FBT002
+ ) -> tuple[pd.DataFrame, pd.Series | None, list[bool], list[str]]:
+ """Returns dataset content as dataframes.
- if scipy.sparse.issparse(y):
- y = np.asarray(y.todense()).astype(target_dtype).flatten()
+ Parameters
+ ----------
+ target : string, List[str] or None (default=None)
+ Name of target column to separate from the data.
+ Splitting multiple columns is currently not supported.
+ include_row_id : boolean (default=False)
+ Whether to include row ids in the returned dataset.
+ include_ignore_attribute : boolean (default=False)
+ Whether to include columns that are marked as "ignore"
+ on the server in the dataset.
- rval.append(x)
- rval.append(y)
- if return_categorical_indicator:
- rval.append(categorical)
- if return_attribute_names:
- rval.append(attribute_names)
+ Returns
+ -------
+ X : dataframe, shape (n_samples, n_columns)
+ Dataset, may have sparse dtypes in the columns if required.
+ y : pd.Series, shape (n_samples, ) or None
+ Target column
+ categorical_indicator : list[bool]
+ Mask that indicate categorical features.
+ attribute_names : list[str]
+ List of attribute names.
+ """
+ data, categorical_mask, attribute_names = self._load_data()
- if len(rval) == 1:
- return rval[0]
+ to_exclude = []
+ if not include_row_id and self.row_id_attribute is not None:
+ if isinstance(self.row_id_attribute, str):
+ to_exclude.append(self.row_id_attribute)
+ elif isinstance(self.row_id_attribute, Iterable):
+ to_exclude.extend(self.row_id_attribute)
+
+ if not include_ignore_attribute and self.ignore_attribute is not None:
+ if isinstance(self.ignore_attribute, str):
+ to_exclude.append(self.ignore_attribute)
+ elif isinstance(self.ignore_attribute, Iterable):
+ to_exclude.extend(self.ignore_attribute)
+
+ if len(to_exclude) > 0:
+ logger.info(f"Going to remove the following attributes: {to_exclude}")
+ keep = np.array([column not in to_exclude for column in attribute_names])
+ data = data.drop(columns=to_exclude)
+ categorical_mask = [cat for cat, k in zip(categorical_mask, keep, strict=False) if k]
+ attribute_names = [att for att, k in zip(attribute_names, keep, strict=False) if k]
+
+ if target is None:
+ return data, None, categorical_mask, attribute_names
+
+ if isinstance(target, str):
+ target_names = target.split(",") if "," in target else [target]
else:
- return rval
+ target_names = target
+
+ # All the assumptions below for the target are dependant on the number of targets being 1
+ n_targets = len(target_names)
+ if n_targets > 1:
+ raise NotImplementedError(
+ f"Multi-target prediction is not yet supported."
+ f"Found {n_targets} target columns: {target_names}. "
+ f"Currently, only single-target datasets are supported. "
+ f"Please select a single target column."
+ )
+
+ target_name = target_names[0]
+ x = data.drop(columns=[target_name])
+ y = data[target_name].squeeze()
+
+ # Finally, remove the target from the list of attributes and categorical mask
+ target_index = attribute_names.index(target_name)
+ categorical_mask.pop(target_index)
+ attribute_names.remove(target_name)
+
+ assert isinstance(y, pd.Series)
+ return x, y, categorical_mask, attribute_names
+
+ def _load_features(self) -> None:
+ """Load the features metadata from the server and store it in the dataset object."""
+ # Delayed Import to avoid circular imports or having to import all of dataset.functions to
+ # import OpenMLDataset.
+ from openml.datasets.functions import _get_dataset_features_file
+
+ if self.dataset_id is None:
+ raise ValueError(
+ "No dataset id specified. Please set the dataset id. Otherwise we cannot load "
+ "metadata.",
+ )
+
+ features_file = _get_dataset_features_file(None, self.dataset_id)
+ self._features = _read_features(features_file)
+
+ def _load_qualities(self) -> None:
+ """Load qualities information from the server and store it in the dataset object."""
+ # same reason as above for _load_features
+ from openml.datasets.functions import _get_dataset_qualities_file
+
+ if self.dataset_id is None:
+ raise ValueError(
+ "No dataset id specified. Please set the dataset id. Otherwise we cannot load "
+ "metadata.",
+ )
+
+ qualities_file = _get_dataset_qualities_file(None, self.dataset_id)
+
+ if qualities_file is None:
+ self._no_qualities_found = True
+ else:
+ self._qualities = _read_qualities(qualities_file)
- def retrieve_class_labels(self, target_name='class'):
+ def retrieve_class_labels(self, target_name: str = "class") -> None | list[str]:
"""Reads the datasets arff to determine the class-labels.
If the task has no class labels (for example a regression problem)
@@ -277,81 +853,231 @@ def retrieve_class_labels(self, target_name='class'):
-------
list
"""
+ for feature in self.features.values():
+ if feature.name == target_name:
+ if feature.data_type == "nominal":
+ return feature.nominal_values
+
+ if feature.data_type == "string":
+ # Rel.: #1311
+ # The target is invalid for a classification task if the feature type is string
+ # and not nominal. For such miss-configured tasks, we silently fix it here as
+ # we can safely interpreter string as nominal.
+ df, *_ = self.get_data()
+ return list(df[feature.name].unique())
+
+ return None
+
+ def get_features_by_type( # noqa: C901
+ self,
+ data_type: str,
+ exclude: list[str] | None = None,
+ exclude_ignore_attribute: bool = True, # noqa: FBT002
+ exclude_row_id_attribute: bool = True, # noqa: FBT002
+ ) -> list[int]:
+ """
+ Return indices of features of a given type, e.g. all nominal features.
+ Optional parameters to exclude various features by index or ontology.
- # TODO improve performance, currently reads the whole file
- # Should make a method that only reads the attributes
- arffFileName = self.data_file
+ Parameters
+ ----------
+ data_type : str
+ The data type to return (e.g., nominal, numeric, date, string)
+ exclude : list(int)
+ List of columns to exclude from the return value
+ exclude_ignore_attribute : bool
+ Whether to exclude the defined ignore attributes (and adapt the
+ return values as if these indices are not present)
+ exclude_row_id_attribute : bool
+ Whether to exclude the defined row id attributes (and adapt the
+ return values as if these indices are not present)
- if self.format.lower() == 'arff':
- return_type = arff.DENSE
- elif self.format.lower() == 'sparse_arff':
- return_type = arff.COO
- else:
- raise ValueError('Unknown data format %s' % self.format)
+ Returns
+ -------
+ result : list
+ a list of indices that have the specified data type
+ """
+ if data_type not in OpenMLDataFeature.LEGAL_DATA_TYPES:
+ raise TypeError("Illegal feature type requested")
+ if self.ignore_attribute is not None and not isinstance(self.ignore_attribute, list):
+ raise TypeError("ignore_attribute should be a list")
+ if self.row_id_attribute is not None and not isinstance(self.row_id_attribute, str):
+ raise TypeError("row id attribute should be a str")
+ if exclude is not None and not isinstance(exclude, list):
+ raise TypeError("Exclude should be a list")
+ # assert all(isinstance(elem, str) for elem in exclude),
+ # "Exclude should be a list of strings"
+ to_exclude = []
+ if exclude is not None:
+ to_exclude.extend(exclude)
+ if exclude_ignore_attribute and self.ignore_attribute is not None:
+ to_exclude.extend(self.ignore_attribute)
+ if exclude_row_id_attribute and self.row_id_attribute is not None:
+ to_exclude.append(self.row_id_attribute)
+
+ result = []
+ offset = 0
+ # this function assumes that everything in to_exclude will
+ # be 'excluded' from the dataset (hence the offset)
+ for idx in self.features:
+ name = self.features[idx].name
+ if name in to_exclude:
+ offset += 1
+ elif self.features[idx].data_type == data_type:
+ result.append(idx - offset)
+ return result
+
+ def _get_file_elements(self) -> dict:
+ """Adds the 'dataset' to file elements."""
+ file_elements: dict = {}
+ path = None if self.data_file is None else Path(self.data_file).absolute()
+
+ if self._dataset is not None:
+ file_elements["dataset"] = self._dataset
+ elif path is not None and path.exists():
+ with path.open("rb") as fp:
+ file_elements["dataset"] = fp.read()
+
+ try:
+ dataset_utf8 = str(file_elements["dataset"], encoding="utf8")
+ arff.ArffDecoder().decode(dataset_utf8, encode_nominal=True)
+ except arff.ArffException as e:
+ raise ValueError("The file you have provided is not a valid arff file.") from e
+
+ elif self.url is None:
+ raise ValueError("No valid url/path to the data file was given.")
+ return file_elements
+
+ def _parse_publish_response(self, xml_response: dict) -> None:
+ """Parse the id from the xml_response and assign it to self."""
+ self.dataset_id = int(xml_response["oml:upload_data_set"]["oml:id"])
+
+ def _to_dict(self) -> dict[str, dict]:
+ """Creates a dictionary representation of self."""
+ props = [
+ "id",
+ "name",
+ "version",
+ "description",
+ "format",
+ "creator",
+ "contributor",
+ "collection_date",
+ "upload_date",
+ "language",
+ "licence",
+ "url",
+ "default_target_attribute",
+ "row_id_attribute",
+ "ignore_attribute",
+ "version_label",
+ "citation",
+ "tag",
+ "visibility",
+ "original_data_url",
+ "paper_url",
+ "update_comment",
+ "md5_checksum",
+ ]
+
+ prop_values = {}
+ for prop in props:
+ content = getattr(self, prop, None)
+ if content is not None:
+ prop_values["oml:" + prop] = content
- with io.open(arffFileName, encoding='utf8') as fh:
- arffData = arff.ArffDecoder().decode(fh, return_type=return_type)
+ return {
+ "oml:data_set_description": {
+ "@xmlns:oml": "http://openml.org/openml",
+ **prop_values,
+ }
+ }
- dataAttributes = dict(arffData['attributes'])
- if target_name in dataAttributes:
- return dataAttributes[target_name]
- else:
- return None
- def publish(self):
- """Publish the dataset on the OpenML server.
+def _read_features(features_file: Path) -> dict[int, OpenMLDataFeature]:
+ features_pickle_file = Path(_get_features_pickle_file(str(features_file)))
+ try:
+ with features_pickle_file.open("rb") as fh_binary:
+ return pickle.load(fh_binary) # type: ignore # noqa: S301
- Upload the dataset description and dataset content to openml.
+ except: # noqa: E722
+ with Path(features_file).open("r", encoding="utf8") as fh:
+ features_xml_string = fh.read()
- Returns
- -------
- return_code : int
- Return code from server
+ features = _parse_features_xml(features_xml_string)
- return_value : string
- xml return from server
- """
+ with features_pickle_file.open("wb") as fh_binary:
+ pickle.dump(features, fh_binary)
- file_elements = {'description': self._to_xml()}
- file_dictionary = {}
+ return features
- if self.data_file is not None:
- file_dictionary['dataset'] = self.data_file
- return_code, return_value = _perform_api_call(
- "/data/", file_dictionary=file_dictionary,
- file_elements=file_elements)
+def _parse_features_xml(features_xml_string: str) -> dict[int, OpenMLDataFeature]:
+ xml_dict = xmltodict.parse(
+ features_xml_string, force_list=("oml:feature", "oml:nominal_value"), strip_whitespace=False
+ )
+ features_xml = xml_dict["oml:data_features"]
- self.dataset_id = int(xmltodict.parse(return_value)['oml:upload_data_set']['oml:id'])
- return self
+ features: dict[int, OpenMLDataFeature] = {}
+ for idx, xmlfeature in enumerate(features_xml["oml:feature"]):
+ nr_missing = xmlfeature.get("oml:number_of_missing_values", 0)
+ feature = OpenMLDataFeature(
+ int(xmlfeature["oml:index"]),
+ xmlfeature["oml:name"],
+ xmlfeature["oml:data_type"],
+ xmlfeature.get("oml:nominal_value"),
+ int(nr_missing),
+ xmlfeature.get("oml:ontology"),
+ )
+ if idx != feature.index:
+ raise ValueError("Data features not provided in right order")
+ features[feature.index] = feature
- def _to_xml(self):
- """Serialize object to xml for upload
+ return features
- Returns
- -------
- xml_dataset : string
- XML description of the data.
- """
- xml_dataset = ('\n')
- props = ['id', 'name', 'version', 'description', 'format', 'creator',
- 'contributor', 'collection_date', 'upload_date', 'language',
- 'licence', 'url', 'default_target_attribute',
- 'row_id_attribute', 'ignore_attribute', 'version_label',
- 'citation', 'tag', 'visibility', 'original_data_url',
- 'paper_url', 'update_comment', 'md5_checksum'] # , 'data_file']
- for prop in props:
- content = getattr(self, prop, None)
- if content is not None:
- xml_dataset += "{1} \n".format(prop, content)
- xml_dataset += " "
- return xml_dataset
-
- def _data_features_supported(self):
- if self.features is not None:
- for feature in self.features['oml:feature']:
- if feature['oml:data_type'] not in ['numeric', 'nominal']:
- return False
- return True
- return True
\ No newline at end of file
+
+# TODO(eddiebergman): Should this really exist?
+def _get_features_pickle_file(features_file: str) -> str:
+ """Exists so it can be mocked during unit testing"""
+ return features_file + ".pkl"
+
+
+# TODO(eddiebergman): Should this really exist?
+def _get_qualities_pickle_file(qualities_file: str) -> str:
+ """Exists so it can be mocked during unit testing."""
+ return qualities_file + ".pkl"
+
+
+def _read_qualities(qualities_file: str | Path) -> dict[str, float]:
+ qualities_file = Path(qualities_file)
+ qualities_pickle_file = Path(_get_qualities_pickle_file(str(qualities_file)))
+ try:
+ with qualities_pickle_file.open("rb") as fh_binary:
+ return pickle.load(fh_binary) # type: ignore # noqa: S301
+ except: # noqa: E722
+ with qualities_file.open(encoding="utf8") as fh:
+ qualities_xml = fh.read()
+
+ qualities = _parse_qualities_xml(qualities_xml)
+ with qualities_pickle_file.open("wb") as fh_binary:
+ pickle.dump(qualities, fh_binary)
+
+ return qualities
+
+
+def _check_qualities(qualities: list[dict[str, str]]) -> dict[str, float]:
+ qualities_ = {}
+ for xmlquality in qualities:
+ name = xmlquality["oml:name"]
+ if xmlquality.get("oml:value", None) is None or xmlquality["oml:value"] == "null":
+ value = float("NaN")
+ else:
+ value = float(xmlquality["oml:value"])
+ qualities_[name] = value
+ return qualities_
+
+
+def _parse_qualities_xml(qualities_xml: str) -> dict[str, float]:
+ xml_as_dict = xmltodict.parse(qualities_xml, force_list=("oml:quality",))
+ qualities = xml_as_dict["oml:data_qualities"]["oml:quality"]
+ return _check_qualities(qualities)
diff --git a/openml/datasets/functions.py b/openml/datasets/functions.py
index 869e93aff..3ac657ea0 100644
--- a/openml/datasets/functions.py
+++ b/openml/datasets/functions.py
@@ -1,294 +1,1082 @@
-import io
+# License: BSD 3-Clause
+# ruff: noqa: PLR0913
+from __future__ import annotations
+
+import logging
import os
-import re
-import shutil
+import warnings
from collections import OrderedDict
+from functools import partial
+from pathlib import Path
+from pyexpat import ExpatError
+from typing import TYPE_CHECKING, Any, Literal
+
+import arff
+import minio.error
+import numpy as np
+import pandas as pd
+import urllib3
import xmltodict
+from scipy.sparse import coo_matrix
+
+import openml._api_calls
+import openml.utils
+from openml.config import OPENML_SKIP_PARQUET_ENV_VAR
+from openml.exceptions import (
+ OpenMLHashException,
+ OpenMLPrivateDatasetError,
+ OpenMLServerError,
+ OpenMLServerException,
+)
+from openml.utils import (
+ _create_cache_directory_for_id,
+ _get_cache_dir_for_id,
+ _remove_cache_dir_for_id,
+)
+
from .dataset import OpenMLDataset
-from ..exceptions import OpenMLCacheException
-from .. import config
-from .._api_calls import _perform_api_call, _read_url
+if TYPE_CHECKING:
+ import scipy
+
+DATASETS_CACHE_DIR_NAME = "datasets"
+logger = logging.getLogger(__name__)
+
+NO_ACCESS_GRANTED_ERRCODE = 112
############################################################################
# Local getters/accessors to the cache directory
-def _list_cached_datasets():
- """Return list with ids of all cached datasets
+
+def _get_cache_directory(dataset: OpenMLDataset) -> Path:
+ """Creates and returns the cache directory of the OpenMLDataset."""
+ assert dataset.dataset_id is not None
+ return _create_cache_directory_for_id(DATASETS_CACHE_DIR_NAME, dataset.dataset_id)
+
+
+def list_qualities() -> list[str]:
+ """Return list of data qualities available.
+
+ The function performs an API call to retrieve the entire list of
+ data qualities that are computed on the datasets uploaded.
Returns
-------
list
- List with IDs of all cached datasets.
"""
- datasets = []
+ api_call = "data/qualities/list"
+ xml_string = openml._api_calls._perform_api_call(api_call, "get")
+ qualities = xmltodict.parse(xml_string, force_list=("oml:quality"))
+ # Minimalistic check if the XML is useful
+ if "oml:data_qualities_list" not in qualities:
+ raise ValueError('Error in return XML, does not contain "oml:data_qualities_list"')
- for dataset_cache in [config.get_cache_directory(), config.get_private_directory()]:
- dataset_cache_dir = os.path.join(dataset_cache, "datasets")
- directory_content = os.listdir(dataset_cache_dir)
- directory_content.sort()
+ if not isinstance(qualities["oml:data_qualities_list"]["oml:quality"], list):
+ raise TypeError('Error in return XML, does not contain "oml:quality" as a list')
- # Find all dataset ids for which we have downloaded the dataset
- # description
- for directory_name in directory_content:
- # First check if the directory name could be an OpenML dataset id
- if not re.match(r"[0-9]*", directory_name):
- continue
+ return qualities["oml:data_qualities_list"]["oml:quality"]
- dataset_id = int(directory_name)
- directory_name = os.path.join(dataset_cache_dir,
- directory_name)
- dataset_directory_content = os.listdir(directory_name)
+def list_datasets(
+ data_id: list[int] | None = None,
+ offset: int | None = None,
+ size: int | None = None,
+ status: str | None = None,
+ tag: str | None = None,
+ data_name: str | None = None,
+ data_version: int | None = None,
+ number_instances: int | str | None = None,
+ number_features: int | str | None = None,
+ number_classes: int | str | None = None,
+ number_missing_values: int | str | None = None,
+) -> pd.DataFrame:
+ """Return a dataframe of all dataset which are on OpenML.
- if "dataset.arff" in dataset_directory_content and \
- "description.xml" in dataset_directory_content:
- if dataset_id not in datasets:
- datasets.append(dataset_id)
+ Supports large amount of results.
- datasets.sort()
- return datasets
+ Parameters
+ ----------
+ data_id : list, optional
+ A list of data ids, to specify which datasets should be
+ listed
+ offset : int, optional
+ The number of datasets to skip, starting from the first.
+ size : int, optional
+ The maximum number of datasets to show.
+ status : str, optional
+ Should be {active, in_preparation, deactivated}. By
+ default active datasets are returned, but also datasets
+ from another status can be requested.
+ tag : str, optional
+ data_name : str, optional
+ data_version : int, optional
+ number_instances : int | str, optional
+ number_features : int | str, optional
+ number_classes : int | str, optional
+ number_missing_values : int | str, optional
+ Returns
+ -------
+ datasets: dataframe
+ Each row maps to a dataset
+ Each column contains the following information:
+ - dataset id
+ - name
+ - format
+ - status
+ If qualities are calculated for the dataset, some of
+ these are also included as columns.
+ """
+ listing_call = partial(
+ _list_datasets,
+ data_id=data_id,
+ status=status,
+ tag=tag,
+ data_name=data_name,
+ data_version=data_version,
+ number_instances=number_instances,
+ number_features=number_features,
+ number_classes=number_classes,
+ number_missing_values=number_missing_values,
+ )
+ batches = openml.utils._list_all(listing_call, offset=offset, limit=size)
+ if len(batches) == 0:
+ return pd.DataFrame()
+
+ return pd.concat(batches)
+
+
+def _list_datasets(
+ limit: int,
+ offset: int,
+ *,
+ data_id: list[int] | None = None,
+ **kwargs: Any,
+) -> pd.DataFrame:
+ """
+ Perform api call to return a list of all datasets.
-def _get_cached_datasets():
- """Searches for all OpenML datasets in the OpenML cache dir.
+ Parameters
+ ----------
+ The arguments that are lists are separated from the single value
+ ones which are put into the kwargs.
+ display_errors is also separated from the kwargs since it has a
+ default value.
+
+ limit : int
+ The maximum number of datasets to show.
+ offset : int
+ The number of datasets to skip, starting from the first.
+ data_id : list, optional
+
+ kwargs : dict, optional
+ Legal filter operators (keys in the dict):
+ tag, status, limit, offset, data_name, data_version, number_instances,
+ number_features, number_classes, number_missing_values.
- Return a dictionary which maps dataset ids to dataset objects"""
- dataset_list = _list_cached_datasets()
- datasets = OrderedDict()
+ Returns
+ -------
+ datasets : dataframe
+ """
+ api_call = "data/list"
- for dataset_id in dataset_list:
- datasets[dataset_id] = _get_cached_dataset(dataset_id)
+ if limit is not None:
+ api_call += f"/limit/{limit}"
+ if offset is not None:
+ api_call += f"/offset/{offset}"
+
+ if kwargs is not None:
+ for operator, value in kwargs.items():
+ if value is not None:
+ api_call += f"/{operator}/{value}"
+ if data_id is not None:
+ api_call += f"/data_id/{','.join([str(int(i)) for i in data_id])}"
+ return __list_datasets(api_call=api_call)
- return datasets
+def __list_datasets(api_call: str) -> pd.DataFrame:
+ xml_string = openml._api_calls._perform_api_call(api_call, "get")
+ datasets_dict = xmltodict.parse(xml_string, force_list=("oml:dataset",))
-def _get_cached_dataset(dataset_id):
- """Get cached dataset for ID.
+ # Minimalistic check if the XML is useful
+ assert isinstance(datasets_dict["oml:data"]["oml:dataset"], list), type(
+ datasets_dict["oml:data"],
+ )
+ assert datasets_dict["oml:data"]["@xmlns:oml"] == "http://openml.org/openml", datasets_dict[
+ "oml:data"
+ ]["@xmlns:oml"]
+
+ datasets = {}
+ for dataset_ in datasets_dict["oml:data"]["oml:dataset"]:
+ ignore_attribute = ["oml:file_id", "oml:quality"]
+ dataset = {
+ k.replace("oml:", ""): v for (k, v) in dataset_.items() if k not in ignore_attribute
+ }
+ dataset["did"] = int(dataset["did"])
+ dataset["version"] = int(dataset["version"])
+
+ # The number of qualities can range from 0 to infinity
+ for quality in dataset_.get("oml:quality", []):
+ try:
+ dataset[quality["@name"]] = int(quality["#text"])
+ except ValueError:
+ dataset[quality["@name"]] = float(quality["#text"])
+ datasets[dataset["did"]] = dataset
+
+ return pd.DataFrame.from_dict(datasets, orient="index").astype(
+ {
+ "did": int,
+ "version": int,
+ "status": pd.CategoricalDtype(["active", "deactivated", "in_preparation"]),
+ }
+ )
+
+
+def _expand_parameter(parameter: str | list[str] | None) -> list[str]:
+ expanded_parameter = []
+ if isinstance(parameter, str):
+ expanded_parameter = [x.strip() for x in parameter.split(",")]
+ elif isinstance(parameter, list):
+ expanded_parameter = parameter
+ return expanded_parameter
+
+
+def _validated_data_attributes(
+ attributes: list[str],
+ data_attributes: list[tuple[str, Any]],
+ parameter_name: str,
+) -> None:
+ for attribute_ in attributes:
+ is_attribute_a_data_attribute = any(dattr[0] == attribute_ for dattr in data_attributes)
+ if not is_attribute_a_data_attribute:
+ raise ValueError(
+ f"all attribute of '{parameter_name}' should be one of the data attribute. "
+ f" Got '{attribute_}' while candidates are"
+ f" {[dattr[0] for dattr in data_attributes]}.",
+ )
+
+
+def check_datasets_active(
+ dataset_ids: list[int],
+ raise_error_if_not_exist: bool = True, # noqa: FBT002
+) -> dict[int, bool]:
+ """
+ Check if the dataset ids provided are active.
+
+ Raises an error if a dataset_id in the given list
+ of dataset_ids does not exist on the server and
+ `raise_error_if_not_exist` is set to True (default).
+
+ Parameters
+ ----------
+ dataset_ids : List[int]
+ A list of integers representing dataset ids.
+ raise_error_if_not_exist : bool (default=True)
+ Flag that if activated can raise an error, if one or more of the
+ given dataset ids do not exist on the server.
Returns
-------
- OpenMLDataset
+ dict
+ A dictionary with items {did: bool}
"""
- description = _get_cached_dataset_description(dataset_id)
- arff_file = _get_cached_dataset_arff(dataset_id)
- features = _get_cached_dataset_features(dataset_id)
- dataset = _create_dataset_from_description(description, features, arff_file)
+ datasets = list_datasets(status="all", data_id=dataset_ids)
+ missing = set(dataset_ids) - set(datasets.index)
+ if raise_error_if_not_exist and missing:
+ missing_str = ", ".join(str(did) for did in missing)
+ raise ValueError(f"Could not find dataset(s) {missing_str} in OpenML dataset list.")
+ mask = datasets["status"] == "active"
+ return dict(mask)
- return dataset
+def _name_to_id(
+ dataset_name: str,
+ version: int | None = None,
+ error_if_multiple: bool = False, # noqa: FBT002
+) -> int:
+ """Attempt to find the dataset id of the dataset with the given name.
-def _get_cached_dataset_description(dataset_id):
- for cache_dir in [config.get_cache_directory(),
- config.get_private_directory()]:
- did_cache_dir = os.path.join(cache_dir, "datasets", str(dataset_id))
- description_file = os.path.join(did_cache_dir, "description.xml")
- try:
- with io.open(description_file, encoding='utf8') as fh:
- dataset_xml = fh.read()
- except (IOError, OSError):
- continue
+ If multiple datasets with the name exist, and ``error_if_multiple`` is ``False``,
+ then return the least recent still active dataset.
+
+ Raises an error if no dataset with the name is found.
+ Raises an error if a version is specified but it could not be found.
- return xmltodict.parse(dataset_xml)["oml:data_set_description"]
+ Parameters
+ ----------
+ dataset_name : str
+ The name of the dataset for which to find its id.
+ version : int, optional
+ Version to retrieve. If not specified, the oldest active version is returned.
+ error_if_multiple : bool (default=False)
+ If `False`, if multiple datasets match, return the least recent active dataset.
+ If `True`, if multiple datasets match, raise an error.
+ download_qualities : bool, optional (default=True)
+ If `True`, also download qualities.xml file. If False it skip the qualities.xml.
- raise OpenMLCacheException("Dataset description for dataset id %d not "
- "cached" % dataset_id)
+ Returns
+ -------
+ int
+ The id of the dataset.
+ """
+ status = None if version is not None else "active"
+ candidates = list_datasets(
+ data_name=dataset_name,
+ status=status,
+ data_version=version,
+ )
+ if error_if_multiple and len(candidates) > 1:
+ msg = f"Multiple active datasets exist with name '{dataset_name}'."
+ raise ValueError(msg)
+
+ if candidates.empty:
+ no_dataset_for_name = f"No active datasets exist with name '{dataset_name}'"
+ and_version = f" and version '{version}'." if version is not None else "."
+ raise RuntimeError(no_dataset_for_name + and_version)
+
+ # Dataset ids are chronological so we can just sort based on ids (instead of version)
+ return candidates["did"].min() # type: ignore
+
+
+def get_datasets(
+ dataset_ids: list[str | int],
+ download_data: bool = False, # noqa: FBT002
+ download_qualities: bool = False, # noqa: FBT002
+) -> list[OpenMLDataset]:
+ """Download datasets.
-def _get_cached_dataset_features(dataset_id):
- for cache_dir in [config.get_cache_directory(),
- config.get_private_directory()]:
- did_cache_dir = os.path.join(cache_dir, "datasets", str(dataset_id))
- features_file = os.path.join(did_cache_dir, "features.xml")
- try:
- with io.open(features_file, encoding='utf8') as fh:
- features_xml = fh.read()
- except (IOError, OSError):
- continue
+ This function iterates :meth:`openml.datasets.get_dataset`.
- return xmltodict.parse(features_xml)["oml:data_features"]
+ Parameters
+ ----------
+ dataset_ids : iterable
+ Integers or strings representing dataset ids or dataset names.
+ If dataset names are specified, the least recent still active dataset version is returned.
+ download_data : bool, optional
+ If True, also download the data file. Beware that some datasets are large and it might
+ make the operation noticeably slower. Metadata is also still retrieved.
+ If False, create the OpenMLDataset and only populate it with the metadata.
+ The data may later be retrieved through the `OpenMLDataset.get_data` method.
+ download_qualities : bool, optional (default=True)
+ If True, also download qualities.xml file. If False it skip the qualities.xml.
+
+ Returns
+ -------
+ datasets : list of datasets
+ A list of dataset objects.
+ """
+ datasets = []
+ for dataset_id in dataset_ids:
+ datasets.append(
+ get_dataset(dataset_id, download_data, download_qualities=download_qualities),
+ )
+ return datasets
- raise OpenMLCacheException("Dataset features for dataset id %d not "
- "cached" % dataset_id)
+@openml.utils.thread_safe_if_oslo_installed
+def get_dataset( # noqa: C901, PLR0912
+ dataset_id: int | str,
+ download_data: bool = False, # noqa: FBT002
+ version: int | None = None,
+ error_if_multiple: bool = False, # noqa: FBT002
+ cache_format: Literal["pickle", "feather"] = "pickle",
+ download_qualities: bool = False, # noqa: FBT002
+ download_features_meta_data: bool = False, # noqa: FBT002
+ download_all_files: bool = False, # noqa: FBT002
+ force_refresh_cache: bool = False, # noqa: FBT002
+) -> OpenMLDataset:
+ """Download the OpenML dataset representation, optionally also download actual data file.
+
+ This function is by default NOT thread/multiprocessing safe, as this function uses caching.
+ A check will be performed to determine if the information has previously been downloaded to a
+ cache, and if so be loaded from disk instead of retrieved from the server.
+
+ To make this function thread safe, you can install the python package ``oslo.concurrency``.
+ If ``oslo.concurrency`` is installed `get_dataset` becomes thread safe.
+
+ Alternatively, to make this function thread/multiprocessing safe initialize the cache first by
+ calling `get_dataset(args)` once before calling `get_dataset(args)` many times in parallel.
+ This will initialize the cache and later calls will use the cache in a thread/multiprocessing
+ safe way.
+
+ If dataset is retrieved by name, a version may be specified.
+ If no version is specified and multiple versions of the dataset exist,
+ the earliest version of the dataset that is still active will be returned.
+ If no version is specified, multiple versions of the dataset exist and
+ ``exception_if_multiple`` is set to ``True``, this function will raise an exception.
-def _get_cached_dataset_arff(dataset_id):
- for cache_dir in [config.get_cache_directory(),
- config.get_private_directory()]:
- did_cache_dir = os.path.join(cache_dir, "datasets", str(dataset_id))
- output_file = os.path.join(did_cache_dir, "dataset.arff")
+ Parameters
+ ----------
+ dataset_id : int or str
+ Dataset ID (integer) or dataset name (string) of the dataset to download.
+ download_data : bool (default=False)
+ If True, also download the data file. Beware that some datasets are large and it might
+ make the operation noticeably slower. Metadata is also still retrieved.
+ If False, create the OpenMLDataset and only populate it with the metadata.
+ The data may later be retrieved through the `OpenMLDataset.get_data` method.
+ version : int, optional (default=None)
+ Specifies the version if `dataset_id` is specified by name.
+ If no version is specified, retrieve the least recent still active version.
+ error_if_multiple : bool (default=False)
+ If ``True`` raise an error if multiple datasets are found with matching criteria.
+ cache_format : str (default='pickle') in {'pickle', 'feather'}
+ Format for caching the dataset - may be feather or pickle
+ Note that the default 'pickle' option may load slower than feather when
+ no.of.rows is very high.
+ download_qualities : bool (default=False)
+ Option to download 'qualities' meta-data in addition to the minimal dataset description.
+ If True, download and cache the qualities file.
+ If False, create the OpenMLDataset without qualities metadata. The data may later be added
+ to the OpenMLDataset through the `OpenMLDataset.load_metadata(qualities=True)` method.
+ download_features_meta_data : bool (default=False)
+ Option to download 'features' meta-data in addition to the minimal dataset description.
+ If True, download and cache the features file.
+ If False, create the OpenMLDataset without features metadata. The data may later be added
+ to the OpenMLDataset through the `OpenMLDataset.load_metadata(features=True)` method.
+ download_all_files: bool (default=False)
+ EXPERIMENTAL. Download all files related to the dataset that reside on the server.
+ Useful for datasets which refer to auxiliary files (e.g., meta-album).
+ force_refresh_cache : bool (default=False)
+ Force the cache to refreshed by deleting the cache directory and re-downloading the data.
+ Note, if `force_refresh_cache` is True, `get_dataset` is NOT thread/multiprocessing safe,
+ because this creates a race condition to creating and deleting the cache; as in general with
+ the cache.
+ Returns
+ -------
+ dataset : :class:`openml.OpenMLDataset`
+ The downloaded dataset.
+ """
+ if download_all_files:
+ warnings.warn(
+ "``download_all_files`` is experimental and is likely to break with new releases.",
+ FutureWarning,
+ stacklevel=2,
+ )
+
+ if cache_format not in ["feather", "pickle"]:
+ raise ValueError(
+ "cache_format must be one of 'feather' or 'pickle. "
+ f"Invalid format specified: {cache_format}",
+ )
+
+ if isinstance(dataset_id, str):
try:
- with io.open(output_file, encoding='utf8'):
- pass
- return output_file
- except (OSError, IOError):
- continue
+ dataset_id = int(dataset_id)
+ except ValueError:
+ dataset_id = _name_to_id(dataset_id, version, error_if_multiple) # type: ignore
+ elif not isinstance(dataset_id, int):
+ raise TypeError(
+ f"`dataset_id` must be one of `str` or `int`, not {type(dataset_id)}.",
+ )
+
+ if force_refresh_cache:
+ did_cache_dir = _get_cache_dir_for_id(DATASETS_CACHE_DIR_NAME, dataset_id)
+ if did_cache_dir.exists():
+ _remove_cache_dir_for_id(DATASETS_CACHE_DIR_NAME, did_cache_dir)
+
+ did_cache_dir = _create_cache_directory_for_id(
+ DATASETS_CACHE_DIR_NAME,
+ dataset_id,
+ )
+
+ remove_dataset_cache = True
+ try:
+ description = _get_dataset_description(did_cache_dir, dataset_id)
+ features_file = None
+ qualities_file = None
+
+ if download_features_meta_data:
+ features_file = _get_dataset_features_file(did_cache_dir, dataset_id)
+ if download_qualities:
+ qualities_file = _get_dataset_qualities_file(did_cache_dir, dataset_id)
+
+ parquet_file = None
+ skip_parquet = os.environ.get(OPENML_SKIP_PARQUET_ENV_VAR, "false").casefold() == "true"
+ download_parquet = "oml:parquet_url" in description and not skip_parquet
+ if download_parquet and (download_data or download_all_files):
+ try:
+ parquet_file = _get_dataset_parquet(
+ description,
+ download_all_files=download_all_files,
+ )
+ except urllib3.exceptions.MaxRetryError:
+ parquet_file = None
+
+ arff_file = None
+ if parquet_file is None and download_data:
+ if download_parquet:
+ logger.warning("Failed to download parquet, fallback on ARFF.")
+ arff_file = _get_dataset_arff(description)
+
+ remove_dataset_cache = False
+ except OpenMLServerException as e:
+ # if there was an exception
+ # check if the user had access to the dataset
+ if e.code == NO_ACCESS_GRANTED_ERRCODE:
+ raise OpenMLPrivateDatasetError(e.message) from None
+
+ raise e
+ finally:
+ if remove_dataset_cache:
+ _remove_cache_dir_for_id(DATASETS_CACHE_DIR_NAME, did_cache_dir)
- raise OpenMLCacheException("ARFF file for dataset id %d not "
- "cached" % dataset_id)
+ return _create_dataset_from_description(
+ description,
+ features_file,
+ qualities_file,
+ arff_file,
+ parquet_file,
+ cache_format,
+ )
-def list_datasets(offset=None, size=None, tag=None):
- """Return a list of all dataset which are on OpenML.
+def attributes_arff_from_df(df: pd.DataFrame) -> list[tuple[str, list[str] | str]]:
+ """Describe attributes of the dataframe according to ARFF specification.
Parameters
----------
- offset : int, optional
- the number of datasets to skip, starting from the first
- size : int, optional
- the maximum datasets of tasks to show
- tag : str, optional
- the tag to include
+ df : DataFrame, shape (n_samples, n_features)
+ The dataframe containing the data set.
Returns
-------
- datasets : list of dicts
- A list of datasets having the given tag (if applicable).
+ attributes_arff : list[str]
+ The data set attributes as required by the ARFF format.
+ """
+ PD_DTYPES_TO_ARFF_DTYPE = {"integer": "INTEGER", "floating": "REAL", "string": "STRING"}
+ attributes_arff: list[tuple[str, list[str] | str]] = []
+
+ if not all(isinstance(column_name, str) for column_name in df.columns):
+ logger.warning("Converting non-str column names to str.")
+ df.columns = [str(column_name) for column_name in df.columns]
+
+ for column_name in df:
+ # skipna=True does not infer properly the dtype. The NA values are
+ # dropped before the inference instead.
+ column_dtype = pd.api.types.infer_dtype(df[column_name].dropna(), skipna=False)
+
+ if column_dtype == "categorical":
+ # for categorical feature, arff expects a list string. However, a
+ # categorical column can contain mixed type and should therefore
+ # raise an error asking to convert all entries to string.
+ categories = df[column_name].cat.categories
+ categories_dtype = pd.api.types.infer_dtype(categories)
+ if categories_dtype not in ("string", "unicode"):
+ raise ValueError(
+ f"The column '{column_name}' of the dataframe is of "
+ "'category' dtype. Therefore, all values in "
+ "this columns should be string. Please "
+ "convert the entries which are not string. "
+ f"Got {categories_dtype} dtype in this column.",
+ )
+ attributes_arff.append((column_name, categories.tolist()))
+ elif column_dtype == "boolean":
+ # boolean are encoded as categorical.
+ attributes_arff.append((column_name, ["True", "False"]))
+ elif column_dtype in PD_DTYPES_TO_ARFF_DTYPE:
+ attributes_arff.append((column_name, PD_DTYPES_TO_ARFF_DTYPE[column_dtype]))
+ else:
+ raise ValueError(
+ f"The dtype '{column_dtype}' of the column '{column_name}' is not "
+ "currently supported by liac-arff. Supported "
+ "dtypes are categorical, string, integer, "
+ "floating, and boolean.",
+ )
+ return attributes_arff
+
+
+def create_dataset( # noqa: C901, PLR0912, PLR0915
+ name: str,
+ description: str | None,
+ creator: str | None,
+ contributor: str | None,
+ collection_date: str | None,
+ language: str | None,
+ licence: str | None,
+ # TODO(eddiebergman): Docstring says `type` but I don't know what this is other than strings
+ # Edit: Found it could also be like ["True", "False"]
+ attributes: list[tuple[str, str | list[str]]] | dict[str, str | list[str]] | Literal["auto"],
+ data: pd.DataFrame | np.ndarray | scipy.sparse.coo_matrix,
+ # TODO(eddiebergman): Function requires `default_target_attribute` exist but API allows None
+ default_target_attribute: str,
+ ignore_attribute: str | list[str] | None,
+ citation: str,
+ row_id_attribute: str | None = None,
+ original_data_url: str | None = None,
+ paper_url: str | None = None,
+ update_comment: str | None = None,
+ version_label: str | None = None,
+) -> OpenMLDataset:
+ """Create a dataset.
+
+ This function creates an OpenMLDataset object.
+ The OpenMLDataset object contains information related to the dataset
+ and the actual data file.
- Every dataset is represented by a dictionary containing
- the following information:
- - dataset id
- - status
+ Parameters
+ ----------
+ name : str
+ Name of the dataset.
+ description : str
+ Description of the dataset.
+ creator : str
+ The person who created the dataset.
+ contributor : str
+ People who contributed to the current version of the dataset.
+ collection_date : str
+ The date the data was originally collected, given by the uploader.
+ language : str
+ Language in which the data is represented.
+ Starts with 1 upper case letter, rest lower case, e.g. 'English'.
+ licence : str
+ License of the data.
+ attributes : list, dict, or 'auto'
+ A list of tuples. Each tuple consists of the attribute name and type.
+ If passing a pandas DataFrame, the attributes can be automatically
+ inferred by passing ``'auto'``. Specific attributes can be manually
+ specified by a passing a dictionary where the key is the name of the
+ attribute and the value is the data type of the attribute.
+ data : ndarray, list, dataframe, coo_matrix, shape (n_samples, n_features)
+ An array that contains both the attributes and the targets. When
+ providing a dataframe, the attribute names and type can be inferred by
+ passing ``attributes='auto'``.
+ The target feature is indicated as meta-data of the dataset.
+ default_target_attribute : str
+ The default target attribute, if it exists.
+ Can have multiple values, comma separated.
+ ignore_attribute : str | list
+ Attributes that should be excluded in modelling,
+ such as identifiers and indexes.
+ Can have multiple values, comma separated.
+ citation : str
+ Reference(s) that should be cited when building on this data.
+ version_label : str, optional
+ Version label provided by user.
+ Can be a date, hash, or some other type of id.
+ row_id_attribute : str, optional
+ The attribute that represents the row-id column, if present in the
+ dataset. If ``data`` is a dataframe and ``row_id_attribute`` is not
+ specified, the index of the dataframe will be used as the
+ ``row_id_attribute``. If the name of the index is ``None``, it will
+ be discarded.
+
+ .. versionadded: 0.8
+ Inference of ``row_id_attribute`` from a dataframe.
+ original_data_url : str, optional
+ For derived data, the url to the original dataset.
+ paper_url : str, optional
+ Link to a paper describing the dataset.
+ update_comment : str, optional
+ An explanation for when the dataset is uploaded.
- If qualities are calculated for the dataset, some of
- these are also returned.
+ Returns
+ -------
+ class:`openml.OpenMLDataset`
+ Dataset description.
"""
- api_call = "data/list"
- if offset is not None:
- api_call += "/offset/%d" % int(offset)
+ if isinstance(data, pd.DataFrame):
+ # infer the row id from the index of the dataset
+ if row_id_attribute is None:
+ row_id_attribute = data.index.name
+ # When calling data.values, the index will be skipped.
+ # We need to reset the index such that it is part of the data.
+ if data.index.name is not None:
+ data = data.reset_index()
+
+ if attributes == "auto" or isinstance(attributes, dict):
+ if not isinstance(data, pd.DataFrame):
+ raise ValueError(
+ "Automatically inferring attributes requires "
+ f"a pandas DataFrame. A {data!r} was given instead.",
+ )
+ # infer the type of data for each column of the DataFrame
+ attributes_ = attributes_arff_from_df(data)
+ if isinstance(attributes, dict):
+ # override the attributes which was specified by the user
+ for attr_idx in range(len(attributes_)):
+ attr_name = attributes_[attr_idx][0]
+ if attr_name in attributes:
+ attributes_[attr_idx] = (attr_name, attributes[attr_name])
+ else:
+ attributes_ = attributes
+ ignore_attributes = _expand_parameter(ignore_attribute)
+ _validated_data_attributes(ignore_attributes, attributes_, "ignore_attribute")
+
+ default_target_attributes = _expand_parameter(default_target_attribute)
+ _validated_data_attributes(default_target_attributes, attributes_, "default_target_attribute")
+
+ if row_id_attribute is not None:
+ is_row_id_an_attribute = any(attr[0] == row_id_attribute for attr in attributes_)
+ if not is_row_id_an_attribute:
+ raise ValueError(
+ "'row_id_attribute' should be one of the data attribute. "
+ f" Got '{row_id_attribute}' while candidates are"
+ f" {[attr[0] for attr in attributes_]}.",
+ )
+
+ if isinstance(data, pd.DataFrame):
+ if all(isinstance(dtype, pd.SparseDtype) for dtype in data.dtypes):
+ data = data.sparse.to_coo()
+ # liac-arff only support COO matrices with sorted rows
+ row_idx_sorted = np.argsort(data.row) # type: ignore
+ data.row = data.row[row_idx_sorted] # type: ignore
+ data.col = data.col[row_idx_sorted] # type: ignore
+ data.data = data.data[row_idx_sorted] # type: ignore
+ else:
+ data = data.to_numpy()
+
+ data_format: Literal["arff", "sparse_arff"]
+ if isinstance(data, (list, np.ndarray)):
+ if isinstance(data[0], (list, np.ndarray)):
+ data_format = "arff"
+ elif isinstance(data[0], dict):
+ data_format = "sparse_arff"
+ else:
+ raise ValueError(
+ "When giving a list or a numpy.ndarray, "
+ "they should contain a list/ numpy.ndarray "
+ "for dense data or a dictionary for sparse "
+ f"data. Got {data[0]!r} instead.",
+ )
+ elif isinstance(data, coo_matrix):
+ data_format = "sparse_arff"
+ else:
+ raise ValueError(
+ "When giving a list or a numpy.ndarray, "
+ "they should contain a list/ numpy.ndarray "
+ "for dense data or a dictionary for sparse "
+ f"data. Got {data[0]!r} instead.",
+ )
+
+ arff_object = {
+ "relation": name,
+ "description": description,
+ "attributes": attributes_,
+ "data": data,
+ }
+
+ # serializes the ARFF dataset object and returns a string
+ arff_dataset = arff.dumps(arff_object)
+ try:
+ # check if ARFF is valid
+ decoder = arff.ArffDecoder()
+ return_type = arff.COO if data_format == "sparse_arff" else arff.DENSE
+ decoder.decode(arff_dataset, encode_nominal=True, return_type=return_type)
+ except arff.ArffException as e:
+ raise ValueError(
+ "The arguments you have provided do not construct a valid ARFF file"
+ ) from e
+
+ return OpenMLDataset(
+ name=name,
+ description=description,
+ data_format=data_format,
+ creator=creator,
+ contributor=contributor,
+ collection_date=collection_date,
+ language=language,
+ licence=licence,
+ default_target_attribute=default_target_attribute,
+ row_id_attribute=row_id_attribute,
+ ignore_attribute=ignore_attribute,
+ citation=citation,
+ version_label=version_label,
+ original_data_url=original_data_url,
+ paper_url=paper_url,
+ update_comment=update_comment,
+ dataset=arff_dataset,
+ )
+
+
+def status_update(data_id: int, status: Literal["active", "deactivated"]) -> None:
+ """
+ Updates the status of a dataset to either 'active' or 'deactivated'.
+ Please see the OpenML API documentation for a description of the status
+ and all legal status transitions:
+ https://docs.openml.org/concepts/data/#dataset-status
- if size is not None:
- api_call += "/limit/%d" % int(size)
+ Parameters
+ ----------
+ data_id : int
+ The data id of the dataset
+ status : str,
+ 'active' or 'deactivated'
+ """
+ legal_status = {"active", "deactivated"}
+ if status not in legal_status:
+ raise ValueError(f"Illegal status value. Legal values: {legal_status}")
+
+ data: openml._api_calls.DATA_TYPE = {"data_id": data_id, "status": status}
+ result_xml = openml._api_calls._perform_api_call("data/status/update", "post", data=data)
+ result = xmltodict.parse(result_xml)
+ server_data_id = result["oml:data_status_update"]["oml:id"]
+ server_status = result["oml:data_status_update"]["oml:status"]
+ if status != server_status or int(data_id) != int(server_data_id):
+ # This should never happen
+ raise ValueError("Data id/status does not collide")
+
+
+def edit_dataset(
+ data_id: int,
+ description: str | None = None,
+ creator: str | None = None,
+ contributor: str | None = None,
+ collection_date: str | None = None,
+ language: str | None = None,
+ default_target_attribute: str | None = None,
+ ignore_attribute: str | list[str] | None = None,
+ citation: str | None = None,
+ row_id_attribute: str | None = None,
+ original_data_url: str | None = None,
+ paper_url: str | None = None,
+) -> int:
+ """Edits an OpenMLDataset.
+
+ In addition to providing the dataset id of the dataset to edit (through data_id),
+ you must specify a value for at least one of the optional function arguments,
+ i.e. one value for a field to edit.
+
+ This function allows editing of both non-critical and critical fields.
+ Critical fields are default_target_attribute, ignore_attribute, row_id_attribute.
+
+ - Editing non-critical data fields is allowed for all authenticated users.
+ - Editing critical fields is allowed only for the owner, provided there are no tasks
+ associated with this dataset.
+
+ If dataset has tasks or if the user is not the owner, the only way
+ to edit critical fields is to use fork_dataset followed by edit_dataset.
- if tag is not None:
- api_call += "/tag/%s" % tag
+ Parameters
+ ----------
+ data_id : int
+ ID of the dataset.
+ description : str
+ Description of the dataset.
+ creator : str
+ The person who created the dataset.
+ contributor : str
+ People who contributed to the current version of the dataset.
+ collection_date : str
+ The date the data was originally collected, given by the uploader.
+ language : str
+ Language in which the data is represented.
+ Starts with 1 upper case letter, rest lower case, e.g. 'English'.
+ default_target_attribute : str
+ The default target attribute, if it exists.
+ Can have multiple values, comma separated.
+ ignore_attribute : str | list
+ Attributes that should be excluded in modelling,
+ such as identifiers and indexes.
+ citation : str
+ Reference(s) that should be cited when building on this data.
+ row_id_attribute : str, optional
+ The attribute that represents the row-id column, if present in the
+ dataset. If ``data`` is a dataframe and ``row_id_attribute`` is not
+ specified, the index of the dataframe will be used as the
+ ``row_id_attribute``. If the name of the index is ``None``, it will
+ be discarded.
+
+ .. versionadded: 0.8
+ Inference of ``row_id_attribute`` from a dataframe.
+ original_data_url : str, optional
+ For derived data, the url to the original dataset.
+ paper_url : str, optional
+ Link to a paper describing the dataset.
- return _list_datasets(api_call)
+ Returns
+ -------
+ Dataset id
+ """
+ if not isinstance(data_id, int):
+ raise TypeError(f"`data_id` must be of type `int`, not {type(data_id)}.")
+
+ # compose data edit parameters as xml
+ form_data = {"data_id": data_id} # type: openml._api_calls.DATA_TYPE
+ xml = OrderedDict() # type: 'OrderedDict[str, OrderedDict]'
+ xml["oml:data_edit_parameters"] = OrderedDict()
+ xml["oml:data_edit_parameters"]["@xmlns:oml"] = "http://openml.org/openml"
+ xml["oml:data_edit_parameters"]["oml:description"] = description
+ xml["oml:data_edit_parameters"]["oml:creator"] = creator
+ xml["oml:data_edit_parameters"]["oml:contributor"] = contributor
+ xml["oml:data_edit_parameters"]["oml:collection_date"] = collection_date
+ xml["oml:data_edit_parameters"]["oml:language"] = language
+ xml["oml:data_edit_parameters"]["oml:default_target_attribute"] = default_target_attribute
+ xml["oml:data_edit_parameters"]["oml:row_id_attribute"] = row_id_attribute
+ xml["oml:data_edit_parameters"]["oml:ignore_attribute"] = ignore_attribute
+ xml["oml:data_edit_parameters"]["oml:citation"] = citation
+ xml["oml:data_edit_parameters"]["oml:original_data_url"] = original_data_url
+ xml["oml:data_edit_parameters"]["oml:paper_url"] = paper_url
+
+ # delete None inputs
+ for k in list(xml["oml:data_edit_parameters"]):
+ if not xml["oml:data_edit_parameters"][k]:
+ del xml["oml:data_edit_parameters"][k]
+
+ file_elements = {
+ "edit_parameters": ("description.xml", xmltodict.unparse(xml)),
+ } # type: openml._api_calls.FILE_ELEMENTS_TYPE
+ result_xml = openml._api_calls._perform_api_call(
+ "data/edit",
+ "post",
+ data=form_data,
+ file_elements=file_elements,
+ )
+ result = xmltodict.parse(result_xml)
+ data_id = result["oml:data_edit"]["oml:id"]
+ return int(data_id)
+
+
+def fork_dataset(data_id: int) -> int:
+ """
+ Creates a new dataset version, with the authenticated user as the new owner.
+ The forked dataset can have distinct dataset meta-data,
+ but the actual data itself is shared with the original version.
+ This API is intended for use when a user is unable to edit the critical fields of a dataset
+ through the edit_dataset API.
+ (Critical fields are default_target_attribute, ignore_attribute, row_id_attribute.)
-def _list_datasets(api_call):
- # TODO add proper error handling here!
- return_code, xml_string = _perform_api_call(api_call)
- datasets_dict = xmltodict.parse(xml_string)
+ Specifically, this happens when the user is:
+ 1. Not the owner of the dataset.
+ 2. User is the owner of the dataset, but the dataset has tasks.
- # Minimalistic check if the XML is useful
- assert type(datasets_dict['oml:data']['oml:dataset']) == list, \
- type(datasets_dict['oml:data'])
- assert datasets_dict['oml:data']['@xmlns:oml'] == \
- 'http://openml.org/openml', datasets_dict['oml:data']['@xmlns:oml']
-
- datasets = dict()
- for dataset_ in datasets_dict['oml:data']['oml:dataset']:
- did = int(dataset_['oml:did'])
- dataset = {'did': did,
- 'name': dataset_['oml:name'],
- 'format': dataset_['oml:format'],
- 'status': dataset_['oml:status']}
+ In these two cases the only way to edit critical fields is:
+ 1. STEP 1: Fork the dataset using fork_dataset API
+ 2. STEP 2: Call edit_dataset API on the forked version.
- # The number of qualities can range from 0 to infinity
- for quality in dataset_.get('oml:quality', list()):
- quality['#text'] = float(quality['#text'])
- if abs(int(quality['#text']) - quality['#text']) < 0.0000001:
- quality['#text'] = int(quality['#text'])
- dataset[quality['@name']] = quality['#text']
- datasets[did] = dataset
- return datasets
+ Parameters
+ ----------
+ data_id : int
+ id of the dataset to be forked
+ Returns
+ -------
+ Dataset id of the forked dataset
-def check_datasets_active(dataset_ids):
- """Check if the dataset ids provided are active.
+ """
+ if not isinstance(data_id, int):
+ raise TypeError(f"`data_id` must be of type `int`, not {type(data_id)}.")
+ # compose data fork parameters
+ form_data = {"data_id": data_id} # type: openml._api_calls.DATA_TYPE
+ result_xml = openml._api_calls._perform_api_call("data/fork", "post", data=form_data)
+ result = xmltodict.parse(result_xml)
+ data_id = result["oml:data_fork"]["oml:id"]
+ return int(data_id)
+
+
+def data_feature_add_ontology(data_id: int, index: int, ontology: str) -> bool:
+ """
+ An ontology describes the concept that are described in a feature. An
+ ontology is defined by an URL where the information is provided. Adds
+ an ontology (URL) to a given dataset feature (defined by a dataset id
+ and index). The dataset has to exists on OpenML and needs to have been
+ processed by the evaluation engine.
Parameters
----------
- dataset_id : iterable
- Integers representing dataset ids.
+ data_id : int
+ id of the dataset to which the feature belongs
+ index : int
+ index of the feature in dataset (0-based)
+ ontology : str
+ URL to ontology (max. 256 characters)
Returns
-------
- dict
- A dictionary with items {did: bool}
+ True or throws an OpenML server exception
"""
- dataset_list = list_datasets()
- dataset_ids = sorted(dataset_ids)
- active = {}
-
- for dataset in dataset_list:
- active[dataset['did']] = dataset['status'] == 'active'
+ upload_data: dict[str, int | str] = {"data_id": data_id, "index": index, "ontology": ontology}
+ openml._api_calls._perform_api_call("data/feature/ontology/add", "post", data=upload_data)
+ # an error will be thrown in case the request was unsuccessful
+ return True
- for did in dataset_ids:
- if did not in active:
- raise ValueError('Could not find dataset %d in OpenML dataset list.'
- % did)
- active = {did: active[did] for did in dataset_ids}
+def data_feature_remove_ontology(data_id: int, index: int, ontology: str) -> bool:
+ """
+ Removes an existing ontology (URL) from a given dataset feature (defined
+ by a dataset id and index). The dataset has to exists on OpenML and needs
+ to have been processed by the evaluation engine. Ontology needs to be
+ attached to the specific fearure.
- return active
+ Parameters
+ ----------
+ data_id : int
+ id of the dataset to which the feature belongs
+ index : int
+ index of the feature in dataset (0-based)
+ ontology : str
+ URL to ontology (max. 256 characters)
+ Returns
+ -------
+ True or throws an OpenML server exception
+ """
+ upload_data: dict[str, int | str] = {"data_id": data_id, "index": index, "ontology": ontology}
+ openml._api_calls._perform_api_call("data/feature/ontology/remove", "post", data=upload_data)
+ # an error will be thrown in case the request was unsuccessful
+ return True
-def get_datasets(dataset_ids):
- """Download datasets.
- This function iterates :meth:`openml.datasets.get_dataset`.
+def _topic_add_dataset(data_id: int, topic: str) -> int:
+ """
+ Adds a topic for a dataset.
+ This API is not available for all OpenML users and is accessible only by admins.
Parameters
----------
- dataset_ids : iterable
- Integers representing dataset ids.
+ data_id : int
+ id of the dataset for which the topic needs to be added
+ topic : str
+ Topic to be added for the dataset
Returns
-------
- datasets : list of datasets
- A list of dataset objects.
+ Dataset id
"""
- datasets = []
- for dataset_id in dataset_ids:
- datasets.append(get_dataset(dataset_id))
- return datasets
-
+ if not isinstance(data_id, int):
+ raise TypeError(f"`data_id` must be of type `int`, not {type(data_id)}.")
+ form_data = {"data_id": data_id, "topic": topic} # type: openml._api_calls.DATA_TYPE
+ result_xml = openml._api_calls._perform_api_call("data/topicadd", "post", data=form_data)
+ result = xmltodict.parse(result_xml)
+ data_id = result["oml:data_topic"]["oml:id"]
+ return int(data_id)
-def get_dataset(dataset_id):
- """Download a dataset.
- TODO: explain caching!
+def _topic_delete_dataset(data_id: int, topic: str) -> int:
+ """
+ Removes a topic from a dataset.
+ This API is not available for all OpenML users and is accessible only by admins.
Parameters
----------
- ddataset_id : int
- Dataset ID of the dataset to download
+ data_id : int
+ id of the dataset to be forked
+ topic : str
+ Topic to be deleted
Returns
-------
- dataset : :class:`openml.OpenMLDataset`
- The downloaded dataset."""
- try:
- dataset_id = int(dataset_id)
- except:
- raise ValueError("Dataset ID is neither an Integer nor can be "
- "cast to an Integer.")
-
- did_cache_dir = _create_dataset_cache_directory(dataset_id)
-
- try:
- description = _get_dataset_description(did_cache_dir, dataset_id)
- arff_file = _get_dataset_arff(did_cache_dir, description)
- features = _get_dataset_features(did_cache_dir, dataset_id)
- # TODO not used yet, figure out what to do with this...
- qualities = _get_dataset_qualities(did_cache_dir, dataset_id)
- except Exception as e:
- _remove_dataset_cache_dir(did_cache_dir)
- raise e
+ Dataset id
+ """
+ if not isinstance(data_id, int):
+ raise TypeError(f"`data_id` must be of type `int`, not {type(data_id)}.")
+ form_data = {"data_id": data_id, "topic": topic} # type: openml._api_calls.DATA_TYPE
+ result_xml = openml._api_calls._perform_api_call("data/topicdelete", "post", data=form_data)
+ result = xmltodict.parse(result_xml)
+ data_id = result["oml:data_topic"]["oml:id"]
+ return int(data_id)
- dataset = _create_dataset_from_description(description, features, arff_file)
- return dataset
+def _get_dataset_description(did_cache_dir: Path, dataset_id: int) -> dict[str, Any]:
+ """Get the dataset description as xml dictionary.
-def _get_dataset_description(did_cache_dir, dataset_id):
- """Get the dataset description as xml dictionary
+ This function is NOT thread/multiprocessing safe.
Parameters
----------
- did_cache_dir : str
+ did_cache_dir : Path
Cache subdirectory for this dataset.
dataset_id : int
@@ -300,80 +1088,177 @@ def _get_dataset_description(did_cache_dir, dataset_id):
XML Dataset description parsed to a dict.
"""
-
- # TODO implement a cache for this that invalidates itself after some
- # time
+ # TODO implement a cache for this that invalidates itself after some time
# This can be saved on disk, but cannot be cached properly, because
# it contains the information on whether a dataset is active.
- description_file = os.path.join(did_cache_dir, "description.xml")
+ description_file = did_cache_dir / "description.xml"
try:
- return _get_cached_dataset_description(dataset_id)
- except (OpenMLCacheException):
- return_code, dataset_xml = _perform_api_call(
- "data/%d" % dataset_id)
+ with description_file.open(encoding="utf8") as fh:
+ dataset_xml = fh.read()
+ description = xmltodict.parse(dataset_xml)["oml:data_set_description"]
+ except Exception: # noqa: BLE001
+ url_extension = f"data/{dataset_id}"
+ dataset_xml = openml._api_calls._perform_api_call(url_extension, "get")
+ try:
+ description = xmltodict.parse(dataset_xml)["oml:data_set_description"]
+ except ExpatError as e:
+ url = openml._api_calls._create_url_from_endpoint(url_extension)
+ raise OpenMLServerError(f"Dataset description XML at '{url}' is malformed.") from e
- with io.open(description_file, "w", encoding='utf8') as fh:
+ with description_file.open("w", encoding="utf8") as fh:
fh.write(dataset_xml)
- description = xmltodict.parse(dataset_xml)[
- "oml:data_set_description"]
+ return description # type: ignore
+
+
+def _get_dataset_parquet(
+ description: dict | OpenMLDataset,
+ cache_directory: Path | None = None,
+ download_all_files: bool = False, # noqa: FBT002
+) -> Path | None:
+ """Return the path to the local parquet file of the dataset. If is not cached, it is downloaded.
+
+ Checks if the file is in the cache, if yes, return the path to the file.
+ If not, downloads the file and caches it, then returns the file path.
+ The cache directory is generated based on dataset information, but can also be specified.
+
+ This function is NOT thread/multiprocessing safe.
+ Unlike the ARFF equivalent, checksums are not available/used (for now).
+
+ Parameters
+ ----------
+ description : dictionary or OpenMLDataset
+ Either a dataset description as dict or OpenMLDataset.
+
+ cache_directory: Path, optional (default=None)
+ Folder to store the parquet file in.
+ If None, use the default cache directory for the dataset.
+
+ download_all_files: bool, optional (default=False)
+ If `True`, download all data found in the bucket to which the description's
+ ``parquet_url`` points, only download the parquet file otherwise.
+
+ Returns
+ -------
+ output_filename : Path, optional
+ Location of the Parquet file if successfully downloaded, None otherwise.
+ """
+ if isinstance(description, dict):
+ url = str(description.get("oml:parquet_url"))
+ did = int(description.get("oml:id")) # type: ignore
+ elif isinstance(description, OpenMLDataset):
+ url = str(description._parquet_url)
+ assert description.dataset_id is not None
+
+ did = int(description.dataset_id)
+ else:
+ raise TypeError("`description` should be either OpenMLDataset or Dict.")
- with io.open(description_file, "w", encoding='utf8') as fh:
- fh.write(dataset_xml)
+ if cache_directory is None:
+ cache_directory = _create_cache_directory_for_id(DATASETS_CACHE_DIR_NAME, did)
- return description
+ output_file_path = cache_directory / f"dataset_{did}.pq"
+ old_file_path = cache_directory / "dataset.pq"
+ if old_file_path.is_file():
+ old_file_path.rename(output_file_path)
-def _get_dataset_arff(did_cache_dir, description):
- """Get the filepath to the dataset arff
+ # The call below skips files already on disk, so avoids downloading the parquet file twice.
+ # To force the old behavior of always downloading everything, use `force_refresh_cache`
+ # of `get_dataset`
+ if download_all_files:
+ openml._api_calls._download_minio_bucket(source=url, destination=cache_directory)
- Checks if the file is in the cache, if yes, return the path to the file. If
- not, downloads the file and caches it, then returns the file path.
+ if not output_file_path.is_file():
+ try:
+ openml._api_calls._download_minio_file(
+ source=url,
+ destination=output_file_path,
+ )
+ except (FileNotFoundError, urllib3.exceptions.MaxRetryError, minio.error.ServerError) as e:
+ logger.warning(f"Could not download file from {url}: {e}")
+ return None
+ return output_file_path
+
+
+def _get_dataset_arff(
+ description: dict | OpenMLDataset,
+ cache_directory: Path | None = None,
+) -> Path:
+ """Return the path to the local arff file of the dataset. If is not cached, it is downloaded.
+
+ Checks if the file is in the cache, if yes, return the path to the file.
+ If not, downloads the file and caches it, then returns the file path.
+ The cache directory is generated based on dataset information, but can also be specified.
+
+ This function is NOT thread/multiprocessing safe.
Parameters
----------
- did_cache_dir : str
- Cache subdirectory for this dataset.
+ description : dictionary or OpenMLDataset
+ Either a dataset description as dict or OpenMLDataset.
- description : dictionary
- Dataset description dict.
+ cache_directory: Path, optional (default=None)
+ Folder to store the arff file in.
+ If None, use the default cache directory for the dataset.
Returns
-------
- output_filename : string
- Location of arff file.
+ output_filename : Path
+ Location of ARFF file.
"""
- output_file_path = os.path.join(did_cache_dir, "dataset.arff")
+ if isinstance(description, dict):
+ md5_checksum_fixture = description.get("oml:md5_checksum")
+ url = str(description["oml:url"])
+ did = int(description.get("oml:id")) # type: ignore
+ elif isinstance(description, OpenMLDataset):
+ md5_checksum_fixture = description.md5_checksum
+ assert description.url is not None
+ assert description.dataset_id is not None
+
+ url = description.url
+ did = int(description.dataset_id)
+ else:
+ raise TypeError("`description` should be either OpenMLDataset or Dict.")
+
+ save_cache_directory = (
+ _create_cache_directory_for_id(DATASETS_CACHE_DIR_NAME, did)
+ if cache_directory is None
+ else Path(cache_directory)
+ )
+ output_file_path = save_cache_directory / "dataset.arff"
- # This means the file is still there; whether it is useful is up to
- # the user and not checked by the program.
try:
- with io.open(output_file_path, encoding='utf8'):
- pass
- return output_file_path
- except (OSError, IOError):
- pass
+ openml._api_calls._download_text_file(
+ source=url,
+ output_path=output_file_path,
+ md5_checksum=md5_checksum_fixture,
+ )
+ except OpenMLHashException as e:
+ additional_info = f" Raised when downloading dataset {did}."
+ e.args = (e.args[0] + additional_info,)
+ raise e
- url = description['oml:url']
- return_code, arff_string = _read_url(url)
+ return output_file_path
- with io.open(output_file_path, "w", encoding='utf8') as fh:
- fh.write(arff_string)
- del arff_string
- return output_file_path
+def _get_features_xml(dataset_id: int) -> str:
+ url_extension = f"data/features/{dataset_id}"
+ return openml._api_calls._perform_api_call(url_extension, "get")
-def _get_dataset_features(did_cache_dir, dataset_id):
- """API call to get dataset features (cached)
+def _get_dataset_features_file(did_cache_dir: str | Path | None, dataset_id: int) -> Path:
+ """API call to load dataset features. Loads from cache or downloads them.
Features are feature descriptions for each column.
(name, index, categorical, ...)
+ This function is NOT thread/multiprocessing safe.
+
Parameters
----------
- did_cache_dir : str
+ did_cache_dir : str or None
Cache subdirectory for this dataset
dataset_id : int
@@ -381,35 +1266,43 @@ def _get_dataset_features(did_cache_dir, dataset_id):
Returns
-------
- features : dict
- Dictionary containing dataset feature descriptions, parsed from XML.
+ Path
+ Path of the cached dataset feature file
"""
- features_file = os.path.join(did_cache_dir, "features.xml")
+ did_cache_dir = Path(did_cache_dir) if did_cache_dir is not None else None
+ if did_cache_dir is None:
+ did_cache_dir = _create_cache_directory_for_id(DATASETS_CACHE_DIR_NAME, dataset_id)
- # Dataset features aren't subject to change...
- try:
- with io.open(features_file, encoding='utf8') as fh:
- features_xml = fh.read()
- except (OSError, IOError):
- return_code, features_xml = _perform_api_call(
- "data/features/%d" % dataset_id)
+ features_file = did_cache_dir / "features.xml"
- with io.open(features_file, "w", encoding='utf8') as fh:
+ # Dataset features aren't subject to change...
+ if not features_file.is_file():
+ features_xml = _get_features_xml(dataset_id)
+ with features_file.open("w", encoding="utf8") as fh:
fh.write(features_xml)
- features = xmltodict.parse(features_xml)["oml:data_features"]
+ return features_file
+
- return features
+def _get_qualities_xml(dataset_id: int) -> str:
+ url_extension = f"data/qualities/{dataset_id!s}"
+ return openml._api_calls._perform_api_call(url_extension, "get")
-def _get_dataset_qualities(did_cache_dir, dataset_id):
- """API call to get dataset qualities (cached)
+def _get_dataset_qualities_file(
+ did_cache_dir: str | Path | None,
+ dataset_id: int,
+) -> Path | None:
+ """Get the path for the dataset qualities file, or None if no qualities exist.
+ Loads from cache or downloads them.
Features are metafeatures (number of features, number of classes, ...)
+ This function is NOT thread/multiprocessing safe.
+
Parameters
----------
- did_cache_dir : str
+ did_cache_dir : str or None
Cache subdirectory for this dataset
dataset_id : int
@@ -417,108 +1310,153 @@ def _get_dataset_qualities(did_cache_dir, dataset_id):
Returns
-------
- qualities : dict
- Dictionary containing dataset qualities, parsed from XML.
+ str
+ Path of the cached qualities file
"""
+ save_did_cache_dir = (
+ _create_cache_directory_for_id(DATASETS_CACHE_DIR_NAME, dataset_id)
+ if did_cache_dir is None
+ else Path(did_cache_dir)
+ )
+
# Dataset qualities are subject to change and must be fetched every time
- qualities_file = os.path.join(did_cache_dir, "qualities.xml")
+ qualities_file = save_did_cache_dir / "qualities.xml"
try:
- with io.open(qualities_file, encoding='utf8') as fh:
+ with qualities_file.open(encoding="utf8") as fh:
qualities_xml = fh.read()
- except (OSError, IOError):
- return_code, qualities_xml = _perform_api_call(
- "data/qualities/%d" % dataset_id)
-
- with io.open(qualities_file, "w", encoding='utf8') as fh:
- fh.write(qualities_xml)
-
- qualities = xmltodict.parse(qualities_xml)['oml:data_qualities']
-
- return qualities
-
+ except OSError:
+ try:
+ qualities_xml = _get_qualities_xml(dataset_id)
+ with qualities_file.open("w", encoding="utf8") as fh:
+ fh.write(qualities_xml)
+ except OpenMLServerException as e:
+ if e.code == 362 and str(e) == "No qualities found - None":
+ # quality file stays as None
+ logger.warning(f"No qualities found for dataset {dataset_id}")
+ return None
+
+ raise e
+
+ return qualities_file
+
+
+def _create_dataset_from_description(
+ description: dict[str, str],
+ features_file: Path | None = None,
+ qualities_file: Path | None = None,
+ arff_file: Path | None = None,
+ parquet_file: Path | None = None,
+ cache_format: Literal["pickle", "feather"] = "pickle",
+) -> OpenMLDataset:
+ """Create a dataset object from a description dict.
-def _create_dataset_cache_directory(dataset_id):
- """Create a dataset cache directory
+ Parameters
+ ----------
+ description : dict
+ Description of a dataset in xml dict.
+ features_file : str
+ Path of the dataset features as xml file.
+ qualities_file : list
+ Path of the dataset qualities as xml file.
+ arff_file : string, optional
+ Path of dataset ARFF file.
+ parquet_file : string, optional
+ Path of dataset Parquet file.
+ cache_format: string, optional
+ Caching option for datasets (feather/pickle)
- In order to have a clearer cache structure and because every dataset
- is cached in several files (description, arff, features, qualities), there
- is a directory for each dataset witch the dataset ID being the directory
- name. This function creates this cache directory.
+ Returns
+ -------
+ dataset : dataset object
+ Dataset object from dict and ARFF.
+ """
+ return OpenMLDataset(
+ description["oml:name"],
+ description.get("oml:description"),
+ data_format=description["oml:format"], # type: ignore
+ dataset_id=int(description["oml:id"]),
+ version=int(description["oml:version"]),
+ creator=description.get("oml:creator"),
+ contributor=description.get("oml:contributor"),
+ collection_date=description.get("oml:collection_date"),
+ upload_date=description.get("oml:upload_date"),
+ language=description.get("oml:language"),
+ licence=description.get("oml:licence"),
+ url=description["oml:url"],
+ default_target_attribute=description.get("oml:default_target_attribute"),
+ row_id_attribute=description.get("oml:row_id_attribute"),
+ ignore_attribute=description.get("oml:ignore_attribute"),
+ version_label=description.get("oml:version_label"),
+ citation=description.get("oml:citation"),
+ tag=description.get("oml:tag"),
+ visibility=description.get("oml:visibility"),
+ original_data_url=description.get("oml:original_data_url"),
+ paper_url=description.get("oml:paper_url"),
+ update_comment=description.get("oml:update_comment"),
+ md5_checksum=description.get("oml:md5_checksum"),
+ data_file=str(arff_file) if arff_file is not None else None,
+ cache_format=cache_format,
+ features_file=str(features_file) if features_file is not None else None,
+ qualities_file=str(qualities_file) if qualities_file is not None else None,
+ parquet_url=description.get("oml:parquet_url"),
+ parquet_file=str(parquet_file) if parquet_file is not None else None,
+ )
+
+
+def _get_online_dataset_arff(dataset_id: int) -> str | None:
+ """Download the ARFF file for a given dataset id
+ from the OpenML website.
Parameters
----------
- did : int
- Dataset ID
+ dataset_id : int
+ A dataset id.
Returns
-------
- str
- Path of the created dataset cache directory.
+ str or None
+ A string representation of an ARFF file. Or None if file already exists.
"""
- dataset_cache_dir = os.path.join(config.get_cache_directory(), "datasets", str(dataset_id))
- try:
- os.makedirs(dataset_cache_dir)
- except (OSError, IOError):
- # TODO add debug information!
- pass
- return dataset_cache_dir
+ dataset_xml = openml._api_calls._perform_api_call(f"data/{dataset_id}", "get")
+ # build a dict from the xml.
+ # use the url from the dataset description and return the ARFF string
+ return openml._api_calls._download_text_file(
+ xmltodict.parse(dataset_xml)["oml:data_set_description"]["oml:url"],
+ )
-def _remove_dataset_cache_dir(did_cache_dir):
- """Remove the dataset cache directory
+def _get_online_dataset_format(dataset_id: int) -> str:
+ """Get the dataset format for a given dataset id from the OpenML website.
Parameters
----------
+ dataset_id : int
+ A dataset id.
+
+ Returns
+ -------
+ str
+ Dataset format.
"""
- try:
- os.rmdir(did_cache_dir)
- except (OSError, IOError):
- try:
- shutil.rmtree(did_cache_dir)
- except (OSError, IOError):
- raise ValueError('Cannot remove faulty dataset cache directory %s.'
- 'Please do this manually!' % did_cache_dir)
+ dataset_xml = openml._api_calls._perform_api_call(f"data/{dataset_id}", "get")
+ # build a dict from the xml and get the format from the dataset description
+ return xmltodict.parse(dataset_xml)["oml:data_set_description"]["oml:format"].lower() # type: ignore
-def _create_dataset_from_description(description, features, arff_file):
- """Create a dataset object from a description dict.
+def delete_dataset(dataset_id: int) -> bool:
+ """Delete dataset with id `dataset_id` from the OpenML server.
+
+ This can only be done if you are the owner of the dataset and
+ no tasks are attached to the dataset.
Parameters
----------
- description : dict
- Description of a dataset in xmlish dict.
- arff_file : string
- Path of dataset arff file.
+ dataset_id : int
+ OpenML id of the dataset
Returns
-------
- dataset : dataset object
- Dataset object from dict and arff.
+ bool
+ True if the deletion was successful. False otherwise.
"""
- dataset = OpenMLDataset(
- description["oml:id"],
- description["oml:name"],
- description["oml:version"],
- description.get("oml:description"),
- description["oml:format"],
- description.get("oml:creator"),
- description.get("oml:contributor"),
- description.get("oml:collection_date"),
- description.get("oml:upload_date"),
- description.get("oml:language"),
- description.get("oml:licence"),
- description["oml:url"],
- description.get("oml:default_target_attribute"),
- description.get("oml:row_id_attribute"),
- description.get("oml:ignore_attribute"),
- description.get("oml:version_label"),
- description.get("oml:citation"),
- description.get("oml:tag"),
- description.get("oml:visibility"),
- description.get("oml:original_data_url"),
- description.get("oml:paper_url"),
- description.get("oml:update_comment"),
- description.get("oml:md5_checksum"),
- data_file=arff_file,
- features=features)
- return dataset
+ return openml.utils._delete_entity("data", dataset_id)
diff --git a/openml/evaluations/__init__.py b/openml/evaluations/__init__.py
new file mode 100644
index 000000000..b56d0c2d5
--- /dev/null
+++ b/openml/evaluations/__init__.py
@@ -0,0 +1,11 @@
+# License: BSD 3-Clause
+
+from .evaluation import OpenMLEvaluation
+from .functions import list_evaluation_measures, list_evaluations, list_evaluations_setups
+
+__all__ = [
+ "OpenMLEvaluation",
+ "list_evaluation_measures",
+ "list_evaluations",
+ "list_evaluations_setups",
+]
diff --git a/openml/evaluations/evaluation.py b/openml/evaluations/evaluation.py
new file mode 100644
index 000000000..5db087024
--- /dev/null
+++ b/openml/evaluations/evaluation.py
@@ -0,0 +1,110 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+from dataclasses import asdict, dataclass
+
+import openml.config
+import openml.datasets
+import openml.flows
+import openml.runs
+import openml.tasks
+
+
+@dataclass
+class OpenMLEvaluation:
+ """
+ Contains all meta-information about a run / evaluation combination,
+ according to the evaluation/list function
+
+ Parameters
+ ----------
+ run_id : int
+ Refers to the run.
+ task_id : int
+ Refers to the task.
+ setup_id : int
+ Refers to the setup.
+ flow_id : int
+ Refers to the flow.
+ flow_name : str
+ Name of the referred flow.
+ data_id : int
+ Refers to the dataset.
+ data_name : str
+ The name of the dataset.
+ function : str
+ The evaluation metric of this item (e.g., accuracy).
+ upload_time : str
+ The time of evaluation.
+ uploader: int
+ Uploader ID (user ID)
+ upload_name : str
+ Name of the uploader of this evaluation
+ value : float
+ The value (score) of this evaluation.
+ values : List[float]
+ The values (scores) per repeat and fold (if requested)
+ array_data : str
+ list of information per class.
+ (e.g., in case of precision, auroc, recall)
+ """
+
+ run_id: int
+ task_id: int
+ setup_id: int
+ flow_id: int
+ flow_name: str
+ data_id: int
+ data_name: str
+ function: str
+ upload_time: str
+ uploader: int
+ uploader_name: str
+ value: float | None
+ values: list[float] | None
+ array_data: str | None = None
+
+ def _to_dict(self) -> dict:
+ return asdict(self)
+
+ def __repr__(self) -> str:
+ header = "OpenML Evaluation"
+ header = f"{header}\n{'=' * len(header)}\n"
+
+ fields = {
+ "Upload Date": self.upload_time,
+ "Run ID": self.run_id,
+ "OpenML Run URL": openml.runs.OpenMLRun.url_for_id(self.run_id),
+ "Task ID": self.task_id,
+ "OpenML Task URL": openml.tasks.OpenMLTask.url_for_id(self.task_id),
+ "Flow ID": self.flow_id,
+ "OpenML Flow URL": openml.flows.OpenMLFlow.url_for_id(self.flow_id),
+ "Setup ID": self.setup_id,
+ "Data ID": self.data_id,
+ "Data Name": self.data_name,
+ "OpenML Data URL": openml.datasets.OpenMLDataset.url_for_id(self.data_id),
+ "Metric Used": self.function,
+ "Result": self.value,
+ }
+
+ order = [
+ "Upload Date",
+ "Run ID",
+ "OpenML Run URL",
+ "Task ID",
+ "OpenML Task URL",
+ "Flow ID",
+ "OpenML Flow URL",
+ "Setup ID",
+ "Data ID",
+ "Data Name",
+ "OpenML Data URL",
+ "Metric Used",
+ "Result",
+ ]
+ _fields = [(key, fields[key]) for key in order if key in fields]
+
+ longest_field_name_length = max(len(name) for name, _ in _fields)
+ field_line_format = f"{{:.<{longest_field_name_length}}}: {{}}"
+ body = "\n".join(field_line_format.format(name, value) for name, value in _fields)
+ return header + body
diff --git a/openml/evaluations/functions.py b/openml/evaluations/functions.py
new file mode 100644
index 000000000..61c95a480
--- /dev/null
+++ b/openml/evaluations/functions.py
@@ -0,0 +1,438 @@
+# License: BSD 3-Clause
+# ruff: noqa: PLR0913
+from __future__ import annotations
+
+import json
+from functools import partial
+from itertools import chain
+from typing import Any, Literal
+from typing_extensions import overload
+
+import numpy as np
+import pandas as pd
+import xmltodict
+
+import openml
+import openml._api_calls
+import openml.utils
+from openml.evaluations import OpenMLEvaluation
+
+
+@overload
+def list_evaluations(
+ function: str,
+ offset: int | None = None,
+ size: int | None = None,
+ tasks: list[str | int] | None = None,
+ setups: list[str | int] | None = None,
+ flows: list[str | int] | None = None,
+ runs: list[str | int] | None = None,
+ uploaders: list[str | int] | None = None,
+ tag: str | None = None,
+ study: int | None = None,
+ per_fold: bool | None = None,
+ sort_order: str | None = None,
+ output_format: Literal["dataframe"] = ...,
+) -> pd.DataFrame: ...
+
+
+@overload
+def list_evaluations(
+ function: str,
+ offset: int | None = None,
+ size: int | None = None,
+ tasks: list[str | int] | None = None,
+ setups: list[str | int] | None = None,
+ flows: list[str | int] | None = None,
+ runs: list[str | int] | None = None,
+ uploaders: list[str | int] | None = None,
+ tag: str | None = None,
+ study: int | None = None,
+ per_fold: bool | None = None,
+ sort_order: str | None = None,
+ output_format: Literal["object"] = "object",
+) -> dict[int, OpenMLEvaluation]: ...
+
+
+def list_evaluations(
+ function: str,
+ offset: int | None = None,
+ size: int | None = None,
+ tasks: list[str | int] | None = None,
+ setups: list[str | int] | None = None,
+ flows: list[str | int] | None = None,
+ runs: list[str | int] | None = None,
+ uploaders: list[str | int] | None = None,
+ tag: str | None = None,
+ study: int | None = None,
+ per_fold: bool | None = None,
+ sort_order: str | None = None,
+ output_format: Literal["object", "dataframe"] = "object",
+) -> dict[int, OpenMLEvaluation] | pd.DataFrame:
+ """List all run-evaluation pairs matching all of the given filters.
+
+ (Supports large amount of results)
+
+ Parameters
+ ----------
+ function : str
+ the evaluation function. e.g., predictive_accuracy
+ offset : int, optional
+ the number of runs to skip, starting from the first
+ size : int, default 10000
+ The maximum number of runs to show.
+ If set to ``None``, it returns all the results.
+
+ tasks : list[int,str], optional
+ the list of task IDs
+ setups: list[int,str], optional
+ the list of setup IDs
+ flows : list[int,str], optional
+ the list of flow IDs
+ runs :list[int,str], optional
+ the list of run IDs
+ uploaders : list[int,str], optional
+ the list of uploader IDs
+ tag : str, optional
+ filter evaluation based on given tag
+
+ study : int, optional
+
+ per_fold : bool, optional
+
+ sort_order : str, optional
+ order of sorting evaluations, ascending ("asc") or descending ("desc")
+
+ output_format: str, optional (default='object')
+ The parameter decides the format of the output.
+ - If 'object' the output is a dict of OpenMLEvaluation objects
+ - If 'dataframe' the output is a pandas DataFrame
+
+ Returns
+ -------
+ dict or dataframe
+ """
+ if output_format not in ("dataframe", "object"):
+ raise ValueError("Invalid output format. Only 'object', 'dataframe'.")
+
+ per_fold_str = None
+ if per_fold is not None:
+ per_fold_str = str(per_fold).lower()
+
+ listing_call = partial(
+ _list_evaluations,
+ function=function,
+ tasks=tasks,
+ setups=setups,
+ flows=flows,
+ runs=runs,
+ uploaders=uploaders,
+ tag=tag,
+ study=study,
+ sort_order=sort_order,
+ per_fold=per_fold_str,
+ )
+ eval_collection = openml.utils._list_all(listing_call, offset=offset, limit=size)
+
+ flattened = list(chain.from_iterable(eval_collection))
+ if output_format == "dataframe":
+ records = [item._to_dict() for item in flattened]
+ return pd.DataFrame.from_records(records) # No index...
+
+ return {e.run_id: e for e in flattened}
+
+
+def _list_evaluations( # noqa: C901
+ limit: int,
+ offset: int,
+ *,
+ function: str,
+ tasks: list | None = None,
+ setups: list | None = None,
+ flows: list | None = None,
+ runs: list | None = None,
+ uploaders: list | None = None,
+ study: int | None = None,
+ sort_order: str | None = None,
+ **kwargs: Any,
+) -> list[OpenMLEvaluation]:
+ """
+ Perform API call ``/evaluation/function{function}/{filters}``
+
+ Parameters
+ ----------
+ The arguments that are lists are separated from the single value
+ ones which are put into the kwargs.
+
+ limit : int
+ the number of evaluations to return
+ offset : int
+ the number of evaluations to skip, starting from the first
+ function : str
+ the evaluation function. e.g., predictive_accuracy
+
+ tasks : list[int,str], optional
+ the list of task IDs
+ setups: list[int,str], optional
+ the list of setup IDs
+ flows : list[int,str], optional
+ the list of flow IDs
+ runs :list[int,str], optional
+ the list of run IDs
+ uploaders : list[int,str], optional
+ the list of uploader IDs
+
+ study : int, optional
+
+ kwargs: dict, optional
+ Legal filter operators: tag, per_fold
+
+ sort_order : str, optional
+ order of sorting evaluations, ascending ("asc") or descending ("desc")
+
+ Returns
+ -------
+ list of OpenMLEvaluation objects
+ """
+ api_call = f"evaluation/list/function/{function}"
+ if limit is not None:
+ api_call += f"/limit/{limit}"
+ if offset is not None:
+ api_call += f"/offset/{offset}"
+ if kwargs is not None:
+ for operator, value in kwargs.items():
+ if value is not None:
+ api_call += f"/{operator}/{value}"
+ if tasks is not None:
+ api_call += f"/task/{','.join([str(int(i)) for i in tasks])}"
+ if setups is not None:
+ api_call += f"/setup/{','.join([str(int(i)) for i in setups])}"
+ if flows is not None:
+ api_call += f"/flow/{','.join([str(int(i)) for i in flows])}"
+ if runs is not None:
+ api_call += f"/run/{','.join([str(int(i)) for i in runs])}"
+ if uploaders is not None:
+ api_call += f"/uploader/{','.join([str(int(i)) for i in uploaders])}"
+ if study is not None:
+ api_call += f"/study/{study}"
+ if sort_order is not None:
+ api_call += f"/sort_order/{sort_order}"
+
+ return __list_evaluations(api_call)
+
+
+def __list_evaluations(api_call: str) -> list[OpenMLEvaluation]:
+ """Helper function to parse API calls which are lists of runs"""
+ xml_string = openml._api_calls._perform_api_call(api_call, "get")
+ evals_dict = xmltodict.parse(xml_string, force_list=("oml:evaluation",))
+ # Minimalistic check if the XML is useful
+ if "oml:evaluations" not in evals_dict:
+ raise ValueError(
+ f'Error in return XML, does not contain "oml:evaluations": {evals_dict!s}',
+ )
+
+ assert isinstance(evals_dict["oml:evaluations"]["oml:evaluation"], list), (
+ "Expected 'oml:evaluation' to be a list, but got"
+ f"{type(evals_dict['oml:evaluations']['oml:evaluation']).__name__}. "
+ )
+
+ uploader_ids = list(
+ {eval_["oml:uploader"] for eval_ in evals_dict["oml:evaluations"]["oml:evaluation"]},
+ )
+ api_users = "user/list/user_id/" + ",".join(uploader_ids)
+ xml_string_user = openml._api_calls._perform_api_call(api_users, "get")
+
+ users = xmltodict.parse(xml_string_user, force_list=("oml:user",))
+ user_dict = {user["oml:id"]: user["oml:username"] for user in users["oml:users"]["oml:user"]}
+
+ evals = []
+ for eval_ in evals_dict["oml:evaluations"]["oml:evaluation"]:
+ run_id = int(eval_["oml:run_id"])
+ value = float(eval_["oml:value"]) if "oml:value" in eval_ else None
+ values = json.loads(eval_["oml:values"]) if eval_.get("oml:values", None) else None
+ array_data = eval_.get("oml:array_data")
+
+ evals.append(
+ OpenMLEvaluation(
+ run_id=run_id,
+ task_id=int(eval_["oml:task_id"]),
+ setup_id=int(eval_["oml:setup_id"]),
+ flow_id=int(eval_["oml:flow_id"]),
+ flow_name=eval_["oml:flow_name"],
+ data_id=int(eval_["oml:data_id"]),
+ data_name=eval_["oml:data_name"],
+ function=eval_["oml:function"],
+ upload_time=eval_["oml:upload_time"],
+ uploader=int(eval_["oml:uploader"]),
+ uploader_name=user_dict[eval_["oml:uploader"]],
+ value=value,
+ values=values,
+ array_data=array_data,
+ )
+ )
+
+ return evals
+
+
+def list_evaluation_measures() -> list[str]:
+ """Return list of evaluation measures available.
+
+ The function performs an API call to retrieve the entire list of
+ evaluation measures that are available.
+
+ Returns
+ -------
+ list
+
+ """
+ api_call = "evaluationmeasure/list"
+ xml_string = openml._api_calls._perform_api_call(api_call, "get")
+ qualities = xmltodict.parse(xml_string, force_list=("oml:measures"))
+ # Minimalistic check if the XML is useful
+ if "oml:evaluation_measures" not in qualities:
+ raise ValueError('Error in return XML, does not contain "oml:evaluation_measures"')
+
+ if not isinstance(qualities["oml:evaluation_measures"]["oml:measures"][0]["oml:measure"], list):
+ raise TypeError('Error in return XML, does not contain "oml:measure" as a list')
+
+ return qualities["oml:evaluation_measures"]["oml:measures"][0]["oml:measure"]
+
+
+def list_estimation_procedures() -> list[str]:
+ """Return list of evaluation procedures available.
+
+ The function performs an API call to retrieve the entire list of
+ evaluation procedures' names that are available.
+
+ Returns
+ -------
+ list
+ """
+ api_call = "estimationprocedure/list"
+ xml_string = openml._api_calls._perform_api_call(api_call, "get")
+ api_results = xmltodict.parse(xml_string)
+
+ # Minimalistic check if the XML is useful
+ if "oml:estimationprocedures" not in api_results:
+ raise ValueError('Error in return XML, does not contain "oml:estimationprocedures"')
+
+ if "oml:estimationprocedure" not in api_results["oml:estimationprocedures"]:
+ raise ValueError('Error in return XML, does not contain "oml:estimationprocedure"')
+
+ if not isinstance(api_results["oml:estimationprocedures"]["oml:estimationprocedure"], list):
+ raise TypeError('Error in return XML, does not contain "oml:estimationprocedure" as a list')
+
+ return [
+ prod["oml:name"]
+ for prod in api_results["oml:estimationprocedures"]["oml:estimationprocedure"]
+ ]
+
+
+def list_evaluations_setups(
+ function: str,
+ offset: int | None = None,
+ size: int | None = None,
+ tasks: list | None = None,
+ setups: list | None = None,
+ flows: list | None = None,
+ runs: list | None = None,
+ uploaders: list | None = None,
+ tag: str | None = None,
+ per_fold: bool | None = None,
+ sort_order: str | None = None,
+ parameters_in_separate_columns: bool = False, # noqa: FBT002
+) -> pd.DataFrame:
+ """List all run-evaluation pairs matching all of the given filters
+ and their hyperparameter settings.
+
+ Parameters
+ ----------
+ function : str
+ the evaluation function. e.g., predictive_accuracy
+ offset : int, optional
+ the number of runs to skip, starting from the first
+ size : int, optional
+ the maximum number of runs to show
+ tasks : list[int], optional
+ the list of task IDs
+ setups: list[int], optional
+ the list of setup IDs
+ flows : list[int], optional
+ the list of flow IDs
+ runs : list[int], optional
+ the list of run IDs
+ uploaders : list[int], optional
+ the list of uploader IDs
+ tag : str, optional
+ filter evaluation based on given tag
+ per_fold : bool, optional
+ sort_order : str, optional
+ order of sorting evaluations, ascending ("asc") or descending ("desc")
+ parameters_in_separate_columns: bool, optional (default= False)
+ Returns hyperparameters in separate columns if set to True.
+ Valid only for a single flow
+
+ Returns
+ -------
+ dataframe with hyperparameter settings as a list of tuples.
+ """
+ if parameters_in_separate_columns and (flows is None or len(flows) != 1):
+ raise ValueError("Can set parameters_in_separate_columns to true only for single flow_id")
+
+ # List evaluations
+ evals = list_evaluations(
+ function=function,
+ offset=offset,
+ size=size,
+ runs=runs,
+ tasks=tasks,
+ setups=setups,
+ flows=flows,
+ uploaders=uploaders,
+ tag=tag,
+ per_fold=per_fold,
+ sort_order=sort_order,
+ output_format="dataframe",
+ )
+ # List setups
+ # list_setups by setup id does not support large sizes (exceeds URL length limit)
+ # Hence we split the list of unique setup ids returned by list_evaluations into chunks of size N
+ _df = pd.DataFrame()
+ if len(evals) != 0:
+ N = 100 # size of section
+ uniq = np.asarray(evals["setup_id"].unique())
+ length = len(uniq)
+
+ # array_split - allows indices_or_sections to not equally divide the array
+ # array_split -length % N sub-arrays of size length//N + 1 and the rest of size length//N.
+ split_size = ((length - 1) // N) + 1
+ setup_chunks = np.array_split(uniq, split_size)
+
+ setup_data = pd.DataFrame()
+ for _setups in setup_chunks:
+ result = openml.setups.list_setups(setup=_setups, output_format="dataframe")
+ assert isinstance(result, pd.DataFrame)
+ result = result.drop("flow_id", axis=1)
+ # concat resulting setup chunks into single datframe
+ setup_data = pd.concat([setup_data, result])
+
+ parameters = []
+ # Convert parameters of setup into dict of (hyperparameter, value)
+ for parameter_dict in setup_data["parameters"]:
+ if parameter_dict is not None:
+ parameters.append(
+ {param["full_name"]: param["value"] for param in parameter_dict.values()},
+ )
+ else:
+ parameters.append({})
+ setup_data["parameters"] = parameters
+ # Merge setups with evaluations
+ _df = evals.merge(setup_data, on="setup_id", how="left")
+
+ if parameters_in_separate_columns:
+ _df = pd.concat(
+ [_df.drop("parameters", axis=1), _df["parameters"].apply(pd.Series)],
+ axis=1,
+ )
+
+ return _df
diff --git a/openml/exceptions.py b/openml/exceptions.py
index d1e957d61..1c1343ff3 100644
--- a/openml/exceptions.py
+++ b/openml/exceptions.py
@@ -1,16 +1,90 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+
class PyOpenMLError(Exception):
- def __init__(self, message):
- super(PyOpenMLError, self).__init__(message)
+ """Base class for all exceptions in OpenML-Python."""
+
+ def __init__(self, message: str):
+ self.message = message
+ super().__init__(message)
class OpenMLServerError(PyOpenMLError):
- """Server didn't respond 200."""
- def __init__(self, message):
- message = "OpenML Server error: " + message
- super(OpenMLServerError, self).__init__(message)
+ """class for when something is really wrong on the server
+ (result did not parse to dict), contains unparsed error.
+ """
+
+
+class OpenMLServerException(OpenMLServerError): # noqa: N818
+ """exception for when the result of the server was
+ not 200 (e.g., listing call w/o results).
+ """
+
+ # Code needs to be optional to allow the exception to be picklable:
+ # https://stackoverflow.com/questions/16244923/how-to-make-a-custom-exception-class-with-multiple-init-args-pickleable # noqa: E501
+ def __init__(self, message: str, code: int | None = None, url: str | None = None):
+ self.message = message
+ self.code = code
+ self.url = url
+ super().__init__(message)
+
+ def __str__(self) -> str:
+ return f"{self.url} returned code {self.code}: {self.message}"
-class OpenMLCacheException(PyOpenMLError):
+class OpenMLServerNoResult(OpenMLServerException):
+ """Exception for when the result of the server is empty."""
+
+
+class OpenMLCacheException(PyOpenMLError): # noqa: N818
"""Dataset / task etc not found in cache"""
- def __init__(self, message):
- super(OpenMLCacheException, self).__init__(message)
+
+
+class OpenMLHashException(PyOpenMLError): # noqa: N818
+ """Locally computed hash is different than hash announced by the server."""
+
+
+class OpenMLPrivateDatasetError(PyOpenMLError):
+ """Exception thrown when the user has no rights to access the dataset."""
+
+
+class OpenMLRunsExistError(PyOpenMLError):
+ """Indicates run(s) already exists on the server when they should not be duplicated."""
+
+ def __init__(self, run_ids: set[int], message: str) -> None:
+ if len(run_ids) < 1:
+ raise ValueError("Set of run ids must be non-empty.")
+ self.run_ids = run_ids
+ super().__init__(message)
+
+
+class OpenMLNotAuthorizedError(OpenMLServerError):
+ """Indicates an authenticated user is not authorized to execute the requested action."""
+
+
+class OpenMLAuthenticationError(OpenMLServerError):
+ """Exception raised when API authentication fails.
+
+ This typically occurs when:
+ - No API key is configured
+ - The API key is invalid or expired
+ - The API key format is incorrect
+
+ This is different from authorization (OpenMLNotAuthorizedError), which occurs
+ when a valid API key lacks permissions for the requested operation.
+ """
+
+ def __init__(self, message: str):
+ help_text = (
+ "\n\nTo fix this:\n"
+ "1. Get your API key from https://www.openml.org/\n"
+ " (you'll need to register for a free account if you don't have one)\n"
+ "2. Configure your API key by following the authentication guide:\n"
+ " https://openml.github.io/openml-python/latest/examples/Basics/introduction_tutorial/#authentication"
+ )
+ super().__init__(message + help_text)
+
+
+class ObjectNotPublishedError(PyOpenMLError):
+ """Indicates an object has not been published yet."""
diff --git a/openml/extensions/__init__.py b/openml/extensions/__init__.py
new file mode 100644
index 000000000..979986182
--- /dev/null
+++ b/openml/extensions/__init__.py
@@ -0,0 +1,15 @@
+# License: BSD 3-Clause
+
+
+from .extension_interface import Extension
+from .functions import get_extension_by_flow, get_extension_by_model, register_extension
+
+extensions: list[type[Extension]] = []
+
+
+__all__ = [
+ "Extension",
+ "get_extension_by_flow",
+ "get_extension_by_model",
+ "register_extension",
+]
diff --git a/openml/extensions/extension_interface.py b/openml/extensions/extension_interface.py
new file mode 100644
index 000000000..e391d109a
--- /dev/null
+++ b/openml/extensions/extension_interface.py
@@ -0,0 +1,270 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+from abc import ABC, abstractmethod
+from collections import OrderedDict
+from typing import TYPE_CHECKING, Any
+
+# Avoid import cycles: https://mypy.readthedocs.io/en/latest/common_issues.html#import-cycles
+if TYPE_CHECKING:
+ import numpy as np
+ import scipy.sparse
+
+ from openml.flows import OpenMLFlow
+ from openml.runs.trace import OpenMLRunTrace, OpenMLTraceIteration # F401
+ from openml.tasks.task import OpenMLTask
+
+
+class Extension(ABC):
+ """Defines the interface to connect machine learning libraries to OpenML-Python.
+
+ See ``openml.extension.sklearn.extension`` for an implementation to bootstrap from.
+ """
+
+ ################################################################################################
+ # General setup
+
+ @classmethod
+ @abstractmethod
+ def can_handle_flow(cls, flow: OpenMLFlow) -> bool:
+ """Check whether a given flow can be handled by this extension.
+
+ This is typically done by parsing the ``external_version`` field.
+
+ Parameters
+ ----------
+ flow : OpenMLFlow
+
+ Returns
+ -------
+ bool
+ """
+
+ @classmethod
+ @abstractmethod
+ def can_handle_model(cls, model: Any) -> bool:
+ """Check whether a model flow can be handled by this extension.
+
+ This is typically done by checking the type of the model, or the package it belongs to.
+
+ Parameters
+ ----------
+ model : Any
+
+ Returns
+ -------
+ bool
+ """
+
+ ################################################################################################
+ # Abstract methods for flow serialization and de-serialization
+
+ @abstractmethod
+ def flow_to_model(
+ self,
+ flow: OpenMLFlow,
+ initialize_with_defaults: bool = False, # noqa: FBT002
+ strict_version: bool = True, # noqa: FBT002
+ ) -> Any:
+ """Instantiate a model from the flow representation.
+
+ Parameters
+ ----------
+ flow : OpenMLFlow
+
+ initialize_with_defaults : bool, optional (default=False)
+ If this flag is set, the hyperparameter values of flows will be
+ ignored and a flow with its defaults is returned.
+
+ strict_version : bool, default=True
+ Whether to fail if version requirements are not fulfilled.
+
+ Returns
+ -------
+ Any
+ """
+
+ @abstractmethod
+ def model_to_flow(self, model: Any) -> OpenMLFlow:
+ """Transform a model to a flow for uploading it to OpenML.
+
+ Parameters
+ ----------
+ model : Any
+
+ Returns
+ -------
+ OpenMLFlow
+ """
+
+ @abstractmethod
+ def get_version_information(self) -> list[str]:
+ """List versions of libraries required by the flow.
+
+ Returns
+ -------
+ List
+ """
+
+ @abstractmethod
+ def create_setup_string(self, model: Any) -> str:
+ """Create a string which can be used to reinstantiate the given model.
+
+ Parameters
+ ----------
+ model : Any
+
+ Returns
+ -------
+ str
+ """
+
+ ################################################################################################
+ # Abstract methods for performing runs with extension modules
+
+ @abstractmethod
+ def is_estimator(self, model: Any) -> bool:
+ """Check whether the given model is an estimator for the given extension.
+
+ This function is only required for backwards compatibility and will be removed in the
+ near future.
+
+ Parameters
+ ----------
+ model : Any
+
+ Returns
+ -------
+ bool
+ """
+
+ @abstractmethod
+ def seed_model(self, model: Any, seed: int | None) -> Any:
+ """Set the seed of all the unseeded components of a model and return the seeded model.
+
+ Required so that all seed information can be uploaded to OpenML for reproducible results.
+
+ Parameters
+ ----------
+ model : Any
+ The model to be seeded
+ seed : int
+
+ Returns
+ -------
+ model
+ """
+
+ @abstractmethod
+ def _run_model_on_fold( # noqa: PLR0913
+ self,
+ model: Any,
+ task: OpenMLTask,
+ X_train: np.ndarray | scipy.sparse.spmatrix,
+ rep_no: int,
+ fold_no: int,
+ y_train: np.ndarray | None = None,
+ X_test: np.ndarray | scipy.sparse.spmatrix | None = None,
+ ) -> tuple[np.ndarray, np.ndarray | None, OrderedDict[str, float], OpenMLRunTrace | None]:
+ """Run a model on a repeat, fold, subsample triplet of the task.
+
+ Returns the data that is necessary to construct the OpenML Run object. Is used by
+ :func:`openml.runs.run_flow_on_task`.
+
+ Parameters
+ ----------
+ model : Any
+ The UNTRAINED model to run. The model instance will be copied and not altered.
+ task : OpenMLTask
+ The task to run the model on.
+ X_train : array-like
+ Training data for the given repetition and fold.
+ rep_no : int
+ The repeat of the experiment (0-based; in case of 1 time CV, always 0)
+ fold_no : int
+ The fold nr of the experiment (0-based; in case of holdout, always 0)
+ y_train : Optional[np.ndarray] (default=None)
+ Target attributes for supervised tasks. In case of classification, these are integer
+ indices to the potential classes specified by dataset.
+ X_test : Optional, array-like (default=None)
+ Test attributes to test for generalization in supervised tasks.
+
+ Returns
+ -------
+ predictions : np.ndarray
+ Model predictions.
+ probabilities : Optional, np.ndarray
+ Predicted probabilities (only applicable for supervised classification tasks).
+ user_defined_measures : OrderedDict[str, float]
+ User defined measures that were generated on this fold
+ trace : Optional, OpenMLRunTrace
+ Hyperparameter optimization trace (only applicable for supervised tasks with
+ hyperparameter optimization).
+ """
+
+ @abstractmethod
+ def obtain_parameter_values(
+ self,
+ flow: OpenMLFlow,
+ model: Any = None,
+ ) -> list[dict[str, Any]]:
+ """Extracts all parameter settings required for the flow from the model.
+
+ If no explicit model is provided, the parameters will be extracted from `flow.model`
+ instead.
+
+ Parameters
+ ----------
+ flow : OpenMLFlow
+ OpenMLFlow object (containing flow ids, i.e., it has to be downloaded from the server)
+
+ model: Any, optional (default=None)
+ The model from which to obtain the parameter values. Must match the flow signature.
+ If None, use the model specified in ``OpenMLFlow.model``.
+
+ Returns
+ -------
+ list
+ A list of dicts, where each dict has the following entries:
+ - ``oml:name`` : str: The OpenML parameter name
+ - ``oml:value`` : mixed: A representation of the parameter value
+ - ``oml:component`` : int: flow id to which the parameter belongs
+ """
+
+ @abstractmethod
+ def check_if_model_fitted(self, model: Any) -> bool:
+ """Returns True/False denoting if the model has already been fitted/trained.
+
+ Parameters
+ ----------
+ model : Any
+
+ Returns
+ -------
+ bool
+ """
+
+ ################################################################################################
+ # Abstract methods for hyperparameter optimization
+
+ @abstractmethod
+ def instantiate_model_from_hpo_class(
+ self,
+ model: Any,
+ trace_iteration: OpenMLTraceIteration,
+ ) -> Any:
+ """Instantiate a base model which can be searched over by the hyperparameter optimization
+ model.
+
+ Parameters
+ ----------
+ model : Any
+ A hyperparameter optimization model which defines the model to be instantiated.
+ trace_iteration : OpenMLTraceIteration
+ Describing the hyperparameter settings to instantiate.
+
+ Returns
+ -------
+ Any
+ """
+ # TODO a trace belongs to a run and therefore a flow -> simplify this part of the interface!
diff --git a/openml/extensions/functions.py b/openml/extensions/functions.py
new file mode 100644
index 000000000..44df5ec69
--- /dev/null
+++ b/openml/extensions/functions.py
@@ -0,0 +1,137 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+import importlib.util
+from typing import TYPE_CHECKING, Any
+
+# Need to implement the following by its full path because otherwise it won't be possible to
+# access openml.extensions.extensions
+import openml.extensions
+
+# Avoid import cycles: https://mypy.readthedocs.io/en/latest/common_issues.html#import-cycles
+if TYPE_CHECKING:
+ from openml.flows import OpenMLFlow
+
+ from . import Extension
+
+SKLEARN_HINT = (
+ "But it looks related to scikit-learn. "
+ "Please install the OpenML scikit-learn extension (openml-sklearn) and try again. "
+ "You can use `pip install openml-sklearn` for installation."
+ "For more information, see "
+ "https://docs.openml.org/python/extensions/"
+)
+
+
+def register_extension(extension: type[Extension]) -> None:
+ """Register an extension.
+
+ Registered extensions are considered by ``get_extension_by_flow`` and
+ ``get_extension_by_model``, which are used by ``openml.flow`` and ``openml.runs``.
+
+ Parameters
+ ----------
+ extension : Type[Extension]
+
+ Returns
+ -------
+ None
+ """
+ openml.extensions.extensions.append(extension)
+
+
+def get_extension_by_flow(
+ flow: OpenMLFlow,
+ raise_if_no_extension: bool = False, # noqa: FBT002
+) -> Extension | None:
+ """Get an extension which can handle the given flow.
+
+ Iterates all registered extensions and checks whether they can handle the presented flow.
+ Raises an exception if two extensions can handle a flow.
+
+ Parameters
+ ----------
+ flow : OpenMLFlow
+
+ raise_if_no_extension : bool (optional, default=False)
+ Raise an exception if no registered extension can handle the presented flow.
+
+ Returns
+ -------
+ Extension or None
+ """
+ # import openml_sklearn to register SklearnExtension
+ if importlib.util.find_spec("openml_sklearn"):
+ import openml_sklearn # noqa: F401
+
+ candidates = []
+ for extension_class in openml.extensions.extensions:
+ if extension_class.can_handle_flow(flow):
+ candidates.append(extension_class())
+ if len(candidates) == 0:
+ if raise_if_no_extension:
+ install_instruction = ""
+ if flow.name.startswith("sklearn"):
+ install_instruction = SKLEARN_HINT
+ raise ValueError(
+ f"No extension registered which can handle flow: {flow.flow_id} ({flow.name}). "
+ f"{install_instruction}"
+ )
+
+ return None
+
+ if len(candidates) == 1:
+ return candidates[0]
+
+ raise ValueError(
+ f"Multiple extensions registered which can handle flow: {flow}, but only one "
+ f"is allowed ({candidates}).",
+ )
+
+
+def get_extension_by_model(
+ model: Any,
+ raise_if_no_extension: bool = False, # noqa: FBT002
+) -> Extension | None:
+ """Get an extension which can handle the given flow.
+
+ Iterates all registered extensions and checks whether they can handle the presented model.
+ Raises an exception if two extensions can handle a model.
+
+ Parameters
+ ----------
+ model : Any
+
+ raise_if_no_extension : bool (optional, default=False)
+ Raise an exception if no registered extension can handle the presented model.
+
+ Returns
+ -------
+ Extension or None
+ """
+ # import openml_sklearn to register SklearnExtension
+ if importlib.util.find_spec("openml_sklearn"):
+ import openml_sklearn # noqa: F401
+
+ candidates = []
+ for extension_class in openml.extensions.extensions:
+ if extension_class.can_handle_model(model):
+ candidates.append(extension_class())
+ if len(candidates) == 0:
+ if raise_if_no_extension:
+ install_instruction = ""
+ if type(model).__module__.startswith("sklearn"):
+ install_instruction = SKLEARN_HINT
+ raise ValueError(
+ f"No extension registered which can handle model: {model}. {install_instruction}"
+ )
+
+ return None
+
+ if len(candidates) == 1:
+ return candidates[0]
+
+ raise ValueError(
+ f"Multiple extensions registered which can handle model: {model}, but only one "
+ f"is allowed ({candidates}).",
+ )
diff --git a/openml/flows/__init__.py b/openml/flows/__init__.py
index 4cda24069..d455249de 100644
--- a/openml/flows/__init__.py
+++ b/openml/flows/__init__.py
@@ -1,3 +1,21 @@
+# License: BSD 3-Clause
+
from .flow import OpenMLFlow
+from .functions import (
+ assert_flows_equal,
+ delete_flow,
+ flow_exists,
+ get_flow,
+ get_flow_id,
+ list_flows,
+)
-__all__ = ['OpenMLFlow']
+__all__ = [
+ "OpenMLFlow",
+ "assert_flows_equal",
+ "delete_flow",
+ "flow_exists",
+ "get_flow",
+ "get_flow_id",
+ "list_flows",
+]
diff --git a/openml/flows/flow.py b/openml/flows/flow.py
index 0821ee26f..7dd84fdee 100644
--- a/openml/flows/flow.py
+++ b/openml/flows/flow.py
@@ -1,124 +1,571 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+import logging
from collections import OrderedDict
+from collections.abc import Hashable, Sequence
+from pathlib import Path
+from typing import Any, cast
+
import xmltodict
-import sklearn
-from .._api_calls import _perform_api_call
-from .functions import _check_flow_exists
+from openml.base import OpenMLBase
+from openml.extensions import Extension, get_extension_by_flow
+from openml.utils import extract_xml_tags
-class OpenMLFlow(object):
+class OpenMLFlow(OpenMLBase):
"""OpenML Flow. Stores machine learning models.
+ Flows should not be generated manually, but by the function
+ :meth:`openml.flows.create_flow_from_model`. Using this helper function
+ ensures that all relevant fields are filled in.
+
+ Implements `openml.implementation.upload.xsd
+ `_.
+
Parameters
----------
- model : scikit-learn compatible model
- The model the flow consists of. The model needs to have fit and predict methods.
- description : string
- Description of the flow (free text).
- creator : string
- FIXME
- contributor : string
- FIXME
- tag : string
- FIXME
+ name : str
+ Name of the flow. Is used together with the attribute
+ `external_version` as a unique identifier of the flow.
+ description : str
+ Human-readable description of the flow (free text).
+ model : object
+ ML model which is described by this flow.
+ components : OrderedDict
+ Mapping from component identifier to an OpenMLFlow object. Components
+ are usually subfunctions of an algorithm (e.g. kernels), base learners
+ in ensemble algorithms (decision tree in adaboost) or building blocks
+ of a machine learning pipeline. Components are modeled as independent
+ flows and can be shared between flows (different pipelines can use
+ the same components).
+ parameters : OrderedDict
+ Mapping from parameter name to the parameter default value. The
+ parameter default value must be of type `str`, so that the respective
+ toolbox plugin can take care of casting the parameter default value to
+ the correct type.
+ parameters_meta_info : OrderedDict
+ Mapping from parameter name to `dict`. Stores additional information
+ for each parameter. Required keys are `data_type` and `description`.
+ external_version : str
+ Version number of the software the flow is implemented in. Is used
+ together with the attribute `name` as a uniquer identifier of the flow.
+ tags : list
+ List of tags. Created on the server by other API calls.
+ language : str
+ Natural language the flow is described in (not the programming
+ language).
+ dependencies : str
+ A list of dependencies necessary to run the flow. This field should
+ contain all libraries the flow depends on. To allow reproducibility
+ it should also specify the exact version numbers.
+ class_name : str, optional
+ The development language name of the class which is described by this
+ flow.
+ custom_name : str, optional
+ Custom name of the flow given by the owner.
+ binary_url : str, optional
+ Url from which the binary can be downloaded. Added by the server.
+ Ignored when uploaded manually. Will not be used by the python API
+ because binaries aren't compatible across machines.
+ binary_format : str, optional
+ Format in which the binary code was uploaded. Will not be used by the
+ python API because binaries aren't compatible across machines.
+ binary_md5 : str, optional
+ MD5 checksum to check if the binary code was correctly downloaded. Will
+ not be used by the python API because binaries aren't compatible across
+ machines.
+ uploader : str, optional
+ OpenML user ID of the uploader. Filled in by the server.
+ upload_date : str, optional
+ Date the flow was uploaded. Filled in by the server.
flow_id : int, optional
- Flow ID. Assigned by the server (fixme shouldn't be here?)
- uploader : string, optional
- User uploading the model (fixme shouldn't be here?). Assigned by the server.
-
-
+ Flow ID. Assigned by the server.
+ extension : Extension, optional
+ The extension for a flow (e.g., sklearn).
+ version : str, optional
+ OpenML version of the flow. Assigned by the server.
"""
- def __init__(self, model, flow_id=None, uploader=None,
- description='Flow generated by run_task', creator=None,
- contributor=None, tag=None):
- self.flow_id = flow_id
- self.upoader = uploader
+
+ def __init__( # noqa: PLR0913
+ self,
+ name: str,
+ description: str,
+ model: object,
+ components: dict,
+ parameters: dict,
+ parameters_meta_info: dict,
+ external_version: str,
+ tags: list,
+ language: str,
+ dependencies: str,
+ class_name: str | None = None,
+ custom_name: str | None = None,
+ binary_url: str | None = None,
+ binary_format: str | None = None,
+ binary_md5: str | None = None,
+ uploader: str | None = None,
+ upload_date: str | None = None,
+ flow_id: int | None = None,
+ extension: Extension | None = None,
+ version: str | None = None,
+ ):
+ self.name = name
self.description = description
- self.creator = creator
- self.tag = tag
self.model = model
- self.source = "FIXME DEFINE PYTHON FLOW"
- self.name = (model.__module__ + "." +
- model.__class__.__name__)
- self.external_version = 'sklearn_' + sklearn.__version__
- def _generate_flow_xml(self):
- """Generate xml representation of self for upload to server.
+ for variable, variable_name in [
+ [components, "components"],
+ [parameters, "parameters"],
+ [parameters_meta_info, "parameters_meta_info"],
+ ]:
+ if not isinstance(variable, (OrderedDict, dict)):
+ raise TypeError(
+ f"{variable_name} must be of type OrderedDict or dict, "
+ f"but is {type(variable)}.",
+ )
- Returns
- -------
- flow_xml : string
- Flow represented as XML string.
- """
- model = self.model
+ self.components = components
+ self.parameters = parameters
+ self.parameters_meta_info = parameters_meta_info
+ self.class_name = class_name
+
+ keys_parameters = set(parameters.keys())
+ keys_parameters_meta_info = set(parameters_meta_info.keys())
+ if len(keys_parameters.difference(keys_parameters_meta_info)) > 0:
+ raise ValueError(
+ f"Parameter {keys_parameters.difference(keys_parameters_meta_info)!s} only in "
+ "parameters, but not in parameters_meta_info.",
+ )
+ if len(keys_parameters_meta_info.difference(keys_parameters)) > 0:
+ raise ValueError(
+ f"Parameter {keys_parameters_meta_info.difference(keys_parameters)!s} only in "
+ " parameters_meta_info, but not in parameters.",
+ )
+
+ self.external_version = external_version
+ self.uploader = uploader
+
+ self.custom_name = custom_name
+ self.tags = tags if tags is not None else []
+ self.binary_url = binary_url
+ self.binary_format = binary_format
+ self.binary_md5 = binary_md5
+ self.version = version
+ self.upload_date = upload_date
+ self.language = language
+ self.dependencies = dependencies
+ self.flow_id = flow_id
+ self._extension = extension
+
+ @property
+ def id(self) -> int | None:
+ """The ID of the flow."""
+ return self.flow_id
+
+ @property
+ def extension(self) -> Extension:
+ """The extension of the flow (e.g., sklearn)."""
+ if self._extension is None:
+ self._extension = cast(
+ "Extension", get_extension_by_flow(self, raise_if_no_extension=True)
+ )
+
+ return self._extension
- flow_dict = OrderedDict()
- flow_dict['oml:flow'] = OrderedDict()
- flow_dict['oml:flow']['@xmlns:oml'] = 'http://openml.org/openml'
- flow_dict['oml:flow']['oml:name'] = self._get_name()
- flow_dict['oml:flow']['oml:external_version'] = self.external_version
- flow_dict['oml:flow']['oml:description'] = self.description
+ def _get_repr_body_fields(self) -> Sequence[tuple[str, str | int | list[str]]]:
+ """Collect all information to display in the __repr__ body."""
+ fields = {
+ "Flow Name": self.name,
+ "Flow Description": self.description,
+ "Dependencies": self.dependencies,
+ }
+ if self.flow_id is not None:
+ fields["Flow URL"] = self.openml_url if self.openml_url is not None else "None"
+ fields["Flow ID"] = str(self.flow_id)
+ if self.version is not None:
+ fields["Flow ID"] += f" (version {self.version})"
+ if self.upload_date is not None:
+ fields["Upload Date"] = self.upload_date.replace("T", " ")
+ if self.binary_url is not None:
+ fields["Binary URL"] = self.binary_url
+
+ # determines the order in which the information will be printed
+ order = [
+ "Flow ID",
+ "Flow URL",
+ "Flow Name",
+ "Flow Description",
+ "Binary URL",
+ "Upload Date",
+ "Dependencies",
+ ]
+ return [(key, fields[key]) for key in order if key in fields]
+
+ def _to_dict(self) -> dict[str, dict]: # noqa: C901, PLR0912
+ """Creates a dictionary representation of self."""
+ flow_container = OrderedDict() # type: 'dict[str, dict]'
+ flow_dict = OrderedDict(
+ [("@xmlns:oml", "http://openml.org/openml")],
+ ) # type: 'dict[str, list | str]' # E501
+ flow_container["oml:flow"] = flow_dict
+ _add_if_nonempty(flow_dict, "oml:id", self.flow_id)
+
+ for required in ["name", "external_version"]:
+ if getattr(self, required) is None:
+ raise ValueError(f"self.{required} is required but None")
+ for attribute in [
+ "uploader",
+ "name",
+ "custom_name",
+ "class_name",
+ "version",
+ "external_version",
+ "description",
+ "upload_date",
+ "language",
+ "dependencies",
+ ]:
+ _add_if_nonempty(flow_dict, f"oml:{attribute}", getattr(self, attribute))
+
+ if not self.description:
+ logger = logging.getLogger(__name__)
+ logger.warning("Flow % has empty description", self.name)
- clf_params = model.get_params()
flow_parameters = []
- for k, v in clf_params.items():
- # data_type, default_value, description, recommendedRange
- # type = v.__class__.__name__ Not using this because it doesn't conform standards
- # eg. int instead of integer
- param_dict = {'oml:name': k}
+ for key in self.parameters:
+ param_dict = OrderedDict() # type: 'OrderedDict[str, str]'
+ param_dict["oml:name"] = key
+ meta_info = self.parameters_meta_info[key]
+
+ _add_if_nonempty(param_dict, "oml:data_type", meta_info["data_type"])
+ param_dict["oml:default_value"] = self.parameters[key]
+ _add_if_nonempty(param_dict, "oml:description", meta_info["description"])
+
+ for key_, value in param_dict.items():
+ if key_ is not None and not isinstance(key_, str):
+ raise ValueError(
+ f"Parameter name {key_} cannot be serialized "
+ f"because it is of type {type(key_)}. Only strings "
+ "can be serialized.",
+ )
+ if value is not None and not isinstance(value, str):
+ raise ValueError(
+ f"Parameter value {value} cannot be serialized "
+ f"because it is of type {type(value)}. Only strings "
+ "can be serialized.",
+ )
+
flow_parameters.append(param_dict)
- flow_dict['oml:flow']['oml:parameter'] = flow_parameters
+ flow_dict["oml:parameter"] = flow_parameters
+
+ components = []
+ for key in self.components:
+ component_dict = OrderedDict() # type: 'OrderedDict[str, dict]'
+ component_dict["oml:identifier"] = key
+ if self.components[key] in ["passthrough", "drop"]:
+ component_dict["oml:flow"] = {
+ "oml-python:serialized_object": "component_reference",
+ "value": {"key": self.components[key], "step_name": self.components[key]},
+ }
+ else:
+ component_dict["oml:flow"] = self.components[key]._to_dict()["oml:flow"]
+
+ for key_ in component_dict:
+ # We only need to check if the key is a string, because the
+ # value is a flow. The flow itself is valid by recursion
+ if key_ is not None and not isinstance(key_, str):
+ raise ValueError(
+ f"Parameter name {key_} cannot be serialized "
+ f"because it is of type {type(key_)}. Only strings "
+ "can be serialized.",
+ )
+
+ components.append(component_dict)
+
+ flow_dict["oml:component"] = components
+ flow_dict["oml:tag"] = self.tags
+ for attribute in ["binary_url", "binary_format", "binary_md5"]:
+ _add_if_nonempty(flow_dict, f"oml:{attribute}", getattr(self, attribute))
+
+ return flow_container
+
+ @classmethod
+ def _from_dict(cls, xml_dict: dict) -> OpenMLFlow:
+ """Create a flow from an xml description.
+
+ Calls itself recursively to create :class:`OpenMLFlow` objects of
+ subflows (components).
+
+ XML definition of a flow is available at
+ https://github.com/openml/OpenML/blob/master/openml_OS/views/pages/api_new/v1/xsd/openml.implementation.upload.xsd
+
+ Parameters
+ ----------
+ xml_dict : dict
+ Dictionary representation of the flow as created by _to_dict()
+
+ Returns
+ -------
+ OpenMLFlow
+
+ """ # E501
+ arguments = OrderedDict()
+ dic = xml_dict["oml:flow"]
+
+ # Mandatory parts in the xml file
+ for key in ["name"]:
+ arguments[key] = dic["oml:" + key]
+
+ # non-mandatory parts in the xml file
+ for key in [
+ "external_version",
+ "uploader",
+ "description",
+ "upload_date",
+ "language",
+ "dependencies",
+ "version",
+ "binary_url",
+ "binary_format",
+ "binary_md5",
+ "class_name",
+ "custom_name",
+ ]:
+ arguments[key] = dic.get("oml:" + key)
+
+ # has to be converted to an int if present and cannot parsed in the
+ # two loops above
+ arguments["flow_id"] = int(dic["oml:id"]) if dic.get("oml:id") is not None else None
+
+ # Now parse parts of a flow which can occur multiple times like
+ # parameters, components (subflows) and tags. These can't be tackled
+ # in the loops above because xmltodict returns a dict if such an
+ # entity occurs once, and a list if it occurs multiple times.
+ # Furthermore, they must be treated differently, for example
+ # for components this method is called recursively and
+ # for parameters the actual information is split into two dictionaries
+ # for easier access in python.
+
+ parameters = OrderedDict()
+ parameters_meta_info = OrderedDict()
+ if "oml:parameter" in dic:
+ # In case of a single parameter, xmltodict returns a dictionary,
+ # otherwise a list.
+ oml_parameters = extract_xml_tags("oml:parameter", dic, allow_none=False)
+
+ for oml_parameter in oml_parameters:
+ parameter_name = oml_parameter["oml:name"]
+ default_value = oml_parameter["oml:default_value"]
+ parameters[parameter_name] = default_value
+
+ meta_info = OrderedDict()
+ meta_info["description"] = oml_parameter.get("oml:description")
+ meta_info["data_type"] = oml_parameter.get("oml:data_type")
+ parameters_meta_info[parameter_name] = meta_info
+ arguments["parameters"] = parameters
+ arguments["parameters_meta_info"] = parameters_meta_info
- flow_xml = xmltodict.unparse(flow_dict, pretty=True)
+ components = OrderedDict()
+ if "oml:component" in dic:
+ # In case of a single component xmltodict returns a dict,
+ # otherwise a list.
+ oml_components = extract_xml_tags("oml:component", dic, allow_none=False)
- # A flow may not be uploaded with the encoding specification..
- flow_xml = flow_xml.split('\n', 1)[-1]
- return flow_xml
+ for component in oml_components:
+ flow = OpenMLFlow._from_dict(component)
+ components[component["oml:identifier"]] = flow
+ arguments["components"] = components
+ arguments["tags"] = extract_xml_tags("oml:tag", dic)
- def publish(self):
- """Publish flow to OpenML server.
+ arguments["model"] = None
+ return cls(**arguments)
+
+ def to_filesystem(self, output_directory: str | Path) -> None:
+ """Write a flow to the filesystem as XML to output_directory."""
+ output_directory = Path(output_directory)
+ output_directory.mkdir(parents=True, exist_ok=True)
+
+ output_path = output_directory / "flow.xml"
+ if output_path.exists():
+ raise ValueError("Output directory already contains a flow.xml file.")
+
+ run_xml = self._to_xml()
+ with output_path.open("w") as f:
+ f.write(run_xml)
+
+ @classmethod
+ def from_filesystem(cls, input_directory: str | Path) -> OpenMLFlow:
+ """Read a flow from an XML in input_directory on the filesystem."""
+ input_directory = Path(input_directory) / "flow.xml"
+ with input_directory.open() as f:
+ xml_string = f.read()
+ return OpenMLFlow._from_dict(xmltodict.parse(xml_string))
+
+ def _parse_publish_response(self, xml_response: dict) -> None:
+ """Parse the id from the xml_response and assign it to self."""
+ self.flow_id = int(xml_response["oml:upload_flow"]["oml:id"])
+
+ def publish(self, raise_error_if_exists: bool = False) -> OpenMLFlow: # noqa: FBT002
+ """Publish this flow to OpenML server.
+
+ Raises a PyOpenMLError if the flow exists on the server, but
+ `self.flow_id` does not match the server known flow id.
+
+ Parameters
+ ----------
+ raise_error_if_exists : bool, optional (default=False)
+ If True, raise PyOpenMLError if the flow exists on the server.
+ If False, update the local flow to match the server flow.
Returns
-------
self : OpenMLFlow
"""
- xml_description = self._generate_flow_xml()
+ # Import at top not possible because of cyclic dependencies. In
+ # particular, flow.py tries to import functions.py in order to call
+ # get_flow(), while functions.py tries to import flow.py in order to
+ # instantiate an OpenMLFlow.
+ import openml.flows.functions
+
+ flow_id = openml.flows.functions.flow_exists(self.name, self.external_version)
+ if not flow_id:
+ if self.flow_id:
+ raise openml.exceptions.PyOpenMLError(
+ "Flow does not exist on the server, but 'flow.flow_id' is not None.",
+ )
+ super().publish()
+ assert self.flow_id is not None # for mypy
+ flow_id = self.flow_id
+ elif raise_error_if_exists:
+ error_message = f"This OpenMLFlow already exists with id: {flow_id}."
+ raise openml.exceptions.PyOpenMLError(error_message)
+ elif self.flow_id is not None and self.flow_id != flow_id:
+ raise openml.exceptions.PyOpenMLError(
+ f"Local flow_id does not match server flow_id: '{self.flow_id}' vs '{flow_id}'",
+ )
- file_elements = {'description': xml_description}
- return_code, return_value = _perform_api_call(
- "flow/", file_elements=file_elements)
- self.flow_id = int(xmltodict.parse(return_value)['oml:upload_flow']['oml:id'])
+ flow = openml.flows.functions.get_flow(flow_id)
+ _copy_server_fields(flow, self)
+ try:
+ openml.flows.functions.assert_flows_equal(
+ self,
+ flow,
+ flow.upload_date,
+ ignore_parameter_values=True,
+ ignore_custom_name_if_none=True,
+ )
+ except ValueError as e:
+ message = e.args[0]
+ raise ValueError(
+ "The flow on the server is inconsistent with the local flow. "
+ f"The server flow ID is {flow_id}. Please check manually and remove "
+ f"the flow if necessary! Error is:\n'{message}'",
+ ) from e
return self
- def _ensure_flow_exists(self):
- """ Checks if a flow exists for the given model and possibly creates it.
+ def get_structure(self, key_item: str) -> dict[str, list[str]]:
+ """
+ Returns for each sub-component of the flow the path of identifiers
+ that should be traversed to reach this component. The resulting dict
+ maps a key (identifying a flow by either its id, name or fullname) to
+ the parameter prefix.
- If the given flow exists on the server, the flow-id will simply
- be returned. Otherwise it will be uploaded to the server.
+ Parameters
+ ----------
+ key_item: str
+ The flow attribute that will be used to identify flows in the
+ structure. Allowed values {flow_id, name}
Returns
-------
- flow_id : int
- Flow id on the server.
+ dict[str, List[str]]
+ The flow structure
+ """
+ if key_item not in ["flow_id", "name"]:
+ raise ValueError("key_item should be in {flow_id, name}")
+ structure = {}
+ for key, sub_flow in self.components.items():
+ sub_structure = sub_flow.get_structure(key_item)
+ for flow_name, flow_sub_structure in sub_structure.items():
+ structure[flow_name] = [key, *flow_sub_structure]
+ structure[getattr(self, key_item)] = []
+ return structure
+
+ def get_subflow(self, structure: list[str]) -> OpenMLFlow:
"""
- import sklearn
- flow_version = 'sklearn_' + sklearn.__version__
- _, _, flow_id = _check_flow_exists(self._get_name(), flow_version)
- # TODO add numpy and scipy version!
+ Returns a subflow from the tree of dependencies.
+
+ Parameters
+ ----------
+ structure: list[str]
+ A list of strings, indicating the location of the subflow
+
+ Returns
+ -------
+ OpenMLFlow
+ The OpenMLFlow that corresponds to the structure
+ """
+ # make a copy of structure, as we don't want to change it in the
+ # outer scope
+ structure = list(structure)
+ if len(structure) < 1:
+ raise ValueError("Please provide a structure list of size >= 1")
+ sub_identifier = structure[0]
+ if sub_identifier not in self.components:
+ raise ValueError(
+ f"Flow {self.name} does not contain component with identifier {sub_identifier}",
+ )
+ if len(structure) == 1:
+ return self.components[sub_identifier] # type: ignore
+
+ structure.pop(0)
+ return self.components[sub_identifier].get_subflow(structure) # type: ignore
- if int(flow_id) == -1:
- return_code, response_xml = self.publish()
- response_dict = xmltodict.parse(response_xml)
- flow_id = response_dict['oml:upload_flow']['oml:id']
- return int(flow_id)
+def _copy_server_fields(source_flow: OpenMLFlow, target_flow: OpenMLFlow) -> None:
+ """Recursively copies the fields added by the server
+ from the `source_flow` to the `target_flow`.
- return int(flow_id)
+ Parameters
+ ----------
+ source_flow : OpenMLFlow
+ To copy the fields from.
+ target_flow : OpenMLFlow
+ To copy the fields to.
+
+ Returns
+ -------
+ None
+ """
+ fields_added_by_the_server = ["flow_id", "uploader", "version", "upload_date"]
+ for field in fields_added_by_the_server:
+ setattr(target_flow, field, getattr(source_flow, field))
+
+ for name, component in source_flow.components.items():
+ assert name in target_flow.components
+ _copy_server_fields(component, target_flow.components[name])
- def _get_name(self):
- """Helper function. Can be mocked for testing."""
- return self.name
+def _add_if_nonempty(dic: dict, key: Hashable, value: Any) -> None:
+ """Adds a key-value pair to a dictionary if the value is not None.
+ Parameters
+ ----------
+ dic: dict
+ To add the key-value pair to.
+ key: hashable
+ To add to the dictionary.
+ value: Any
+ To add to the dictionary.
+
+ Returns
+ -------
+ None
+ """
+ if value is not None:
+ dic[key] = value
diff --git a/openml/flows/functions.py b/openml/flows/functions.py
index 31933ede5..0a2058890 100644
--- a/openml/flows/functions.py
+++ b/openml/flows/functions.py
@@ -1,38 +1,668 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+import os
+import re
+from collections import OrderedDict
+from functools import partial
+from typing import Any
+
+import dateutil.parser
+import pandas as pd
import xmltodict
-from openml._api_calls import _perform_api_call
+import openml._api_calls
+import openml.utils
+from openml.exceptions import OpenMLCacheException
+from . import OpenMLFlow
-def _check_flow_exists(name, version):
- """Retrieves the flow id of the flow uniquely identified by name+version.
+FLOWS_CACHE_DIR_NAME = "flows"
- Parameter
- ---------
- name : string
- Name of the flow
- version : string
- Version information associated with flow.
+
+def _get_cached_flows() -> OrderedDict:
+ """Return all the cached flows.
+
+ Returns
+ -------
+ flows : OrderedDict
+ Dictionary with flows. Each flow is an instance of OpenMLFlow.
+ """
+ flows = OrderedDict() # type: 'OrderedDict[int, OpenMLFlow]'
+
+ flow_cache_dir = openml.utils._create_cache_directory(FLOWS_CACHE_DIR_NAME)
+ directory_content = os.listdir(flow_cache_dir) # noqa: PTH208
+ directory_content.sort()
+ # Find all flow ids for which we have downloaded
+ # the flow description
+
+ for filename in directory_content:
+ if not re.match(r"[0-9]*", filename):
+ continue
+
+ fid = int(filename)
+ flows[fid] = _get_cached_flow(fid)
+
+ return flows
+
+
+def _get_cached_flow(fid: int) -> OpenMLFlow:
+ """Get the cached flow with the given id.
+
+ Parameters
+ ----------
+ fid : int
+ Flow id.
+
+ Returns
+ -------
+ OpenMLFlow.
+ """
+ fid_cache_dir = openml.utils._create_cache_directory_for_id(FLOWS_CACHE_DIR_NAME, fid)
+ flow_file = fid_cache_dir / "flow.xml"
+
+ try:
+ with flow_file.open(encoding="utf8") as fh:
+ return _create_flow_from_xml(fh.read())
+ except OSError as e:
+ openml.utils._remove_cache_dir_for_id(FLOWS_CACHE_DIR_NAME, fid_cache_dir)
+ raise OpenMLCacheException(f"Flow file for fid {fid} not cached") from e
+
+
+@openml.utils.thread_safe_if_oslo_installed
+def get_flow(flow_id: int, reinstantiate: bool = False, strict_version: bool = True) -> OpenMLFlow: # noqa: FBT002
+ """Fetch an OpenMLFlow by its server-assigned ID.
+
+ Queries the OpenML REST API for the flow metadata and returns an
+ :class:`OpenMLFlow` instance. If the flow is already cached locally,
+ the cached copy is returned. Optionally the flow can be re-instantiated
+ into a concrete model instance using the registered extension.
+
+ Parameters
+ ----------
+ flow_id : int
+ The OpenML flow id.
+ reinstantiate : bool, optional (default=False)
+ If True, convert the flow description into a concrete model instance
+ using the flow's extension (e.g., sklearn). If conversion fails and
+ ``strict_version`` is True, an exception will be raised.
+ strict_version : bool, optional (default=True)
+ When ``reinstantiate`` is True, whether to enforce exact version
+ requirements for the extension/model. If False, a new flow may
+ be returned when versions differ.
Returns
-------
- flow_exist : int
- Flow id or -1 if the flow doesn't exist.
+ OpenMLFlow
+ The flow object with metadata; ``model`` may be populated when
+ ``reinstantiate=True``.
+
+ Raises
+ ------
+ OpenMLCacheException
+ When cached flow files are corrupted or cannot be read.
+ OpenMLServerException
+ When the REST API call fails.
+
+ Side Effects
+ ------------
+ - Writes to ``openml.config.cache_directory/flows/{flow_id}/flow.xml``
+ when the flow is downloaded from the server.
+
+ Preconditions
+ -------------
+ - Network access to the OpenML server is required unless the flow is cached.
+ - For private flows, ``openml.config.apikey`` must be set.
Notes
-----
- see https://www.openml.org/api_docs/#!/flow/get_flow_exists_name_version
- """
- if not (type(name) is str and len(name) > 0):
- raise ValueError('Argument \'name\' should be a non-empty string')
- if not (type(version) is str and len(version) > 0):
- raise ValueError('Argument \'version\' should be a non-empty string')
-
- return_code, xml_response = _perform_api_call(
- "flow/exists/%s/%s" % (name, version))
- # TODO check with latest version of code if this raises an exception
- if return_code != 200:
- # fixme raise appropriate error
- raise ValueError("api call failed: %s" % xml_response)
- xml_dict = xmltodict.parse(xml_response)
- flow_id = xml_dict['oml:flow_exists']['oml:id']
- return return_code, xml_response, flow_id
+ Results are cached to speed up subsequent calls. When ``reinstantiate`` is
+ True and version mismatches occur, a new flow may be returned to reflect
+ the converted model (only when ``strict_version`` is False).
+
+ Examples
+ --------
+ >>> import openml
+ >>> flow = openml.flows.get_flow(5) # doctest: +SKIP
+ """
+ flow_id = int(flow_id)
+ flow = _get_flow_description(flow_id)
+
+ if reinstantiate:
+ flow.model = flow.extension.flow_to_model(flow, strict_version=strict_version)
+ if not strict_version:
+ # check if we need to return a new flow b/c of version mismatch
+ new_flow = flow.extension.model_to_flow(flow.model)
+ if new_flow.dependencies != flow.dependencies:
+ return new_flow
+ return flow
+
+
+def _get_flow_description(flow_id: int) -> OpenMLFlow:
+ """Get the Flow for a given ID.
+
+ Does the real work for get_flow. It returns a cached flow
+ instance if the flow exists locally, otherwise it downloads the
+ flow and returns an instance created from the xml representation.
+
+ Parameters
+ ----------
+ flow_id : int
+ The OpenML flow id.
+
+ Returns
+ -------
+ OpenMLFlow
+ """
+ try:
+ return _get_cached_flow(flow_id)
+ except OpenMLCacheException:
+ xml_file = (
+ openml.utils._create_cache_directory_for_id(FLOWS_CACHE_DIR_NAME, flow_id) / "flow.xml"
+ )
+ flow_xml = openml._api_calls._perform_api_call(f"flow/{flow_id}", request_method="get")
+
+ with xml_file.open("w", encoding="utf8") as fh:
+ fh.write(flow_xml)
+
+ return _create_flow_from_xml(flow_xml)
+
+
+def list_flows(
+ offset: int | None = None,
+ size: int | None = None,
+ tag: str | None = None,
+ uploader: str | None = None,
+) -> pd.DataFrame:
+ """List flows available on the OpenML server.
+
+ This function supports paging and filtering and returns a pandas
+ DataFrame with one row per flow and columns for id, name, version,
+ external_version, full_name and uploader.
+
+ Parameters
+ ----------
+ offset : int, optional
+ Number of flows to skip, starting from the first (for paging).
+ size : int, optional
+ Maximum number of flows to return.
+ tag : str, optional
+ Only return flows having this tag.
+ uploader : str, optional
+ Only return flows uploaded by this user.
+
+ Returns
+ -------
+ pandas.DataFrame
+ Rows correspond to flows. Columns include ``id``, ``full_name``,
+ ``name``, ``version``, ``external_version``, and ``uploader``.
+
+ Raises
+ ------
+ OpenMLServerException
+ When the API call fails.
+
+ Side Effects
+ ------------
+ - None: results are fetched and returned; Read-only operation.
+
+ Preconditions
+ -------------
+ - Network access is required to list flows unless cached mechanisms are
+ used by the underlying API helper.
+
+ Examples
+ --------
+ >>> import openml
+ >>> flows = openml.flows.list_flows(size=100) # doctest: +SKIP
+ """
+ listing_call = partial(_list_flows, tag=tag, uploader=uploader)
+ batches = openml.utils._list_all(listing_call, offset=offset, limit=size)
+ if len(batches) == 0:
+ return pd.DataFrame()
+
+ return pd.concat(batches)
+
+
+def _list_flows(limit: int, offset: int, **kwargs: Any) -> pd.DataFrame:
+ """
+ Perform the api call that return a list of all flows.
+
+ Parameters
+ ----------
+ limit : int
+ the maximum number of flows to return
+ offset : int
+ the number of flows to skip, starting from the first
+ kwargs: dict, optional
+ Legal filter operators: uploader, tag
+
+ Returns
+ -------
+ flows : dataframe
+ """
+ api_call = "flow/list"
+
+ if limit is not None:
+ api_call += f"/limit/{limit}"
+ if offset is not None:
+ api_call += f"/offset/{offset}"
+
+ if kwargs is not None:
+ for operator, value in kwargs.items():
+ if value is not None:
+ api_call += f"/{operator}/{value}"
+
+ return __list_flows(api_call=api_call)
+
+
+def flow_exists(name: str, external_version: str) -> int | bool:
+ """Check whether a flow (name + external_version) exists on the server.
+
+ The OpenML server defines uniqueness of flows by the pair
+ ``(name, external_version)``. This helper queries the server and
+ returns the corresponding flow id when present.
+
+ Parameters
+ ----------
+ name : str
+ Flow name (e.g., ``sklearn.tree._classes.DecisionTreeClassifier(1)``).
+ external_version : str
+ Version information associated with flow.
+
+ Returns
+ -------
+ int or bool
+ The flow id if the flow exists on the server, otherwise ``False``.
+
+ Raises
+ ------
+ ValueError
+ If ``name`` or ``external_version`` are empty or not strings.
+ OpenMLServerException
+ When the API request fails.
+
+ Examples
+ --------
+ >>> import openml
+ >>> openml.flows.flow_exists("weka.JRip", "Weka_3.9.0_10153") # doctest: +SKIP
+ """
+ if not (isinstance(name, str) and len(name) > 0):
+ raise ValueError("Argument 'name' should be a non-empty string")
+ if not (isinstance(name, str) and len(external_version) > 0):
+ raise ValueError("Argument 'version' should be a non-empty string")
+
+ xml_response = openml._api_calls._perform_api_call(
+ "flow/exists",
+ "post",
+ data={"name": name, "external_version": external_version},
+ )
+
+ result_dict = xmltodict.parse(xml_response)
+ flow_id = int(result_dict["oml:flow_exists"]["oml:id"])
+ return flow_id if flow_id > 0 else False
+
+
+def get_flow_id(
+ model: Any | None = None,
+ name: str | None = None,
+ exact_version: bool = True, # noqa: FBT002
+) -> int | bool | list[int]:
+ """Retrieve flow id(s) for a model instance or a flow name.
+
+ Provide either a concrete ``model`` (which will be converted to a flow by
+ the appropriate extension) or a flow ``name``. Behavior depends on
+ ``exact_version``:
+
+ - ``model`` + ``exact_version=True``: convert ``model`` to a flow and call
+ :func:`flow_exists` to get a single flow id (or False).
+ - ``model`` + ``exact_version=False``: convert ``model`` to a flow and
+ return all server flow ids with the same flow name.
+ - ``name``: ignore ``exact_version`` and return all server flow ids that
+ match ``name``.
+
+ Parameters
+ ----------
+ model : object, optional
+ A model instance that can be handled by a registered extension. Either
+ ``model`` or ``name`` must be provided.
+ name : str, optional
+ Flow name to query for. Either ``model`` or ``name`` must be provided.
+ exact_version : bool, optional (default=True)
+ When True and ``model`` is provided, only return the id for the exact
+ external version. When False, return a list of matching ids.
+
+ Returns
+ -------
+ int or bool or list[int]
+ If ``exact_version`` is True: the flow id if found, otherwise ``False``.
+ If ``exact_version`` is False: a list of matching flow ids (may be empty).
+
+ Raises
+ ------
+ ValueError
+ If neither ``model`` nor ``name`` is provided, or if both are provided.
+ OpenMLServerException
+ If underlying API calls fail.
+
+ Side Effects
+ ------------
+ - May call server APIs (``flow/exists``, ``flow/list``) and therefore
+ depends on network access and API keys for private flows.
+
+ Examples
+ --------
+ >>> import openml
+ >>> # Lookup by flow name
+ >>> openml.flows.get_flow_id(name="weka.JRip") # doctest: +SKIP
+ >>> # Lookup by model instance (requires a registered extension)
+ >>> import sklearn
+ >>> import openml_sklearn
+ >>> clf = sklearn.tree.DecisionTreeClassifier()
+ >>> openml.flows.get_flow_id(model=clf) # doctest: +SKIP
+ """
+ if model is not None and name is not None:
+ raise ValueError("Must provide either argument `model` or argument `name`, but not both.")
+
+ if model is not None:
+ extension = openml.extensions.get_extension_by_model(model, raise_if_no_extension=True)
+ if extension is None:
+ # This should never happen and is only here to please mypy will be gone soon once the
+ # whole function is removed
+ raise TypeError(extension)
+ flow = extension.model_to_flow(model)
+ flow_name = flow.name
+ external_version = flow.external_version
+ elif name is not None:
+ flow_name = name
+ exact_version = False
+ external_version = None
+ else:
+ raise ValueError(
+ "Need to provide either argument `model` or argument `name`, but both are `None`."
+ )
+
+ if exact_version:
+ if external_version is None:
+ raise ValueError("exact_version should be False if model is None!")
+ return flow_exists(name=flow_name, external_version=external_version)
+
+ flows = list_flows()
+ flows = flows.query(f'name == "{flow_name}"')
+ return flows["id"].to_list() # type: ignore[no-any-return]
+
+
+def __list_flows(api_call: str) -> pd.DataFrame:
+ """Retrieve information about flows from OpenML API
+ and parse it to a dictionary or a Pandas DataFrame.
+
+ Parameters
+ ----------
+ api_call: str
+ Retrieves the information about flows.
+
+ Returns
+ -------
+ The flows information in the specified output format.
+ """
+ xml_string = openml._api_calls._perform_api_call(api_call, "get")
+ flows_dict = xmltodict.parse(xml_string, force_list=("oml:flow",))
+
+ # Minimalistic check if the XML is useful
+ assert isinstance(flows_dict["oml:flows"]["oml:flow"], list), type(flows_dict["oml:flows"])
+ assert flows_dict["oml:flows"]["@xmlns:oml"] == "http://openml.org/openml", flows_dict[
+ "oml:flows"
+ ]["@xmlns:oml"]
+
+ flows = {}
+ for flow_ in flows_dict["oml:flows"]["oml:flow"]:
+ fid = int(flow_["oml:id"])
+ flow = {
+ "id": fid,
+ "full_name": flow_["oml:full_name"],
+ "name": flow_["oml:name"],
+ "version": flow_["oml:version"],
+ "external_version": flow_["oml:external_version"],
+ "uploader": flow_["oml:uploader"],
+ }
+ flows[fid] = flow
+
+ return pd.DataFrame.from_dict(flows, orient="index")
+
+
+def _check_flow_for_server_id(flow: OpenMLFlow) -> None:
+ """Raises a ValueError if the flow or any of its subflows has no flow id."""
+ # Depth-first search to check if all components were uploaded to the
+ # server before parsing the parameters
+ stack = [flow]
+ while len(stack) > 0:
+ current = stack.pop()
+ if current.flow_id is None:
+ raise ValueError(f"Flow {current.name} has no flow_id!")
+
+ for component in current.components.values():
+ stack.append(component)
+
+
+def assert_flows_equal( # noqa: C901, PLR0912, PLR0913, PLR0915
+ flow1: OpenMLFlow,
+ flow2: OpenMLFlow,
+ ignore_parameter_values_on_older_children: str | None = None,
+ ignore_parameter_values: bool = False, # noqa: FBT002
+ ignore_custom_name_if_none: bool = False, # noqa: FBT002
+ check_description: bool = True, # noqa: FBT002
+) -> None:
+ """Check equality of two flows.
+
+ Two flows are equal if their all keys which are not set by the server
+ are equal, as well as all their parameters and components.
+
+ Parameters
+ ----------
+ flow1 : OpenMLFlow
+
+ flow2 : OpenMLFlow
+
+ ignore_parameter_values_on_older_children : str (optional)
+ If set to ``OpenMLFlow.upload_date``, ignores parameters in a child
+ flow if it's upload date predates the upload date of the parent flow.
+
+ ignore_parameter_values : bool
+ Whether to ignore parameter values when comparing flows.
+
+ ignore_custom_name_if_none : bool
+ Whether to ignore the custom name field if either flow has `custom_name` equal to `None`.
+
+ check_description : bool
+ Whether to ignore matching of flow descriptions.
+
+ Raises
+ ------
+ TypeError
+ When either argument is not an :class:`OpenMLFlow`.
+ ValueError
+ When a relevant mismatch is found between the two flows.
+
+ Examples
+ --------
+ >>> import openml
+ >>> f1 = openml.flows.get_flow(5) # doctest: +SKIP
+ >>> f2 = openml.flows.get_flow(5) # doctest: +SKIP
+ >>> openml.flows.assert_flows_equal(f1, f2) # doctest: +SKIP
+ >>> # If flows differ, a ValueError is raised
+ """
+ if not isinstance(flow1, OpenMLFlow):
+ raise TypeError(f"Argument 1 must be of type OpenMLFlow, but is {type(flow1)}")
+
+ if not isinstance(flow2, OpenMLFlow):
+ raise TypeError(f"Argument 2 must be of type OpenMLFlow, but is {type(flow2)}")
+
+ # TODO as they are actually now saved during publish, it might be good to
+ # check for the equality of these as well.
+ generated_by_the_server = [
+ "flow_id",
+ "uploader",
+ "version",
+ "upload_date",
+ # Tags aren't directly created by the server,
+ # but the uploader has no control over them!
+ "tags",
+ ]
+ ignored_by_python_api = ["binary_url", "binary_format", "binary_md5", "model", "_entity_id"]
+
+ for key in set(flow1.__dict__.keys()).union(flow2.__dict__.keys()):
+ if key in generated_by_the_server + ignored_by_python_api:
+ continue
+ attr1 = getattr(flow1, key, None)
+ attr2 = getattr(flow2, key, None)
+ if key == "components":
+ if not (isinstance(attr1, dict) and isinstance(attr2, dict)):
+ raise TypeError("Cannot compare components because they are not dictionary.")
+
+ for name in set(attr1.keys()).union(attr2.keys()):
+ if name not in attr1:
+ raise ValueError(
+ f"Component {name} only available in argument2, but not in argument1.",
+ )
+ if name not in attr2:
+ raise ValueError(
+ f"Component {name} only available in argument2, but not in argument1.",
+ )
+ assert_flows_equal(
+ attr1[name],
+ attr2[name],
+ ignore_parameter_values_on_older_children,
+ ignore_parameter_values,
+ ignore_custom_name_if_none,
+ )
+ elif key == "_extension":
+ continue
+ elif check_description and key == "description":
+ # to ignore matching of descriptions since sklearn based flows may have
+ # altering docstrings and is not guaranteed to be consistent
+ continue
+ else:
+ if key == "parameters":
+ if ignore_parameter_values or ignore_parameter_values_on_older_children:
+ params_flow_1 = set(flow1.parameters.keys())
+ params_flow_2 = set(flow2.parameters.keys())
+ symmetric_difference = params_flow_1 ^ params_flow_2
+ if len(symmetric_difference) > 0:
+ raise ValueError(
+ f"Flow {flow1.name}: parameter set of flow "
+ "differs from the parameters stored "
+ "on the server.",
+ )
+
+ if ignore_parameter_values_on_older_children:
+ assert flow1.upload_date is not None, (
+ "Flow1 has no upload date that allows us to compare age of children."
+ )
+ upload_date_current_flow = dateutil.parser.parse(flow1.upload_date)
+ upload_date_parent_flow = dateutil.parser.parse(
+ ignore_parameter_values_on_older_children,
+ )
+ if upload_date_current_flow < upload_date_parent_flow:
+ continue
+
+ if ignore_parameter_values:
+ # Continue needs to be done here as the first if
+ # statement triggers in both special cases
+ continue
+ elif (
+ key == "custom_name"
+ and ignore_custom_name_if_none
+ and (attr1 is None or attr2 is None)
+ ):
+ # If specified, we allow `custom_name` inequality if one flow's name is None.
+ # Helps with backwards compatibility as `custom_name` is now auto-generated, but
+ # before it used to be `None`.
+ continue
+ elif key == "parameters_meta_info":
+ # this value is a dictionary where each key is a parameter name, containing another
+ # dictionary with keys specifying the parameter's 'description' and 'data_type'
+ # checking parameter descriptions can be ignored since that might change
+ # data type check can also be ignored if one of them is not defined, i.e., None
+ params1 = set(flow1.parameters_meta_info)
+ params2 = set(flow2.parameters_meta_info)
+ if params1 != params2:
+ raise ValueError(
+ "Parameter list in meta info for parameters differ in the two flows.",
+ )
+ # iterating over the parameter's meta info list
+ for param in params1:
+ if (
+ isinstance(flow1.parameters_meta_info[param], dict)
+ and isinstance(flow2.parameters_meta_info[param], dict)
+ and "data_type" in flow1.parameters_meta_info[param]
+ and "data_type" in flow2.parameters_meta_info[param]
+ ):
+ value1 = flow1.parameters_meta_info[param]["data_type"]
+ value2 = flow2.parameters_meta_info[param]["data_type"]
+ else:
+ value1 = flow1.parameters_meta_info[param]
+ value2 = flow2.parameters_meta_info[param]
+ if value1 is None or value2 is None:
+ continue
+
+ if value1 != value2:
+ raise ValueError(
+ f"Flow {flow1.name}: data type for parameter {param} in {key} differ "
+ f"as {value1}\nvs\n{value2}",
+ )
+ # the continue is to avoid the 'attr != attr2' check at end of function
+ continue
+
+ if attr1 != attr2:
+ raise ValueError(
+ f"Flow {flow1.name!s}: values for attribute '{key!s}' differ: "
+ f"'{attr1!s}'\nvs\n'{attr2!s}'.",
+ )
+
+
+def _create_flow_from_xml(flow_xml: str) -> OpenMLFlow:
+ """Create flow object from xml
+
+ Parameters
+ ----------
+ flow_xml: xml representation of a flow
+
+ Returns
+ -------
+ OpenMLFlow
+ """
+ return OpenMLFlow._from_dict(xmltodict.parse(flow_xml))
+
+
+def delete_flow(flow_id: int) -> bool:
+ """Delete flow with id `flow_id` from the OpenML server.
+
+ You can only delete flows which you uploaded and which
+ which are not linked to runs.
+
+ Parameters
+ ----------
+ flow_id : int
+ OpenML id of the flow
+
+ Returns
+ -------
+ bool
+ True if the deletion was successful. False otherwise.
+
+ Raises
+ ------
+ OpenMLServerException
+ If the server-side deletion fails due to permissions or other errors.
+
+ Side Effects
+ ------------
+ - Removes the flow from the OpenML server (if permitted).
+
+ Examples
+ --------
+ >>> import openml
+ >>> # Deletes flow 23 if you are the uploader and it's not linked to runs
+ >>> openml.flows.delete_flow(23) # doctest: +SKIP
+ """
+ return openml.utils._delete_entity("flow", flow_id)
diff --git a/.nojekyll b/openml/py.typed
similarity index 100%
rename from .nojekyll
rename to openml/py.typed
diff --git a/openml/runs/__init__.py b/openml/runs/__init__.py
index 38c869fe0..2f068a2e6 100644
--- a/openml/runs/__init__.py
+++ b/openml/runs/__init__.py
@@ -1,4 +1,32 @@
+# License: BSD 3-Clause
+
+from .functions import (
+ delete_run,
+ get_run,
+ get_run_trace,
+ get_runs,
+ initialize_model_from_run,
+ initialize_model_from_trace,
+ list_runs,
+ run_exists,
+ run_flow_on_task,
+ run_model_on_task,
+)
from .run import OpenMLRun
-from .functions import (run_task, get_run, list_runs)
+from .trace import OpenMLRunTrace, OpenMLTraceIteration
-__all__ = ['OpenMLRun', 'run_task', 'get_run', 'list_runs']
+__all__ = [
+ "OpenMLRun",
+ "OpenMLRunTrace",
+ "OpenMLTraceIteration",
+ "delete_run",
+ "get_run",
+ "get_run_trace",
+ "get_runs",
+ "initialize_model_from_run",
+ "initialize_model_from_trace",
+ "list_runs",
+ "run_exists",
+ "run_flow_on_task",
+ "run_model_on_task",
+]
diff --git a/openml/runs/functions.py b/openml/runs/functions.py
index d65c470a1..503788dbd 100644
--- a/openml/runs/functions.py
+++ b/openml/runs/functions.py
@@ -1,107 +1,790 @@
-from collections import OrderedDict, defaultdict
-import io
-import os
-import sys
+# License: BSD 3-Clause
+from __future__ import annotations
+
+import itertools
+import time
+import warnings
+from collections import OrderedDict
+from functools import partial
+from pathlib import Path
+from typing import TYPE_CHECKING, Any
+
+import numpy as np
+import pandas as pd
+import sklearn.metrics
import xmltodict
+from joblib.parallel import Parallel, delayed
+
+import openml
+import openml._api_calls
+import openml.utils
+from openml import config
+from openml.exceptions import (
+ OpenMLCacheException,
+ OpenMLRunsExistError,
+ OpenMLServerException,
+ PyOpenMLError,
+)
+from openml.extensions import get_extension_by_model
+from openml.flows import OpenMLFlow, flow_exists, get_flow
+from openml.flows.flow import _copy_server_fields
+from openml.setups import initialize_model, setup_exists
+from openml.tasks import (
+ OpenMLClassificationTask,
+ OpenMLClusteringTask,
+ OpenMLLearningCurveTask,
+ OpenMLRegressionTask,
+ OpenMLSupervisedTask,
+ OpenMLTask,
+ TaskType,
+ get_task,
+)
-from .. import config
-from ..flows import OpenMLFlow
-from ..exceptions import OpenMLCacheException
-from ..util import URLError
-from ..tasks.functions import _create_task_from_xml
-from .._api_calls import _perform_api_call
from .run import OpenMLRun
+from .trace import OpenMLRunTrace
+
+# Avoid import cycles: https://mypy.readthedocs.io/en/latest/common_issues.html#import-cycles
+if TYPE_CHECKING:
+ from openml.config import _Config
+ from openml.extensions.extension_interface import Extension
+
+# get_dict is in run.py to avoid circular imports
+
+RUNS_CACHE_DIR_NAME = "runs"
+ERROR_CODE = 512
+
+
+# TODO(eddiebergman): Could potentially overload this but
+# it seems very big to do so
+def run_model_on_task( # noqa: PLR0913
+ model: Any,
+ task: int | str | OpenMLTask,
+ avoid_duplicate_runs: bool | None = None,
+ flow_tags: list[str] | None = None,
+ seed: int | None = None,
+ add_local_measures: bool = True, # noqa: FBT002
+ upload_flow: bool = False, # noqa: FBT002
+ return_flow: bool = False, # noqa: FBT002
+ n_jobs: int | None = None,
+) -> OpenMLRun | tuple[OpenMLRun, OpenMLFlow]:
+ """Run the model on the dataset defined by the task.
+ Parameters
+ ----------
+ model : sklearn model
+ A model which has a function fit(X,Y) and predict(X),
+ all supervised estimators of scikit learn follow this definition of a model.
+ task : OpenMLTask or int or str
+ Task to perform or Task id.
+ This may be a model instead if the first argument is an OpenMLTask.
+ avoid_duplicate_runs : bool, optional (default=None)
+ If True, the run will throw an error if the setup/task combination is already present on
+ the server. This feature requires an internet connection.
+ If not set, it will use the default from your openml configuration (False if unset).
+ flow_tags : List[str], optional (default=None)
+ A list of tags that the flow should have at creation.
+ seed: int, optional (default=None)
+ Models that are not seeded will get this seed.
+ add_local_measures : bool, optional (default=True)
+ Determines whether to calculate a set of evaluation measures locally,
+ to later verify server behaviour.
+ upload_flow : bool (default=False)
+ If True, upload the flow to OpenML if it does not exist yet.
+ If False, do not upload the flow to OpenML.
+ return_flow : bool (default=False)
+ If True, returns the OpenMLFlow generated from the model in addition to the OpenMLRun.
+ n_jobs : int (default=None)
+ The number of processes/threads to distribute the evaluation asynchronously.
+ If `None` or `1`, then the evaluation is treated as synchronous and processed sequentially.
+ If `-1`, then the job uses as many cores available.
-# _get_version_info, _get_dict and _create_setup_string are in run.py to avoid
-# circular imports
+ Returns
+ -------
+ run : OpenMLRun
+ Result of the run.
+ flow : OpenMLFlow (optional, only if `return_flow` is True).
+ Flow generated from the model.
+ """
+ if avoid_duplicate_runs is None:
+ avoid_duplicate_runs = openml.config.avoid_duplicate_runs
+ if avoid_duplicate_runs and not config.apikey:
+ warnings.warn(
+ "avoid_duplicate_runs is set to True, but no API key is set. "
+ "Please set your API key in the OpenML configuration file, see"
+ "https://openml.github.io/openml-python/main/examples/20_basic/introduction_tutorial"
+ ".html#authentication for more information on authentication.",
+ RuntimeWarning,
+ stacklevel=2,
+ )
+
+ # TODO: At some point in the future do not allow for arguments in old order (6-2018).
+ # Flexibility currently still allowed due to code-snippet in OpenML100 paper (3-2019).
+ # When removing this please also remove the method `is_estimator` from the extension
+ # interface as it is only used here (MF, 3-2019)
+ if isinstance(model, (int, str, OpenMLTask)):
+ warnings.warn(
+ "The old argument order (task, model) is deprecated and "
+ "will not be supported in the future. Please use the "
+ "order (model, task).",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ task, model = model, task
+
+ extension = get_extension_by_model(model, raise_if_no_extension=True)
+ if extension is None:
+ # This should never happen and is only here to please mypy will be gone soon once the
+ # whole function is removed
+ raise TypeError(extension)
+
+ flow = extension.model_to_flow(model)
+
+ def get_task_and_type_conversion(_task: int | str | OpenMLTask) -> OpenMLTask:
+ """Retrieve an OpenMLTask object from either an integer or string ID,
+ or directly from an OpenMLTask object.
+
+ Parameters
+ ----------
+ _task : Union[int, str, OpenMLTask]
+ The task ID or the OpenMLTask object.
+
+ Returns
+ -------
+ OpenMLTask
+ The OpenMLTask object.
+ """
+ if isinstance(_task, (int, str)):
+ return get_task(int(_task)) # type: ignore
+
+ return _task
+
+ task = get_task_and_type_conversion(task)
+
+ run = run_flow_on_task(
+ task=task,
+ flow=flow,
+ avoid_duplicate_runs=avoid_duplicate_runs,
+ flow_tags=flow_tags,
+ seed=seed,
+ add_local_measures=add_local_measures,
+ upload_flow=upload_flow,
+ n_jobs=n_jobs,
+ )
+ if return_flow:
+ return run, flow
+ return run
+def run_flow_on_task( # noqa: C901, PLR0912, PLR0915, PLR0913
+ flow: OpenMLFlow,
+ task: OpenMLTask,
+ avoid_duplicate_runs: bool | None = None,
+ flow_tags: list[str] | None = None,
+ seed: int | None = None,
+ add_local_measures: bool = True, # noqa: FBT002
+ upload_flow: bool = False, # noqa: FBT002
+ n_jobs: int | None = None,
+) -> OpenMLRun:
+ """Run the model provided by the flow on the dataset defined by task.
-def run_task(task, model):
- """Performs a CV run on the dataset of the given task, using the split.
+ Takes the flow and repeat information into account.
+ The Flow may optionally be published.
Parameters
----------
+ flow : OpenMLFlow
+ A flow wraps a machine learning model together with relevant information.
+ The model has a function fit(X,Y) and predict(X),
+ all supervised estimators of scikit learn follow this definition of a model.
task : OpenMLTask
- Task to perform.
- model : sklearn model
- a model which has a function fit(X,Y) and predict(X),
- all supervised estimators of scikit learn follow this definition of a model [1]
- [1](http://scikit-learn.org/stable/tutorial/statistical_inference/supervised_learning.html)
-
+ Task to perform. This may be an OpenMLFlow instead if the first argument is an OpenMLTask.
+ avoid_duplicate_runs : bool, optional (default=None)
+ If True, the run will throw an error if the setup/task combination is already present on
+ the server. This feature requires an internet connection.
+ If not set, it will use the default from your openml configuration (False if unset).
+ flow_tags : List[str], optional (default=None)
+ A list of tags that the flow should have at creation.
+ seed: int, optional (default=None)
+ Models that are not seeded will get this seed.
+ add_local_measures : bool, optional (default=True)
+ Determines whether to calculate a set of evaluation measures locally,
+ to later verify server behaviour.
+ upload_flow : bool (default=False)
+ If True, upload the flow to OpenML if it does not exist yet.
+ If False, do not upload the flow to OpenML.
+ n_jobs : int (default=None)
+ The number of processes/threads to distribute the evaluation asynchronously.
+ If `None` or `1`, then the evaluation is treated as synchronous and processed sequentially.
+ If `-1`, then the job uses as many cores available.
Returns
-------
run : OpenMLRun
Result of the run.
"""
- # TODO move this into its onwn module. While it somehow belongs here, it
- # adds quite a lot of functionality which is better suited in other places!
- # TODO why doesn't this accept a flow as input?
-
- flow = OpenMLFlow(model=model)
- flow_id = flow._ensure_flow_exists()
- if (flow_id < 0):
- print("No flow")
- return 0, 2
- config.logger.info(flow_id)
-
- arff_datacontent = []
+ if flow_tags is not None and not isinstance(flow_tags, list):
+ raise ValueError("flow_tags should be a list")
+
+ if avoid_duplicate_runs is None:
+ avoid_duplicate_runs = openml.config.avoid_duplicate_runs
+
+ # TODO: At some point in the future do not allow for arguments in old order (changed 6-2018).
+ # Flexibility currently still allowed due to code-snippet in OpenML100 paper (3-2019).
+ if isinstance(flow, OpenMLTask) and isinstance(task, OpenMLFlow):
+ # We want to allow either order of argument (to avoid confusion).
+ warnings.warn(
+ "The old argument order (Flow, model) is deprecated and "
+ "will not be supported in the future. Please use the "
+ "order (model, Flow).",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ task, flow = flow, task
+
+ if task.task_id is None:
+ raise ValueError("The task should be published at OpenML")
+
+ if flow.model is None:
+ flow.model = flow.extension.flow_to_model(flow)
+
+ flow.model = flow.extension.seed_model(flow.model, seed=seed)
+
+ # We only need to sync with the server right now if we want to upload the flow,
+ # or ensure no duplicate runs exist. Otherwise it can be synced at upload time.
+ flow_id = None
+ if upload_flow or avoid_duplicate_runs:
+ flow_id = flow_exists(flow.name, flow.external_version)
+ if isinstance(flow.flow_id, int) and flow_id != flow.flow_id:
+ if flow_id is not False:
+ raise PyOpenMLError(
+ f"Local flow_id does not match server flow_id: '{flow.flow_id}' vs '{flow_id}'",
+ )
+ raise PyOpenMLError(
+ "Flow does not exist on the server, but 'flow.flow_id' is not None."
+ )
+ if upload_flow and flow_id is False:
+ flow.publish()
+ flow_id = flow.flow_id
+ elif flow_id:
+ flow_from_server = get_flow(flow_id)
+ _copy_server_fields(flow_from_server, flow)
+ if avoid_duplicate_runs:
+ flow_from_server.model = flow.model
+ setup_id = setup_exists(flow_from_server)
+ ids = run_exists(task.task_id, setup_id)
+ if ids:
+ error_message = (
+ "One or more runs of this setup were already performed on the task."
+ )
+ raise OpenMLRunsExistError(ids, error_message)
+ else:
+ # Flow does not exist on server and we do not want to upload it.
+ # No sync with the server happens.
+ flow_id = None
dataset = task.get_dataset()
- X, Y = dataset.get_data(target=task.target_name)
- class_labels = task.class_labels
- if class_labels is None:
- raise ValueError('The task has no class labels. This method currently '
- 'only works for tasks with class labels.')
+ run_environment = flow.extension.get_version_information()
+ tags = ["openml-python", run_environment[1]]
+
+ if flow.extension.check_if_model_fitted(flow.model):
+ warnings.warn(
+ "The model is already fitted! This might cause inconsistency in comparison of results.",
+ RuntimeWarning,
+ stacklevel=2,
+ )
+
+ # execute the run
+ res = _run_task_get_arffcontent(
+ model=flow.model,
+ task=task,
+ extension=flow.extension,
+ add_local_measures=add_local_measures,
+ n_jobs=n_jobs,
+ )
+
+ data_content, trace, fold_evaluations, sample_evaluations = res
+ fields = [*run_environment, time.strftime("%c"), "Created by run_flow_on_task"]
+ generated_description = "\n".join(fields)
+ run = OpenMLRun(
+ task_id=task.task_id,
+ flow_id=flow_id,
+ dataset_id=dataset.dataset_id,
+ model=flow.model,
+ flow_name=flow.name,
+ tags=tags,
+ trace=trace,
+ data_content=data_content,
+ flow=flow,
+ setup_string=flow.extension.create_setup_string(flow.model),
+ description_text=generated_description,
+ )
+
+ if (upload_flow or avoid_duplicate_runs) and flow.flow_id is not None:
+ # We only extract the parameter settings if a sync happened with the server.
+ # I.e. when the flow was uploaded or we found it in the avoid_duplicate check.
+ # Otherwise, we will do this at upload time.
+ run.parameter_settings = flow.extension.obtain_parameter_values(flow)
+
+ # now we need to attach the detailed evaluations
+ if task.task_type_id == TaskType.LEARNING_CURVE:
+ run.sample_evaluations = sample_evaluations
+ else:
+ run.fold_evaluations = fold_evaluations
- run = OpenMLRun(task_id=task.task_id, flow_id=flow_id,
- dataset_id=dataset.dataset_id)
- run.data_content = _run_task_get_arffcontent(model, task, class_labels)
+ if flow_id:
+ message = f"Executed Task {task.task_id} with Flow id:{run.flow_id}"
+ else:
+ message = f"Executed Task {task.task_id} on local Flow with name {flow.name}."
+ config.logger.info(message)
- # The model will not be uploaded at the moment, but used to get the
- # hyperparameter values when uploading the run
- X, Y = task.get_X_and_y()
- run.model = model.fit(X, Y)
return run
-def _run_task_get_arffcontent(model, task, class_labels):
- X, Y = task.get_X_and_y()
- arff_datacontent = []
+def get_run_trace(run_id: int) -> OpenMLRunTrace:
+ """
+ Get the optimization trace object for a given run id.
+
+ Parameters
+ ----------
+ run_id : int
+
+ Returns
+ -------
+ openml.runs.OpenMLTrace
+ """
+ trace_xml = openml._api_calls._perform_api_call(f"run/trace/{run_id}", "get")
+ return OpenMLRunTrace.trace_from_xml(trace_xml)
+
+
+def initialize_model_from_run(run_id: int, *, strict_version: bool = True) -> Any:
+ """
+ Initialized a model based on a run_id (i.e., using the exact
+ same parameter settings)
+
+ Parameters
+ ----------
+ run_id : int
+ The Openml run_id
+ strict_version: bool (default=True)
+ See `flow_to_model` strict_version.
+
+ Returns
+ -------
+ model
+ """
+ run = get_run(run_id)
+ # TODO(eddiebergman): I imagine this is None if it's not published,
+ # might need to raise an explicit error for that
+ assert run.setup_id is not None
+ return initialize_model(setup_id=run.setup_id, strict_version=strict_version)
+
+
+def initialize_model_from_trace(
+ run_id: int,
+ repeat: int,
+ fold: int,
+ iteration: int | None = None,
+) -> Any:
+ """
+ Initialize a model based on the parameters that were set
+ by an optimization procedure (i.e., using the exact same
+ parameter settings)
+
+ Parameters
+ ----------
+ run_id : int
+ The Openml run_id. Should contain a trace file,
+ otherwise a OpenMLServerException is raised
+
+ repeat : int
+ The repeat nr (column in trace file)
+
+ fold : int
+ The fold nr (column in trace file)
+
+ iteration : int
+ The iteration nr (column in trace file). If None, the
+ best (selected) iteration will be searched (slow),
+ according to the selection criteria implemented in
+ OpenMLRunTrace.get_selected_iteration
+
+ Returns
+ -------
+ model
+ """
+ run = get_run(run_id)
+ # TODO(eddiebergman): I imagine this is None if it's not published,
+ # might need to raise an explicit error for that
+ assert run.flow_id is not None
+
+ flow = get_flow(run.flow_id)
+ run_trace = get_run_trace(run_id)
+
+ if iteration is None:
+ iteration = run_trace.get_selected_iteration(repeat, fold)
+
+ request = (repeat, fold, iteration)
+ if request not in run_trace.trace_iterations:
+ raise ValueError("Combination repeat, fold, iteration not available")
+ current = run_trace.trace_iterations[(repeat, fold, iteration)]
+
+ search_model = initialize_model_from_run(run_id)
+ return flow.extension.instantiate_model_from_hpo_class(search_model, current)
+
+
+def run_exists(task_id: int, setup_id: int) -> set[int]:
+ """Checks whether a task/setup combination is already present on the
+ server.
+
+ Parameters
+ ----------
+ task_id : int
+
+ setup_id : int
+
+ Returns
+ -------
+ Set run ids for runs where flow setup_id was run on task_id. Empty
+ set if it wasn't run yet.
+ """
+ if setup_id <= 0:
+ # openml setups are in range 1-inf
+ return set()
+
+ try:
+ result = list_runs(task=[task_id], setup=[setup_id])
+ return set() if result.empty else set(result["run_id"])
+ except OpenMLServerException as exception:
+ # error code implies no results. The run does not exist yet
+ if exception.code != ERROR_CODE:
+ raise exception
+ return set()
+
+
+def _run_task_get_arffcontent( # noqa: PLR0915, PLR0912, C901
+ *,
+ model: Any,
+ task: OpenMLTask,
+ extension: Extension,
+ add_local_measures: bool,
+ n_jobs: int | None = None,
+) -> tuple[
+ list[list],
+ OpenMLRunTrace | None,
+ OrderedDict[str, OrderedDict],
+ OrderedDict[str, OrderedDict],
+]:
+ """Runs the hyperparameter optimization on the given task
+ and returns the arfftrace content.
+
+ Parameters
+ ----------
+ model : Any
+ The model that is to be evalauted.
+ task : OpenMLTask
+ The OpenMLTask to evaluate.
+ extension : Extension
+ The OpenML extension object.
+ add_local_measures : bool
+ Whether to compute additional local evaluation measures.
+ n_jobs : int
+ Number of jobs to run in parallel.
+ If None, use 1 core by default. If -1, use all available cores.
+
+ Returns
+ -------
+ Tuple[List[List], Optional[OpenMLRunTrace],
+ OrderedDict[str, OrderedDict], OrderedDict[str, OrderedDict]]
+ A tuple containing the arfftrace content,
+ the OpenML run trace, the global and local evaluation measures.
+ """
+ arff_datacontent = [] # type: list[list]
+ traces = [] # type: list[OpenMLRunTrace]
+ # stores fold-based evaluation measures. In case of a sample based task,
+ # this information is multiple times overwritten, but due to the ordering
+ # of tne loops, eventually it contains the information based on the full
+ # dataset size
+ user_defined_measures_per_fold = OrderedDict() # type: 'OrderedDict[str, OrderedDict]'
+ # stores sample-based evaluation measures (sublevel of fold-based)
+ # will also be filled on a non sample-based task, but the information
+ # is the same as the fold-based measures, and disregarded in that case
+ user_defined_measures_per_sample = OrderedDict() # type: 'OrderedDict[str, OrderedDict]'
- rep_no = 0
# TODO use different iterator to only provide a single iterator (less
# methods, less maintenance, less confusion)
- for rep in task.iterate_repeats():
- fold_no = 0
- for fold in rep:
- train_indices, test_indices = fold
- trainX = X[train_indices]
- trainY = Y[train_indices]
- testX = X[test_indices]
- testY = Y[test_indices]
-
- model.fit(trainX, trainY)
- ProbaY = model.predict_proba(testX)
- PredY = model.predict(testX)
-
- for i in range(0, len(test_indices)):
- arff_line = [rep_no, fold_no, test_indices[i]]
- arff_line.extend(ProbaY[i])
- arff_line.append(class_labels[PredY[i]])
- arff_line.append(class_labels[testY[i]])
+ num_reps, num_folds, num_samples = task.get_split_dimensions()
+
+ jobs = []
+ for n_fit, (rep_no, fold_no, sample_no) in enumerate(
+ itertools.product(
+ range(num_reps),
+ range(num_folds),
+ range(num_samples),
+ ),
+ start=1,
+ ):
+ jobs.append((n_fit, rep_no, fold_no, sample_no))
+
+ # The forked child process may not copy the configuration state of OpenML from the parent.
+ # Current configuration setup needs to be copied and passed to the child processes.
+ _config = config.get_config_as_dict()
+ # Execute runs in parallel
+ # assuming the same number of tasks as workers (n_jobs), the total compute time for this
+ # statement will be similar to the slowest run
+ # TODO(eddiebergman): Simplify this
+ job_rvals: list[
+ tuple[
+ np.ndarray,
+ pd.DataFrame | None,
+ np.ndarray,
+ pd.DataFrame | None,
+ OpenMLRunTrace | None,
+ OrderedDict[str, float],
+ ],
+ ]
+ job_rvals = Parallel(verbose=0, n_jobs=n_jobs)( # type: ignore
+ delayed(_run_task_get_arffcontent_parallel_helper)(
+ extension=extension,
+ fold_no=fold_no,
+ model=model,
+ rep_no=rep_no,
+ sample_no=sample_no,
+ task=task,
+ configuration=_config,
+ )
+ for _n_fit, rep_no, fold_no, sample_no in jobs
+ ) # job_rvals contain the output of all the runs with one-to-one correspondence with `jobs`
+
+ for n_fit, rep_no, fold_no, sample_no in jobs:
+ pred_y, proba_y, test_indices, test_y, inner_trace, user_defined_measures_fold = job_rvals[
+ n_fit - 1
+ ]
+
+ if inner_trace is not None:
+ traces.append(inner_trace)
+
+ # add client-side calculated metrics. These is used on the server as
+ # consistency check, only useful for supervised tasks
+ def _calculate_local_measure( # type: ignore
+ sklearn_fn,
+ openml_name,
+ _test_y=test_y,
+ _pred_y=pred_y,
+ _user_defined_measures_fold=user_defined_measures_fold,
+ ):
+ _user_defined_measures_fold[openml_name] = sklearn_fn(_test_y, _pred_y)
+
+ if isinstance(task, (OpenMLClassificationTask, OpenMLLearningCurveTask)):
+ assert test_y is not None
+ assert proba_y is not None
+
+ for i, tst_idx in enumerate(test_indices):
+ if task.class_labels is not None:
+ prediction = (
+ task.class_labels[pred_y[i]]
+ if isinstance(pred_y[i], (int, np.integer))
+ else pred_y[i]
+ )
+ if isinstance(test_y, pd.Series):
+ truth = (
+ task.class_labels[test_y.iloc[i]]
+ if isinstance(test_y.iloc[i], int)
+ else test_y.iloc[i]
+ )
+ else:
+ truth = (
+ task.class_labels[test_y[i]]
+ if isinstance(test_y[i], (int, np.integer))
+ else test_y[i]
+ )
+ pred_prob = proba_y.iloc[i] if isinstance(proba_y, pd.DataFrame) else proba_y[i]
+
+ arff_line = format_prediction(
+ task=task,
+ repeat=rep_no,
+ fold=fold_no,
+ sample=sample_no,
+ index=tst_idx,
+ prediction=prediction,
+ truth=truth,
+ proba=dict(zip(task.class_labels, pred_prob, strict=False)),
+ )
+ else:
+ raise ValueError("The task has no class labels")
+
arff_datacontent.append(arff_line)
- fold_no = fold_no + 1
- rep_no = rep_no + 1
+ if add_local_measures:
+ _calculate_local_measure(
+ sklearn.metrics.accuracy_score,
+ "predictive_accuracy",
+ )
+
+ elif isinstance(task, OpenMLRegressionTask):
+ assert test_y is not None
+ for i, _ in enumerate(test_indices):
+ truth = test_y.iloc[i] if isinstance(test_y, pd.Series) else test_y[i]
+ arff_line = format_prediction(
+ task=task,
+ repeat=rep_no,
+ fold=fold_no,
+ index=test_indices[i],
+ prediction=pred_y[i],
+ truth=truth,
+ )
- return arff_datacontent
+ arff_datacontent.append(arff_line)
+ if add_local_measures:
+ _calculate_local_measure(
+ sklearn.metrics.mean_absolute_error,
+ "mean_absolute_error",
+ )
+
+ elif isinstance(task, OpenMLClusteringTask):
+ for i, _ in enumerate(test_indices):
+ arff_line = [test_indices[i], pred_y[i]] # row_id, cluster ID
+ arff_datacontent.append(arff_line)
+
+ else:
+ raise TypeError(type(task))
+
+ for measure in user_defined_measures_fold:
+ if measure not in user_defined_measures_per_fold:
+ user_defined_measures_per_fold[measure] = OrderedDict()
+ if rep_no not in user_defined_measures_per_fold[measure]:
+ user_defined_measures_per_fold[measure][rep_no] = OrderedDict()
+
+ if measure not in user_defined_measures_per_sample:
+ user_defined_measures_per_sample[measure] = OrderedDict()
+ if rep_no not in user_defined_measures_per_sample[measure]:
+ user_defined_measures_per_sample[measure][rep_no] = OrderedDict()
+ if fold_no not in user_defined_measures_per_sample[measure][rep_no]:
+ user_defined_measures_per_sample[measure][rep_no][fold_no] = OrderedDict()
+
+ user_defined_measures_per_fold[measure][rep_no][fold_no] = user_defined_measures_fold[
+ measure
+ ]
+ user_defined_measures_per_sample[measure][rep_no][fold_no][sample_no] = (
+ user_defined_measures_fold[measure]
+ )
+
+ trace: OpenMLRunTrace | None = None
+ if len(traces) > 0:
+ if len(traces) != len(jobs):
+ raise ValueError(
+ f"Did not find enough traces (expected {len(jobs)}, found {len(traces)})",
+ )
+
+ trace = OpenMLRunTrace.merge_traces(traces)
+
+ return (
+ arff_datacontent,
+ trace,
+ user_defined_measures_per_fold,
+ user_defined_measures_per_sample,
+ )
+
+
+def _run_task_get_arffcontent_parallel_helper( # noqa: PLR0913
+ extension: Extension,
+ fold_no: int,
+ model: Any,
+ rep_no: int,
+ sample_no: int,
+ task: OpenMLTask,
+ configuration: _Config | None = None,
+) -> tuple[
+ np.ndarray,
+ pd.DataFrame | None,
+ np.ndarray,
+ pd.DataFrame | None,
+ OpenMLRunTrace | None,
+ OrderedDict[str, float],
+]:
+ """Helper function that runs a single model on a single task fold sample.
-def get_runs(run_ids):
+ Parameters
+ ----------
+ extension : Extension
+ An OpenML extension instance.
+ fold_no : int
+ The fold number to be run.
+ model : Any
+ The model that is to be evaluated.
+ rep_no : int
+ Repetition number to be run.
+ sample_no : int
+ Sample number to be run.
+ task : OpenMLTask
+ The task object from OpenML.
+ configuration : _Config
+ Hyperparameters to configure the model.
+
+ Returns
+ -------
+ Tuple[np.ndarray, Optional[pd.DataFrame], np.ndarray, Optional[pd.DataFrame],
+ Optional[OpenMLRunTrace], OrderedDict[str, float]]
+ A tuple containing the predictions, probability estimates (if applicable),
+ actual target values, actual target value probabilities (if applicable),
+ the trace object of the OpenML run (if applicable),
+ and a dictionary of local measures for this particular fold.
+ """
+ # Sets up the OpenML instantiated in the child process to match that of the parent's
+ # if configuration=None, loads the default
+ config._setup(configuration)
+
+ train_indices, test_indices = task.get_train_test_split_indices(
+ repeat=rep_no,
+ fold=fold_no,
+ sample=sample_no,
+ )
+
+ if isinstance(task, OpenMLSupervisedTask):
+ x, y = task.get_X_and_y()
+ assert isinstance(y, (pd.Series, pd.DataFrame))
+ train_x = x.iloc[train_indices]
+ train_y = y.iloc[train_indices]
+ test_x = x.iloc[test_indices]
+ test_y = y.iloc[test_indices]
+ elif isinstance(task, OpenMLClusteringTask):
+ x = task.get_X()
+ train_x = x.iloc[train_indices]
+ train_y = None
+ test_x = None
+ test_y = None
+ else:
+ raise NotImplementedError(
+ f"Task type '{task.task_type}' is not supported. "
+ f"Only OpenMLSupervisedTask and OpenMLClusteringTask are currently implemented. "
+ f"Task details: task_id={getattr(task, 'task_id', 'unknown')}, "
+ f"task_class={task.__class__.__name__}"
+ )
+
+ config.logger.info(
+ f"Going to run model {model!s} on "
+ f"dataset {openml.datasets.get_dataset(task.dataset_id).name} "
+ f"for repeat {rep_no} fold {fold_no} sample {sample_no}"
+ )
+ (
+ pred_y,
+ proba_y,
+ user_defined_measures_fold,
+ trace,
+ ) = extension._run_model_on_fold(
+ model=model,
+ task=task,
+ X_train=train_x,
+ y_train=train_y,
+ rep_no=rep_no,
+ fold_no=fold_no,
+ X_test=test_x,
+ )
+ return pred_y, proba_y, test_indices, test_y, trace, user_defined_measures_fold # type: ignore
+
+
+def get_runs(run_ids: list[int]) -> list[OpenMLRun]:
"""Gets all runs in run_ids list.
Parameters
@@ -113,237 +796,524 @@ def get_runs(run_ids):
runs : list of OpenMLRun
List of runs corresponding to IDs, fetched from the server.
"""
-
runs = []
for run_id in run_ids:
runs.append(get_run(run_id))
return runs
-def get_run(run_id):
+@openml.utils.thread_safe_if_oslo_installed
+def get_run(run_id: int, ignore_cache: bool = False) -> OpenMLRun: # noqa: FBT002
"""Gets run corresponding to run_id.
Parameters
----------
run_id : int
+ ignore_cache : bool
+ Whether to ignore the cache. If ``true`` this will download and overwrite the run xml
+ even if the requested run is already cached.
+
+ ignore_cache
+
Returns
-------
run : OpenMLRun
Run corresponding to ID, fetched from the server.
"""
- run_file = os.path.join(config.get_cache_directory(), "runs",
- "run_%d.xml" % run_id)
+ run_dir = Path(openml.utils._create_cache_directory_for_id(RUNS_CACHE_DIR_NAME, run_id))
+ run_file = run_dir / "description.xml"
- try:
- return _get_cached_run(run_id)
- except (OpenMLCacheException):
- try:
- return_code, run_xml = _perform_api_call("run/%d" % run_id)
- except (URLError, UnicodeEncodeError) as e:
- # TODO logger.debug
- print(e)
- raise e
-
- with io.open(run_file, "w", encoding='utf8') as fh:
- fh.write(run_xml)
+ run_dir.mkdir(parents=True, exist_ok=True)
try:
- run = _create_run_from_xml(run_xml)
- except Exception as e:
- # TODO logger.debug
- print("Run ID", run_id)
- raise e
+ if not ignore_cache:
+ return _get_cached_run(run_id)
- with io.open(run_file, "w", encoding='utf8') as fh:
- fh.write(run_xml)
+ raise OpenMLCacheException(message="dummy")
- return run
+ except OpenMLCacheException:
+ run_xml = openml._api_calls._perform_api_call(f"run/{run_id}", "get")
+ with run_file.open("w", encoding="utf8") as fh:
+ fh.write(run_xml)
+ return _create_run_from_xml(run_xml)
-def _create_run_from_xml(xml):
+
+def _create_run_from_xml(xml: str, from_server: bool = True) -> OpenMLRun: # noqa: PLR0915, PLR0912, C901, FBT002
"""Create a run object from xml returned from server.
Parameters
----------
- run_xml : string
+ xml : string
XML describing a run.
+ from_server : bool, optional (default=True)
+ If True, an AttributeError is raised if any of the fields required by the server is not
+ present in the xml. If False, those absent fields will be treated as None.
+
Returns
-------
run : OpenMLRun
New run object representing run_xml.
"""
- run = xmltodict.parse(xml)["oml:run"]
- run_id = int(run['oml:run_id'])
- uploader = int(run['oml:uploader'])
- uploader_name = run['oml:uploader_name']
- task_id = int(run['oml:task_id'])
- task_type = run['oml:task_type']
- task_evaluation_measure = run['oml:task_evaluation_measure']
- flow_id = int(run['oml:flow_id'])
- flow_name = run['oml:flow_name']
- setup_id = int(run['oml:setup_id'])
- setup_string = run['oml:setup_string']
-
- parameters = dict()
- if 'oml:parameter_settings' in run:
- parameter_settings = run['oml:parameter_settings']
- for parameter_dict in parameter_settings:
- key = parameter_dict['oml:name']
- value = parameter_dict['oml:value']
- parameters[key] = value
-
- dataset_id = int(run['oml:input_data']['oml:dataset']['oml:did'])
-
- predictions_url = None
- for file_dict in run['oml:output_data']['oml:file']:
- if file_dict['oml:name'] == 'predictions':
- predictions_url = file_dict['oml:url']
- if predictions_url is None:
- raise ValueError('No URL to download predictions for run %d in run '
- 'description XML' % run_id)
- evaluations = dict()
- detailed_evaluations = defaultdict(lambda: defaultdict(dict))
- evaluation_flows = dict()
- for evaluation_dict in run['oml:output_data']['oml:evaluation']:
- key = evaluation_dict['oml:name']
- flow_id = int(evaluation_dict['oml:flow_id'])
- if 'oml:value' in evaluation_dict:
- value = float(evaluation_dict['oml:value'])
- elif 'oml:array_data' in evaluation_dict:
- value = evaluation_dict['oml:array_data']
- else:
- raise ValueError('Could not find keys "value" or "array_data" '
- 'in %s' % str(evaluation_dict.keys()))
-
- if '@repeat' in evaluation_dict and '@fold' in evaluation_dict:
- repeat = int(evaluation_dict['@repeat'])
- fold = int(evaluation_dict['@fold'])
- repeat_dict = detailed_evaluations[key]
- fold_dict = repeat_dict[repeat]
- fold_dict[fold] = value
- else:
- evaluations[key] = value
- evaluation_flows[key] = flow_id
- evaluation_flows[key] = flow_id
+ def obtain_field(xml_obj, fieldname, from_server, cast=None): # type: ignore
+ # this function can be used to check whether a field is present in an
+ # object. if it is not present, either returns None or throws an error
+ # (this is usually done if the xml comes from the server)
+ if fieldname in xml_obj:
+ if cast is not None:
+ return cast(xml_obj[fieldname])
+ return xml_obj[fieldname]
+
+ if not from_server:
+ return None
+
+ raise AttributeError("Run XML does not contain required (server) field: ", fieldname)
+
+ run = xmltodict.parse(xml, force_list=["oml:file", "oml:evaluation", "oml:parameter_setting"])[
+ "oml:run"
+ ]
+ run_id = obtain_field(run, "oml:run_id", from_server, cast=int)
+ uploader = obtain_field(run, "oml:uploader", from_server, cast=int)
+ uploader_name = obtain_field(run, "oml:uploader_name", from_server)
+ task_id = int(run["oml:task_id"])
+ task_type = obtain_field(run, "oml:task_type", from_server)
+
+ # even with the server requirement this field may be empty.
+ task_evaluation_measure = run.get("oml:task_evaluation_measure", None)
+
+ if not from_server and run["oml:flow_id"] is None:
+ # This can happen for a locally stored run of which the flow is not yet published.
+ flow_id = None
+ parameters = None
+ else:
+ flow_id = obtain_field(run, "oml:flow_id", from_server, cast=int)
+ # parameters are only properly formatted once the flow is established on the server.
+ # thus they are also not stored for runs with local flows.
+ parameters = []
+ if "oml:parameter_setting" in run:
+ obtained_parameter_settings = run["oml:parameter_setting"]
+ for parameter_dict in obtained_parameter_settings:
+ current_parameter = {
+ "oml:name": parameter_dict["oml:name"],
+ "oml:value": parameter_dict["oml:value"],
+ }
+ if "oml:component" in parameter_dict:
+ current_parameter["oml:component"] = parameter_dict["oml:component"]
+ parameters.append(current_parameter)
+
+ flow_name = obtain_field(run, "oml:flow_name", from_server)
+ setup_id = obtain_field(run, "oml:setup_id", from_server, cast=int)
+ setup_string = obtain_field(run, "oml:setup_string", from_server)
+ # run_details is currently not sent by the server, so we need to retrieve it safely.
+ # whenever that's resolved, we can enforce it being present (OpenML#1087)
+ run_details = obtain_field(run, "oml:run_details", from_server=False)
+
+ if "oml:input_data" in run:
+ dataset_id = int(run["oml:input_data"]["oml:dataset"]["oml:did"])
+ elif not from_server:
+ dataset_id = None
+ else:
+ # fetching the task to obtain dataset_id
+ t = openml.tasks.get_task(task_id, download_data=False)
+ if not hasattr(t, "dataset_id"):
+ raise ValueError(
+ f"Unable to fetch dataset_id from the task({task_id}) linked to run({run_id})",
+ )
+ dataset_id = t.dataset_id
+
+ files: dict[str, int] = {}
+ evaluations: dict[str, float | Any] = {}
+ fold_evaluations: dict[str, dict[int, dict[int, float | Any]]] = {}
+ sample_evaluations: dict[str, dict[int, dict[int, dict[int, float | Any]]]] = {}
+ if "oml:output_data" not in run:
+ if from_server:
+ raise ValueError("Run does not contain output_data (OpenML server error?)")
+ predictions_url = None
+ else:
+ output_data = run["oml:output_data"]
+ predictions_url = None
+ if "oml:file" in output_data:
+ # multiple files, the normal case
+ for file_dict in output_data["oml:file"]:
+ files[file_dict["oml:name"]] = int(file_dict["oml:file_id"])
+ if file_dict["oml:name"] == "predictions":
+ predictions_url = file_dict["oml:url"]
+ if "oml:evaluation" in output_data:
+ # in normal cases there should be evaluations, but in case there
+ # was an error these could be absent
+ for evaluation_dict in output_data["oml:evaluation"]:
+ key = evaluation_dict["oml:name"]
+ if "oml:value" in evaluation_dict:
+ value = float(evaluation_dict["oml:value"])
+ elif "oml:array_data" in evaluation_dict:
+ value = evaluation_dict["oml:array_data"]
+ else:
+ raise ValueError(
+ 'Could not find keys "value" or '
+ f'"array_data" in {evaluation_dict.keys()!s}',
+ )
+ if (
+ "@repeat" in evaluation_dict
+ and "@fold" in evaluation_dict
+ and "@sample" in evaluation_dict
+ ):
+ repeat = int(evaluation_dict["@repeat"])
+ fold = int(evaluation_dict["@fold"])
+ sample = int(evaluation_dict["@sample"])
+ if key not in sample_evaluations:
+ sample_evaluations[key] = {}
+ if repeat not in sample_evaluations[key]:
+ sample_evaluations[key][repeat] = {}
+ if fold not in sample_evaluations[key][repeat]:
+ sample_evaluations[key][repeat][fold] = {}
+ sample_evaluations[key][repeat][fold][sample] = value
+ elif "@repeat" in evaluation_dict and "@fold" in evaluation_dict:
+ repeat = int(evaluation_dict["@repeat"])
+ fold = int(evaluation_dict["@fold"])
+ if key not in fold_evaluations:
+ fold_evaluations[key] = {}
+ if repeat not in fold_evaluations[key]:
+ fold_evaluations[key][repeat] = {}
+ fold_evaluations[key][repeat][fold] = value
+ else:
+ evaluations[key] = value
+
+ if "description" not in files and from_server is True:
+ raise ValueError(f"No description file for run {run_id} in run description XML")
+
+ if "predictions" not in files and from_server is True:
+ task = openml.tasks.get_task(task_id)
+ if task.task_type_id == TaskType.SUBGROUP_DISCOVERY:
+ raise NotImplementedError(
+ f"Subgroup discovery tasks are not yet supported. "
+ f"Task ID: {task_id}. Please check the OpenML documentation"
+ f"for supported task types. "
+ f"Currently supported task types: Classification, Regression,"
+ f"Clustering, and Learning Curve."
+ )
+
+ # JvR: actually, I am not sure whether this error should be raised.
+ # a run can consist without predictions. But for now let's keep it
+ # Matthias: yes, it should stay as long as we do not really handle
+ # this stuff
+ raise ValueError(f"No prediction files for run {run_id} in run description XML")
+
+ tags = openml.utils.extract_xml_tags("oml:tag", run)
+
+ return OpenMLRun(
+ run_id=run_id,
+ uploader=uploader,
+ uploader_name=uploader_name,
+ task_id=task_id,
+ task_type=task_type,
+ task_evaluation_measure=task_evaluation_measure,
+ flow_id=flow_id,
+ flow_name=flow_name,
+ setup_id=setup_id,
+ setup_string=setup_string,
+ parameter_settings=parameters,
+ dataset_id=dataset_id,
+ output_files=files,
+ # Make sure default values are used where needed to keep run objects identical
+ evaluations=evaluations or None,
+ fold_evaluations=fold_evaluations or None,
+ sample_evaluations=sample_evaluations or None,
+ tags=tags,
+ predictions_url=predictions_url,
+ run_details=run_details,
+ )
+
+
+def _get_cached_run(run_id: int) -> OpenMLRun:
+ """Load a run from the cache."""
+ run_cache_dir = openml.utils._create_cache_directory_for_id(RUNS_CACHE_DIR_NAME, run_id)
+ run_file = run_cache_dir / "description.xml"
+ try:
+ with run_file.open(encoding="utf8") as fh:
+ return _create_run_from_xml(xml=fh.read())
+ except OSError as e:
+ raise OpenMLCacheException(f"Run file for run id {run_id} not cached") from e
+
+
+def list_runs( # noqa: PLR0913
+ offset: int | None = None,
+ size: int | None = None,
+ id: list | None = None, # noqa: A002
+ task: list[int] | None = None,
+ setup: list | None = None,
+ flow: list | None = None,
+ uploader: list | None = None,
+ tag: str | None = None,
+ study: int | None = None,
+ display_errors: bool = False, # noqa: FBT002
+ task_type: TaskType | int | None = None,
+) -> pd.DataFrame:
+ """
+ List all runs matching all of the given filters.
+ (Supports large amount of results)
- return OpenMLRun(run_id=run_id, uploader=uploader,
- uploader_name=uploader_name, task_id=task_id,
- task_type=task_type,
- task_evaluation_measure=task_evaluation_measure,
- flow_id=flow_id, flow_name=flow_name,
- setup_id=setup_id, setup_string=setup_string,
- parameter_settings=parameters,
- dataset_id=dataset_id, predictions_url=predictions_url,
- evaluations=evaluations,
- detailed_evaluations=detailed_evaluations)
+ Parameters
+ ----------
+ offset : int, optional
+ the number of runs to skip, starting from the first
+ size : int, optional
+ the maximum number of runs to show
+ id : list, optional
-def _get_cached_run(run_id):
- """Load a run from the cache."""
- for cache_dir in [config.get_cache_directory(),
- config.get_private_directory()]:
- run_cache_dir = os.path.join(cache_dir, "runs")
- try:
- run_file = os.path.join(run_cache_dir,
- "run_%d.xml" % int(run_id))
- with io.open(run_file, encoding='utf8') as fh:
- run = _create_task_from_xml(xml=fh.read())
- return run
+ task : list, optional
- except (OSError, IOError):
- continue
+ setup: list, optional
- raise OpenMLCacheException("Run file for run id %d not "
- "cached" % run_id)
+ flow : list, optional
+
+ uploader : list, optional
+
+ tag : str, optional
+ study : int, optional
-def list_runs(offset=None, size=None, id=None, task=None,
- flow=None, uploader=None, tag=None):
- """List all runs matching all of the given filters.
+ display_errors : bool, optional (default=None)
+ Whether to list runs which have an error (for example a missing
+ prediction file).
- Perform API call `/run/list/{filters} `_
+ task_type : str, optional
+
+ Returns
+ -------
+ dataframe
+ """
+ if id is not None and (not isinstance(id, list)):
+ raise TypeError("id must be of type list.")
+ if task is not None and (not isinstance(task, list)):
+ raise TypeError("task must be of type list.")
+ if setup is not None and (not isinstance(setup, list)):
+ raise TypeError("setup must be of type list.")
+ if flow is not None and (not isinstance(flow, list)):
+ raise TypeError("flow must be of type list.")
+ if uploader is not None and (not isinstance(uploader, list)):
+ raise TypeError("uploader must be of type list.")
+
+ listing_call = partial(
+ _list_runs,
+ id=id,
+ task=task,
+ setup=setup,
+ flow=flow,
+ uploader=uploader,
+ tag=tag,
+ study=study,
+ display_errors=display_errors,
+ task_type=task_type,
+ )
+ batches = openml.utils._list_all(listing_call, offset=offset, limit=size)
+ if len(batches) == 0:
+ return pd.DataFrame()
+
+ return pd.concat(batches)
+
+
+def _list_runs( # noqa: PLR0913, C901
+ limit: int,
+ offset: int,
+ *,
+ id: list | None = None, # noqa: A002
+ task: list | None = None,
+ setup: list | None = None,
+ flow: list | None = None,
+ uploader: list | None = None,
+ study: int | None = None,
+ tag: str | None = None,
+ display_errors: bool = False,
+ task_type: TaskType | int | None = None,
+) -> pd.DataFrame:
+ """
+ Perform API call `/run/list/{filters}'
+ `
Parameters
----------
- offset : int, optional
- the number of runs to skip, starting from the first
- size : int, optional
- the maximum number of runs to show
+ The arguments that are lists are separated from the single value
+ ones which are put into the kwargs.
+ display_errors is also separated from the kwargs since it has a
+ default value.
id : list, optional
task : list, optional
+ setup: list, optional
+
flow : list, optional
+ tag: str, optional
+
uploader : list, optional
- tag : str, optional
+ study : int, optional
+
+ display_errors : bool, optional (default=None)
+ Whether to list runs which have an error (for example a missing
+ prediction file).
+
+ task_type : str, optional
Returns
-------
- list
+ dict, or dataframe
List of found runs.
"""
-
api_call = "run/list"
+ if limit is not None:
+ api_call += f"/limit/{limit}"
if offset is not None:
- api_call += "/offset/%d" % int(offset)
- if size is not None:
- api_call += "/limit/%d" % int(size)
+ api_call += f"/offset/{offset}"
if id is not None:
- api_call += "/run/%s" % ','.join([str(int(i)) for i in id])
+ api_call += f"/run/{','.join([str(int(i)) for i in id])}"
if task is not None:
- api_call += "/task/%s" % ','.join([str(int(i)) for i in task])
+ api_call += f"/task/{','.join([str(int(i)) for i in task])}"
+ if setup is not None:
+ api_call += f"/setup/{','.join([str(int(i)) for i in setup])}"
if flow is not None:
- api_call += "/flow/%s" % ','.join([str(int(i)) for i in flow])
+ api_call += f"/flow/{','.join([str(int(i)) for i in flow])}"
if uploader is not None:
- api_call += "/uploader/%s" % ','.join([str(int(i)) for i in uploader])
+ api_call += f"/uploader/{','.join([str(int(i)) for i in uploader])}"
+ if study is not None:
+ api_call += f"/study/{study}"
+ if display_errors:
+ api_call += "/show_errors/true"
if tag is not None:
- api_call += "/tag/%s" % tag
+ api_call += f"/tag/{tag}"
+ if task_type is not None:
+ tvalue = task_type.value if isinstance(task_type, TaskType) else task_type
+ api_call += f"/task_type/{tvalue}"
+ return __list_runs(api_call=api_call)
- return _list_runs(api_call)
-
-def _list_runs(api_call):
+def __list_runs(api_call: str) -> pd.DataFrame:
"""Helper function to parse API calls which are lists of runs"""
+ xml_string = openml._api_calls._perform_api_call(api_call, "get")
+ runs_dict = xmltodict.parse(xml_string, force_list=("oml:run",))
+ # Minimalistic check if the XML is useful
+ if "oml:runs" not in runs_dict:
+ raise ValueError(f'Error in return XML, does not contain "oml:runs": {runs_dict}')
+
+ if "@xmlns:oml" not in runs_dict["oml:runs"]:
+ raise ValueError(
+ f'Error in return XML, does not contain "oml:runs"/@xmlns:oml: {runs_dict}'
+ )
+
+ if runs_dict["oml:runs"]["@xmlns:oml"] != "http://openml.org/openml":
+ raise ValueError(
+ "Error in return XML, value of "
+ '"oml:runs"/@xmlns:oml is not '
+ f'"http://openml.org/openml": {runs_dict}',
+ )
+
+ assert isinstance(runs_dict["oml:runs"]["oml:run"], list), type(runs_dict["oml:runs"])
+
+ runs = {
+ int(r["oml:run_id"]): {
+ "run_id": int(r["oml:run_id"]),
+ "task_id": int(r["oml:task_id"]),
+ "setup_id": int(r["oml:setup_id"]),
+ "flow_id": int(r["oml:flow_id"]),
+ "uploader": int(r["oml:uploader"]),
+ "task_type": TaskType(int(r["oml:task_type_id"])),
+ "upload_time": str(r["oml:upload_time"]),
+ "error_message": str((r["oml:error_message"]) or ""),
+ }
+ for r in runs_dict["oml:runs"]["oml:run"]
+ }
+ return pd.DataFrame.from_dict(runs, orient="index")
+
+
+def format_prediction( # noqa: PLR0913
+ task: OpenMLSupervisedTask,
+ repeat: int,
+ fold: int,
+ index: int,
+ prediction: str | int | float,
+ truth: str | int | float,
+ sample: int | None = None,
+ proba: dict[str, float] | None = None,
+) -> list[str | int | float]:
+ """Format the predictions in the specific order as required for the run results.
+
+ Parameters
+ ----------
+ task: OpenMLSupervisedTask
+ Task for which to format the predictions.
+ repeat: int
+ From which repeat this predictions is made.
+ fold: int
+ From which fold this prediction is made.
+ index: int
+ For which index this prediction is made.
+ prediction: str, int or float
+ The predicted class label or value.
+ truth: str, int or float
+ The true class label or value.
+ sample: int, optional (default=None)
+ From which sample set this prediction is made.
+ Required only for LearningCurve tasks.
+ proba: Dict[str, float], optional (default=None)
+ For classification tasks only.
+ A mapping from each class label to their predicted probability.
+ The dictionary should contain an entry for each of the `task.class_labels`.
+ E.g.: {"Iris-Setosa": 0.2, "Iris-Versicolor": 0.7, "Iris-Virginica": 0.1}
- return_code, xml_string = _perform_api_call(api_call)
+ Returns
+ -------
+ A list with elements for the prediction results of a run.
- runs_dict = xmltodict.parse(xml_string)
- # Minimalistic check if the XML is useful
- if 'oml:runs' not in runs_dict:
- raise ValueError('Error in return XML, does not contain "oml:runs": %s'
- % str(runs_dict))
- elif '@xmlns:oml' not in runs_dict['oml:runs']:
- raise ValueError('Error in return XML, does not contain '
- '"oml:runs"/@xmlns:oml: %s'
- % str(runs_dict))
- elif runs_dict['oml:runs']['@xmlns:oml'] != 'http://openml.org/openml':
- raise ValueError('Error in return XML, value of '
- '"oml:runs"/@xmlns:oml is not '
- '"http://openml.org/openml": %s'
- % str(runs_dict))
-
- if isinstance(runs_dict['oml:runs']['oml:run'], list):
- runs_list = runs_dict['oml:runs']['oml:run']
- elif isinstance(runs_dict['oml:runs']['oml:run'], dict):
- runs_list = [runs_dict['oml:runs']['oml:run']]
- else:
- raise TypeError()
+ The returned order of the elements is (if available):
+ [repeat, fold, sample, index, prediction, truth, *probabilities]
- runs = dict()
- for run_ in runs_list:
- run_id = int(run_['oml:run_id'])
- run = {'run_id': run_id,
- 'task_id': int(run_['oml:task_id']),
- 'setup_id': int(run_['oml:setup_id']),
- 'flow_id': int(run_['oml:flow_id']),
- 'uploader': int(run_['oml:uploader'])}
+ This order follows the R Client API.
+ """
+ if isinstance(task, OpenMLClassificationTask):
+ if proba is None:
+ raise ValueError("`proba` is required for classification task")
+ if task.class_labels is None:
+ raise ValueError("The classification task must have class labels set")
+ if not set(task.class_labels) == set(proba):
+ raise ValueError("Each class should have a predicted probability")
+ if sample is None:
+ if isinstance(task, OpenMLLearningCurveTask):
+ raise ValueError("`sample` can not be none for LearningCurveTask")
- runs[run_id] = run
+ sample = 0
+ probabilities = [proba[c] for c in task.class_labels]
+ return [repeat, fold, sample, index, prediction, truth, *probabilities]
- return runs
+ if isinstance(task, OpenMLRegressionTask):
+ return [repeat, fold, index, prediction, truth]
+
+ raise NotImplementedError(
+ f"Formatting for {type(task)} is not supported."
+ f"Supported task types: OpenMLClassificationTask, OpenMLRegressionTask,"
+ f"and OpenMLLearningCurveTask. "
+ f"Please ensure your task is one of these types."
+ )
+
+
+def delete_run(run_id: int) -> bool:
+ """Delete run with id `run_id` from the OpenML server.
+
+ You can only delete runs which you uploaded.
+
+ Parameters
+ ----------
+ run_id : int
+ OpenML id of the run
+
+ Returns
+ -------
+ bool
+ True if the deletion was successful. False otherwise.
+ """
+ return openml.utils._delete_entity("run", run_id)
diff --git a/openml/runs/run.py b/openml/runs/run.py
index e0902d1df..eff011408 100644
--- a/openml/runs/run.py
+++ b/openml/runs/run.py
@@ -1,29 +1,127 @@
-from collections import OrderedDict
-import sys
+# License: BSD 3-Clause
+from __future__ import annotations
+
+import pickle
import time
+from collections import OrderedDict
+from collections.abc import Callable, Sequence
+from pathlib import Path
+from typing import (
+ TYPE_CHECKING,
+ Any,
+)
import arff
-import xmltodict
+import numpy as np
+import pandas as pd
-from ..tasks import get_task
-from .._api_calls import _perform_api_call
+import openml
+import openml._api_calls
+from openml.base import OpenMLBase
+from openml.exceptions import PyOpenMLError
+from openml.flows import OpenMLFlow, get_flow
+from openml.tasks import (
+ OpenMLClassificationTask,
+ OpenMLClusteringTask,
+ OpenMLLearningCurveTask,
+ OpenMLRegressionTask,
+ OpenMLTask,
+ TaskType,
+ get_task,
+)
+if TYPE_CHECKING:
+ from openml.runs.trace import OpenMLRunTrace
-class OpenMLRun(object):
- """OpenML Run: result of running a model on an openml dataset.
+
+class OpenMLRun(OpenMLBase):
+ """OpenML Run: result of running a model on an OpenML dataset.
Parameters
----------
- FIXME
-
+ task_id: int
+ The ID of the OpenML task associated with the run.
+ flow_id: int
+ The ID of the OpenML flow associated with the run.
+ dataset_id: int
+ The ID of the OpenML dataset used for the run.
+ setup_string: str
+ The setup string of the run.
+ output_files: Dict[str, int]
+ Specifies where each related file can be found.
+ setup_id: int
+ An integer representing the ID of the setup used for the run.
+ tags: List[str]
+ Representing the tags associated with the run.
+ uploader: int
+ User ID of the uploader.
+ uploader_name: str
+ The name of the person who uploaded the run.
+ evaluations: Dict
+ Representing the evaluations of the run.
+ fold_evaluations: Dict
+ The evaluations of the run for each fold.
+ sample_evaluations: Dict
+ The evaluations of the run for each sample.
+ data_content: List[List]
+ The predictions generated from executing this run.
+ trace: OpenMLRunTrace
+ The trace containing information on internal model evaluations of this run.
+ model: object
+ The untrained model that was evaluated in the run.
+ task_type: str
+ The type of the OpenML task associated with the run.
+ task_evaluation_measure: str
+ The evaluation measure used for the task.
+ flow_name: str
+ The name of the OpenML flow associated with the run.
+ parameter_settings: list[OrderedDict]
+ Representing the parameter settings used for the run.
+ predictions_url: str
+ The URL of the predictions file.
+ task: OpenMLTask
+ An instance of the OpenMLTask class, representing the OpenML task associated
+ with the run.
+ flow: OpenMLFlow
+ An instance of the OpenMLFlow class, representing the OpenML flow associated
+ with the run.
+ run_id: int
+ The ID of the run.
+ description_text: str, optional
+ Description text to add to the predictions file. If left None, is set to the
+ time the arff file is generated.
+ run_details: str, optional (default=None)
+ Description of the run stored in the run meta-data.
"""
- def __init__(self, task_id, flow_id, dataset_id, setup_string=None,
- files=None, setup_id=None, tags=None, uploader=None, uploader_name=None,
- evaluations=None, detailed_evaluations=None,
- data_content=None, model=None, task_type=None,
- task_evaluation_measure=None, flow_name=None,
- parameter_settings=None, predictions_url=None, task=None,
- flow=None, run_id=None):
+
+ def __init__( # noqa: PLR0913
+ self,
+ task_id: int,
+ flow_id: int | None,
+ dataset_id: int | None,
+ setup_string: str | None = None,
+ output_files: dict[str, int] | None = None,
+ setup_id: int | None = None,
+ tags: list[str] | None = None,
+ uploader: int | None = None,
+ uploader_name: str | None = None,
+ evaluations: dict | None = None,
+ fold_evaluations: dict | None = None,
+ sample_evaluations: dict | None = None,
+ data_content: list[list] | None = None,
+ trace: OpenMLRunTrace | None = None,
+ model: object | None = None,
+ task_type: str | None = None,
+ task_evaluation_measure: str | None = None,
+ flow_name: str | None = None,
+ parameter_settings: list[dict[str, Any]] | None = None,
+ predictions_url: str | None = None,
+ task: OpenMLTask | None = None,
+ flow: OpenMLFlow | None = None,
+ run_id: int | None = None,
+ description_text: str | None = None,
+ run_details: str | None = None,
+ ):
self.uploader = uploader
self.uploader_name = uploader_name
self.task_id = task_id
@@ -35,19 +133,270 @@ def __init__(self, task_id, flow_id, dataset_id, setup_string=None,
self.setup_string = setup_string
self.parameter_settings = parameter_settings
self.dataset_id = dataset_id
- self.predictions_url = predictions_url
self.evaluations = evaluations
- self.detailed_evaluations = detailed_evaluations
+ self.fold_evaluations = fold_evaluations
+ self.sample_evaluations = sample_evaluations
self.data_content = data_content
+ self.output_files = output_files
+ self.trace = trace
+ self.error_message = None
self.task = task
self.flow = flow
self.run_id = run_id
+ self.model = model
+ self.tags = tags
+ self.predictions_url = predictions_url
+ self.description_text = description_text
+ self.run_details = run_details
+ self._predictions = None
+
+ @property
+ def predictions(self) -> pd.DataFrame:
+ """Return a DataFrame with predictions for this run"""
+ if self._predictions is None:
+ if self.data_content:
+ arff_dict = self._generate_arff_dict()
+ elif self.predictions_url:
+ arff_text = openml._api_calls._download_text_file(self.predictions_url)
+ arff_dict = arff.loads(arff_text)
+ else:
+ raise RuntimeError("Run has no predictions.")
+ self._predictions = pd.DataFrame(
+ arff_dict["data"],
+ columns=[name for name, _ in arff_dict["attributes"]],
+ )
+ return self._predictions
+
+ @property
+ def id(self) -> int | None:
+ """The ID of the run, None if not uploaded to the server yet."""
+ return self.run_id
+
+ def _evaluation_summary(self, metric: str) -> str:
+ """Summarizes the evaluation of a metric over all folds.
+
+ The fold scores for the metric must exist already. During run creation,
+ by default, the MAE for OpenMLRegressionTask and the accuracy for
+ OpenMLClassificationTask/OpenMLLearningCurveTasktasks are computed.
+
+ If repetition exist, we take the mean over all repetitions.
+
+ Parameters
+ ----------
+ metric: str
+ Name of an evaluation metric that was used to compute fold scores.
+
+ Returns
+ -------
+ metric_summary: str
+ A formatted string that displays the metric's evaluation summary.
+ The summary consists of the mean and std.
+ """
+ if self.fold_evaluations is None:
+ raise ValueError("No fold evaluations available.")
+ fold_score_lists = self.fold_evaluations[metric].values()
+
+ # Get the mean and std over all repetitions
+ rep_means = [np.mean(list(x.values())) for x in fold_score_lists]
+ rep_stds = [np.std(list(x.values())) for x in fold_score_lists]
+
+ return f"{np.mean(rep_means):.4f} +- {np.mean(rep_stds):.4f}"
+
+ def _get_repr_body_fields(self) -> Sequence[tuple[str, str | int | list[str]]]:
+ """Collect all information to display in the __repr__ body."""
+ # Set up fields
+ fields = {
+ "Uploader Name": self.uploader_name,
+ "Metric": self.task_evaluation_measure,
+ "Run ID": self.run_id,
+ "Task ID": self.task_id,
+ "Task Type": self.task_type,
+ "Task URL": openml.tasks.OpenMLTask.url_for_id(self.task_id),
+ "Flow ID": self.flow_id,
+ "Flow Name": self.flow_name,
+ "Flow URL": (
+ openml.flows.OpenMLFlow.url_for_id(self.flow_id)
+ if self.flow_id is not None
+ else None
+ ),
+ "Setup ID": self.setup_id,
+ "Setup String": self.setup_string,
+ "Dataset ID": self.dataset_id,
+ "Dataset URL": (
+ openml.datasets.OpenMLDataset.url_for_id(self.dataset_id)
+ if self.dataset_id is not None
+ else None
+ ),
+ }
- def _generate_arff_dict(self):
- """Generates the arff dictionary for upload to the server.
+ # determines the order of the initial fields in which the information will be printed
+ order = ["Uploader Name", "Uploader Profile", "Metric", "Result"]
+
+ if self.uploader is not None:
+ fields["Uploader Profile"] = f"{openml.config.get_server_base_url()}/u/{self.uploader}"
+ if self.run_id is not None:
+ fields["Run URL"] = self.openml_url
+ if self.evaluations is not None and self.task_evaluation_measure in self.evaluations:
+ fields["Result"] = self.evaluations[self.task_evaluation_measure]
+ elif self.fold_evaluations is not None:
+ # -- Add locally computed summary values if possible
+ if "predictive_accuracy" in self.fold_evaluations:
+ # OpenMLClassificationTask; OpenMLLearningCurveTask
+ result_field = "Local Result - Accuracy (+- STD)"
+ fields[result_field] = self._evaluation_summary("predictive_accuracy")
+ order.append(result_field)
+ elif "mean_absolute_error" in self.fold_evaluations:
+ # OpenMLRegressionTask
+ result_field = "Local Result - MAE (+- STD)"
+ fields[result_field] = self._evaluation_summary("mean_absolute_error")
+ order.append(result_field)
+
+ if "usercpu_time_millis" in self.fold_evaluations:
+ # Runtime should be available for most tasks types
+ rt_field = "Local Runtime - ms (+- STD)"
+ fields[rt_field] = self._evaluation_summary("usercpu_time_millis")
+ order.append(rt_field)
+
+ # determines the remaining order
+ order += [
+ "Run ID",
+ "Run URL",
+ "Task ID",
+ "Task Type",
+ "Task URL",
+ "Flow ID",
+ "Flow Name",
+ "Flow URL",
+ "Setup ID",
+ "Setup String",
+ "Dataset ID",
+ "Dataset URL",
+ ]
+ return [
+ (key, "None" if fields[key] is None else fields[key]) # type: ignore
+ for key in order
+ if key in fields
+ ]
+
+ @classmethod
+ def from_filesystem(cls, directory: str | Path, expect_model: bool = True) -> OpenMLRun: # noqa: FBT002
+ """
+ The inverse of the to_filesystem method. Instantiates an OpenMLRun
+ object based on files stored on the file system.
+
+ Parameters
+ ----------
+ directory : str
+ a path leading to the folder where the results
+ are stored
+
+ expect_model : bool
+ if True, it requires the model pickle to be present, and an error
+ will be thrown if not. Otherwise, the model might or might not
+ be present.
+
+ Returns
+ -------
+ run : OpenMLRun
+ the re-instantiated run object
+ """
+ # Avoiding cyclic imports
+ import openml.runs.functions
+
+ directory = Path(directory)
+ if not directory.is_dir():
+ raise ValueError("Could not find folder")
+
+ description_path = directory / "description.xml"
+ predictions_path = directory / "predictions.arff"
+ trace_path = directory / "trace.arff"
+ model_path = directory / "model.pkl"
+
+ if not description_path.is_file():
+ raise ValueError("Could not find description.xml")
+ if not predictions_path.is_file():
+ raise ValueError("Could not find predictions.arff")
+ if (not model_path.is_file()) and expect_model:
+ raise ValueError("Could not find model.pkl")
+
+ with description_path.open() as fht:
+ xml_string = fht.read()
+ run = openml.runs.functions._create_run_from_xml(xml_string, from_server=False)
+
+ if run.flow_id is None:
+ flow = openml.flows.OpenMLFlow.from_filesystem(directory)
+ run.flow = flow
+ run.flow_name = flow.name
+
+ with predictions_path.open() as fht:
+ predictions = arff.load(fht)
+ run.data_content = predictions["data"]
+
+ if model_path.is_file():
+ # note that it will load the model if the file exists, even if
+ # expect_model is False
+ with model_path.open("rb") as fhb:
+ run.model = pickle.load(fhb) # noqa: S301
+
+ if trace_path.is_file():
+ run.trace = openml.runs.OpenMLRunTrace._from_filesystem(trace_path)
+
+ return run
+
+ def to_filesystem(
+ self,
+ directory: str | Path,
+ store_model: bool = True, # noqa: FBT002
+ ) -> None:
+ """
+ The inverse of the from_filesystem method. Serializes a run
+ on the filesystem, to be uploaded later.
+
+ Parameters
+ ----------
+ directory : str
+ a path leading to the folder where the results
+ will be stored. Should be empty
+
+ store_model : bool, optional (default=True)
+ if True, a model will be pickled as well. As this is the most
+ storage expensive part, it is often desirable to not store the
+ model.
+ """
+ if self.data_content is None or self.model is None:
+ raise ValueError("Run should have been executed (and contain model / predictions)")
+ directory = Path(directory)
+ directory.mkdir(exist_ok=True, parents=True)
+
+ if any(directory.iterdir()):
+ raise ValueError(f"Output directory {directory.expanduser().resolve()} should be empty")
+
+ run_xml = self._to_xml()
+ predictions_arff = arff.dumps(self._generate_arff_dict())
+
+ # It seems like typing does not allow to define the same variable multiple times
+ with (directory / "description.xml").open("w") as fh:
+ fh.write(run_xml)
+ with (directory / "predictions.arff").open("w") as fh:
+ fh.write(predictions_arff)
+ if store_model:
+ with (directory / "model.pkl").open("wb") as fh_b:
+ pickle.dump(self.model, fh_b)
+
+ if self.flow_id is None and self.flow is not None:
+ self.flow.to_filesystem(directory)
+
+ if self.trace is not None:
+ self.trace._to_filesystem(directory)
+
+ def _generate_arff_dict(self) -> OrderedDict[str, Any]:
+ """Generates the arff dictionary for uploading predictions to the
+ server.
Assumes that the run has been executed.
+ The order of the attributes follows the order defined by the Client API for R.
+
Returns
-------
arf_dict : dict
@@ -55,139 +404,310 @@ def _generate_arff_dict(self):
Contains predictions and information about the run environment.
"""
if self.data_content is None:
- raise ValueError('Run has not been executed.')
+ raise ValueError("Run has not been executed.")
+ if self.flow is None:
+ assert self.flow_id is not None, "Run has no associated flow id!"
+ self.flow = get_flow(self.flow_id)
- run_environment = (_get_version_information() +
- [time.strftime("%c")] + ['Created by run_task()'])
+ if self.description_text is None:
+ self.description_text = time.strftime("%c")
task = get_task(self.task_id)
- class_labels = task.class_labels
-
- arff_dict = {}
- arff_dict['attributes'] = [('repeat', 'NUMERIC'), # lowercase 'numeric' gives an error
- ('fold', 'NUMERIC'),
- ('row_id', 'NUMERIC')] + \
- [('confidence.' + class_labels[i], 'NUMERIC') for i in range(len(class_labels))] +\
- [('prediction', class_labels),
- ('correct', class_labels)]
- arff_dict['data'] = self.data_content
- arff_dict['description'] = "\n".join(run_environment)
- arff_dict['relation'] = 'openml_task_' + str(task.task_id) + '_predictions'
+
+ arff_dict = OrderedDict() # type: 'OrderedDict[str, Any]'
+ arff_dict["data"] = self.data_content
+ arff_dict["description"] = self.description_text
+ arff_dict["relation"] = f"openml_task_{task.task_id}_predictions"
+
+ if isinstance(task, OpenMLLearningCurveTask):
+ class_labels = task.class_labels
+ instance_specifications = [
+ ("repeat", "NUMERIC"),
+ ("fold", "NUMERIC"),
+ ("sample", "NUMERIC"),
+ ("row_id", "NUMERIC"),
+ ]
+
+ arff_dict["attributes"] = instance_specifications
+ if class_labels is not None:
+ arff_dict["attributes"] = (
+ arff_dict["attributes"]
+ + [("prediction", class_labels), ("correct", class_labels)]
+ + [
+ ("confidence." + class_labels[i], "NUMERIC")
+ for i in range(len(class_labels))
+ ]
+ )
+ else:
+ raise ValueError("The task has no class labels")
+
+ elif isinstance(task, OpenMLClassificationTask):
+ class_labels = task.class_labels
+ instance_specifications = [
+ ("repeat", "NUMERIC"),
+ ("fold", "NUMERIC"),
+ ("sample", "NUMERIC"), # Legacy
+ ("row_id", "NUMERIC"),
+ ]
+
+ arff_dict["attributes"] = instance_specifications
+ if class_labels is not None:
+ prediction_confidences = [
+ ("confidence." + class_labels[i], "NUMERIC") for i in range(len(class_labels))
+ ]
+ prediction_and_true = [("prediction", class_labels), ("correct", class_labels)]
+ arff_dict["attributes"] = (
+ arff_dict["attributes"] + prediction_and_true + prediction_confidences
+ )
+ else:
+ raise ValueError("The task has no class labels")
+
+ elif isinstance(task, OpenMLRegressionTask):
+ arff_dict["attributes"] = [
+ ("repeat", "NUMERIC"),
+ ("fold", "NUMERIC"),
+ ("row_id", "NUMERIC"),
+ ("prediction", "NUMERIC"),
+ ("truth", "NUMERIC"),
+ ]
+
+ elif isinstance(task, OpenMLClusteringTask):
+ arff_dict["attributes"] = [
+ ("repeat", "NUMERIC"),
+ ("fold", "NUMERIC"),
+ ("row_id", "NUMERIC"),
+ ("cluster", "NUMERIC"),
+ ]
+
+ else:
+ raise NotImplementedError(
+ f"Task type '{task.task_type}' is not yet supported. "
+ f"Supported task types: Classification, Regression, Clustering, Learning Curve. "
+ f"Task ID: {task.task_id}. "
+ f"Please check the OpenML documentation for supported task types."
+ )
+
return arff_dict
- def publish(self):
- """Publish a run to the OpenML server.
+ def get_metric_fn(self, sklearn_fn: Callable, kwargs: dict | None = None) -> np.ndarray: # noqa: PLR0915, PLR0912, C901
+ """Calculates metric scores based on predicted values. Assumes the
+ run has been executed locally (and contains run_data). Furthermore,
+ it assumes that the 'correct' or 'truth' attribute is specified in
+ the arff (which is an optional field, but always the case for
+ openml-python runs)
- Uploads the results of a run to OpenML.
- Sets the run_id on self
+ Parameters
+ ----------
+ sklearn_fn : function
+ a function pointer to a sklearn function that
+ accepts ``y_true``, ``y_pred`` and ``**kwargs``
+ kwargs : dict
+ kwargs for the function
Returns
-------
- self : OpenMLRun
+ scores : ndarray of scores of length num_folds * num_repeats
+ metric results
"""
- predictions = arff.dumps(self._generate_arff_dict())
- description_xml = self._create_description_xml()
- file_elements = {'predictions': ("predictions.csv", predictions),
- 'description': ("description.xml", description_xml)}
- return_code, return_value = _perform_api_call(
- "/run/", file_elements=file_elements)
- run_id = int(xmltodict.parse(return_value)['oml:upload_run']['oml:run_id'])
- self.run_id = run_id
- return self
+ kwargs = kwargs if kwargs else {}
+ if self.data_content is not None and self.task_id is not None:
+ predictions_arff = self._generate_arff_dict()
+ elif (self.output_files is not None) and ("predictions" in self.output_files):
+ predictions_file_url = openml._api_calls._file_id_to_url(
+ self.output_files["predictions"],
+ "predictions.arff",
+ )
+ response = openml._api_calls._download_text_file(predictions_file_url)
+ predictions_arff = arff.loads(response)
+ # TODO: make this a stream reader
+ else:
+ raise ValueError(
+ "Run should have been locally executed or contain outputfile reference.",
+ )
- def _create_description_xml(self):
- """Create xml representation of run for upload.
+ # Need to know more about the task to compute scores correctly
+ task = get_task(self.task_id)
- Returns
- -------
- xml_string : string
- XML description of run.
+ attribute_names = [att[0] for att in predictions_arff["attributes"]]
+ if (
+ task.task_type_id in [TaskType.SUPERVISED_CLASSIFICATION, TaskType.LEARNING_CURVE]
+ and "correct" not in attribute_names
+ ):
+ raise ValueError('Attribute "correct" should be set for classification task runs')
+ if task.task_type_id == TaskType.SUPERVISED_REGRESSION and "truth" not in attribute_names:
+ raise ValueError('Attribute "truth" should be set for regression task runs')
+ if task.task_type_id != TaskType.CLUSTERING and "prediction" not in attribute_names:
+ raise ValueError('Attribute "prediction" should be set for supervised task runs')
+
+ def _attribute_list_to_dict(attribute_list): # type: ignore
+ # convenience function: Creates a mapping to map from the name of
+ # attributes present in the arff prediction file to their index.
+ # This is necessary because the number of classes can be different
+ # for different tasks.
+ res = OrderedDict()
+ for idx in range(len(attribute_list)):
+ res[attribute_list[idx][0]] = idx
+ return res
+
+ attribute_dict = _attribute_list_to_dict(predictions_arff["attributes"])
+
+ repeat_idx = attribute_dict["repeat"]
+ fold_idx = attribute_dict["fold"]
+ predicted_idx = attribute_dict["prediction"] # Assume supervised task
+
+ if task.task_type_id in (TaskType.SUPERVISED_CLASSIFICATION, TaskType.LEARNING_CURVE):
+ correct_idx = attribute_dict["correct"]
+ elif task.task_type_id == TaskType.SUPERVISED_REGRESSION:
+ correct_idx = attribute_dict["truth"]
+ has_samples = False
+ if "sample" in attribute_dict:
+ sample_idx = attribute_dict["sample"]
+ has_samples = True
+
+ if (
+ predictions_arff["attributes"][predicted_idx][1]
+ != predictions_arff["attributes"][correct_idx][1]
+ ):
+ pred = predictions_arff["attributes"][predicted_idx][1]
+ corr = predictions_arff["attributes"][correct_idx][1]
+ raise ValueError(
+ f"Predicted and Correct do not have equal values: {pred!s} Vs. {corr!s}",
+ )
+
+ # TODO: these could be cached
+ values_predict: dict[int, dict[int, dict[int, list[float]]]] = {}
+ values_correct: dict[int, dict[int, dict[int, list[float]]]] = {}
+ for _line_idx, line in enumerate(predictions_arff["data"]):
+ rep = line[repeat_idx]
+ fold = line[fold_idx]
+ samp = line[sample_idx] if has_samples else 0
+
+ if task.task_type_id in [
+ TaskType.SUPERVISED_CLASSIFICATION,
+ TaskType.LEARNING_CURVE,
+ ]:
+ prediction = predictions_arff["attributes"][predicted_idx][1].index(
+ line[predicted_idx],
+ )
+ correct = predictions_arff["attributes"][predicted_idx][1].index(line[correct_idx])
+ elif task.task_type_id == TaskType.SUPERVISED_REGRESSION:
+ prediction = line[predicted_idx]
+ correct = line[correct_idx]
+ if rep not in values_predict:
+ values_predict[rep] = OrderedDict()
+ values_correct[rep] = OrderedDict()
+ if fold not in values_predict[rep]:
+ values_predict[rep][fold] = OrderedDict()
+ values_correct[rep][fold] = OrderedDict()
+ if samp not in values_predict[rep][fold]:
+ values_predict[rep][fold][samp] = []
+ values_correct[rep][fold][samp] = []
+
+ values_predict[rep][fold][samp].append(prediction)
+ values_correct[rep][fold][samp].append(correct)
+
+ scores = []
+ for rep in values_predict: # noqa: PLC0206
+ for fold in values_predict[rep]:
+ last_sample = len(values_predict[rep][fold]) - 1
+ y_pred = values_predict[rep][fold][last_sample]
+ y_true = values_correct[rep][fold][last_sample]
+ scores.append(sklearn_fn(y_true, y_pred, **kwargs))
+ return np.array(scores)
+
+ def _parse_publish_response(self, xml_response: dict) -> None:
+ """Parse the id from the xml_response and assign it to self."""
+ self.run_id = int(xml_response["oml:upload_run"]["oml:run_id"])
+
+ def _get_file_elements(self) -> dict:
+ """Get file_elements to upload to the server.
+
+ Derived child classes should overwrite this method as necessary.
+ The description field will be populated automatically if not provided.
"""
- run_environment = _get_version_information()
-
- parameter_settings = self.model.get_params()
- # as a tag, it must be of the form ([a-zA-Z0-9_\-\.])+
- # so we format time from 'mm/dd/yy hh:mm:ss' to 'mm-dd-yy_hh.mm.ss'
- well_formatted_time = time.strftime("%c").replace(
- ' ', '_').replace('/', '-').replace(':', '.')
- tags = run_environment + [well_formatted_time] + ['run_task'] + \
- [self.model.__module__ + "." + self.model.__class__.__name__]
- description = _to_dict(taskid=self.task_id, flow_id=self.flow_id,
- setup_string=_create_setup_string(self.model),
- parameter_settings=parameter_settings,
- tags=tags)
- description_xml = xmltodict.unparse(description, pretty=True)
- return description_xml
-
-################################################################################
-# Functions which cannot be in runs/functions due to circular imports
-
-
-# This can possibly be done by a package such as pyxb, but I could not get
-# it to work properly.
-def _get_version_information():
- """Gets versions of python, sklearn, numpy and scipy, returns them in an array,
-
- Returns
- -------
- result : an array with version information of the above packages
- """
- import sklearn
- import scipy
- import numpy
+ if self.parameter_settings is None and self.model is None:
+ raise PyOpenMLError(
+ "OpenMLRun must contain a model or be initialized with parameter_settings.",
+ )
+ if self.flow_id is None:
+ if self.flow is None:
+ raise PyOpenMLError(
+ "OpenMLRun object does not contain a flow id or reference to OpenMLFlow "
+ "(these should have been added while executing the task). ",
+ )
- major, minor, micro, _, _ = sys.version_info
- python_version = 'Python_{}.'.format(
- ".".join([str(major), str(minor), str(micro)]))
- sklearn_version = 'Sklearn_{}.'.format(sklearn.__version__)
- numpy_version = 'NumPy_{}.'.format(numpy.__version__)
- scipy_version = 'SciPy_{}.'.format(scipy.__version__)
+ # publish the linked Flow before publishing the run.
+ self.flow.publish()
+ self.flow_id = self.flow.flow_id
- return [python_version, sklearn_version, numpy_version, scipy_version]
+ if self.parameter_settings is None:
+ if self.flow is None:
+ assert self.flow_id is not None # for mypy
+ self.flow = openml.flows.get_flow(self.flow_id)
+ self.parameter_settings = self.flow.extension.obtain_parameter_values(
+ self.flow,
+ self.model,
+ )
+ file_elements = {"description": ("description.xml", self._to_xml())}
-def _to_dict(taskid, flow_id, setup_string, parameter_settings, tags):
- """ Creates a dictionary corresponding to the desired xml desired by openML
+ if self.error_message is None:
+ predictions = arff.dumps(self._generate_arff_dict())
+ file_elements["predictions"] = ("predictions.arff", predictions)
- Parameters
- ----------
- taskid : int
- the identifier of the task
- setup_string : string
- a CLI string which can invoke the learning with the correct parameter settings
- parameter_settings : array of dicts
- each dict containing keys name, value and component, one per parameter setting
- tags : array of strings
- information that give a description of the run, must conform to
- regex ``([a-zA-Z0-9_\-\.])+``
-
- Returns
- -------
- result : an array with version information of the above packages
- """
- description = OrderedDict()
- description['oml:run'] = OrderedDict()
- description['oml:run']['@xmlns:oml'] = 'http://openml.org/openml'
- description['oml:run']['oml:task_id'] = taskid
- description['oml:run']['oml:flow_id'] = flow_id
-
- params = []
- for k, v in parameter_settings.items():
- param_dict = OrderedDict()
- param_dict['oml:name'] = k
- param_dict['oml:value'] = ('None' if v is None else v)
- params.append(param_dict)
-
- description['oml:run']['oml:parameter_setting'] = params
- description['oml:run']['oml:tag'] = tags # Tags describing the run
- # description['oml:run']['oml:output_data'] = 0;
- # all data that was output of this run, which can be evaluation scores
- # (though those are also calculated serverside)
- # must be of special data type
- return description
-
-
-def _create_setup_string(model):
- """Create a string representing the model"""
- run_environment = " ".join(_get_version_information())
- # fixme str(model) might contain (...)
- return run_environment + " " + str(model)
\ No newline at end of file
+ if self.trace is not None:
+ trace_arff = arff.dumps(self.trace.trace_to_arff())
+ file_elements["trace"] = ("trace.arff", trace_arff)
+ return file_elements
+
+ def _to_dict(self) -> dict[str, dict]: # noqa: PLR0912, C901
+ """Creates a dictionary representation of self."""
+ description = OrderedDict() # type: 'OrderedDict'
+ description["oml:run"] = OrderedDict()
+ description["oml:run"]["@xmlns:oml"] = "http://openml.org/openml"
+ description["oml:run"]["oml:task_id"] = self.task_id
+ description["oml:run"]["oml:flow_id"] = self.flow_id
+ if self.setup_string is not None:
+ description["oml:run"]["oml:setup_string"] = self.setup_string
+ if self.error_message is not None:
+ description["oml:run"]["oml:error_message"] = self.error_message
+ if self.run_details is not None:
+ description["oml:run"]["oml:run_details"] = self.run_details
+ description["oml:run"]["oml:parameter_setting"] = self.parameter_settings
+ if self.tags is not None:
+ description["oml:run"]["oml:tag"] = self.tags
+ if (self.fold_evaluations is not None and len(self.fold_evaluations) > 0) or (
+ self.sample_evaluations is not None and len(self.sample_evaluations) > 0
+ ):
+ description["oml:run"]["oml:output_data"] = OrderedDict()
+ description["oml:run"]["oml:output_data"]["oml:evaluation"] = []
+ if self.fold_evaluations is not None:
+ for measure in self.fold_evaluations:
+ for repeat in self.fold_evaluations[measure]:
+ for fold, value in self.fold_evaluations[measure][repeat].items():
+ current = OrderedDict(
+ [
+ ("@repeat", str(repeat)),
+ ("@fold", str(fold)),
+ ("oml:name", measure),
+ ("oml:value", str(value)),
+ ],
+ )
+ description["oml:run"]["oml:output_data"]["oml:evaluation"].append(current)
+ if self.sample_evaluations is not None:
+ for measure in self.sample_evaluations:
+ for repeat in self.sample_evaluations[measure]:
+ for fold in self.sample_evaluations[measure][repeat]:
+ for sample, value in self.sample_evaluations[measure][repeat][fold].items():
+ current = OrderedDict(
+ [
+ ("@repeat", str(repeat)),
+ ("@fold", str(fold)),
+ ("@sample", str(sample)),
+ ("oml:name", measure),
+ ("oml:value", str(value)),
+ ],
+ )
+ description["oml:run"]["oml:output_data"]["oml:evaluation"].append(
+ current,
+ )
+ return description
diff --git a/openml/runs/trace.py b/openml/runs/trace.py
new file mode 100644
index 000000000..708cdd8f1
--- /dev/null
+++ b/openml/runs/trace.py
@@ -0,0 +1,526 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+import json
+from collections import OrderedDict
+from collections.abc import Iterator
+from dataclasses import dataclass
+from pathlib import Path
+from typing import IO, Any
+from typing_extensions import Self
+
+import arff
+import xmltodict
+
+PREFIX = "parameter_"
+REQUIRED_ATTRIBUTES = [
+ "repeat",
+ "fold",
+ "iteration",
+ "evaluation",
+ "selected",
+]
+
+
+@dataclass
+class OpenMLTraceIteration:
+ """
+ OpenML Trace Iteration: parsed output from Run Trace call
+ Exactly one of `setup_string` or `parameters` must be provided.
+
+ Parameters
+ ----------
+ repeat : int
+ repeat number (in case of no repeats: 0)
+
+ fold : int
+ fold number (in case of no folds: 0)
+
+ iteration : int
+ iteration number of optimization procedure
+
+ setup_string : str, optional
+ json string representing the parameters
+ If not provided, ``parameters`` should be set.
+
+ evaluation : double
+ The evaluation that was awarded to this trace iteration.
+ Measure is defined by the task
+
+ selected : bool
+ Whether this was the best of all iterations, and hence
+ selected for making predictions. Per fold/repeat there
+ should be only one iteration selected
+
+ parameters : OrderedDict, optional
+ Dictionary specifying parameter names and their values.
+ If not provided, ``setup_string`` should be set.
+ """
+
+ repeat: int
+ fold: int
+ iteration: int
+
+ evaluation: float
+ selected: bool
+
+ setup_string: dict[str, str] | None = None
+ parameters: dict[str, str | int | float] | None = None
+
+ def __post_init__(self) -> None:
+ # TODO: refactor into one argument of type
+ if self.setup_string and self.parameters:
+ raise ValueError(
+ "Can only be instantiated with either `setup_string` or `parameters` argument.",
+ )
+
+ if not (self.setup_string or self.parameters):
+ raise ValueError(
+ "Either `setup_string` or `parameters` needs to be passed as argument.",
+ )
+
+ if self.parameters is not None and not isinstance(self.parameters, dict):
+ raise TypeError(
+ f"argument parameters is not an instance of OrderedDict, but"
+ f" {type(self.parameters)!s}",
+ )
+
+ def get_parameters(self) -> dict[str, Any]:
+ """Get the parameters of this trace iteration."""
+ # parameters have prefix 'parameter_'
+ if self.setup_string:
+ return {
+ param[len(PREFIX) :]: json.loads(value)
+ for param, value in self.setup_string.items()
+ }
+
+ assert self.parameters is not None
+ return {param[len(PREFIX) :]: value for param, value in self.parameters.items()}
+
+
+class OpenMLRunTrace:
+ """OpenML Run Trace: parsed output from Run Trace call
+
+ Parameters
+ ----------
+ run_id : int
+ OpenML run id.
+
+ trace_iterations : dict
+ Mapping from key ``(repeat, fold, iteration)`` to an object of
+ OpenMLTraceIteration.
+
+ """
+
+ def __init__(
+ self,
+ run_id: int | None,
+ trace_iterations: dict[tuple[int, int, int], OpenMLTraceIteration],
+ ):
+ """Object to hold the trace content of a run.
+
+ Parameters
+ ----------
+ run_id : int
+ Id for which the trace content is to be stored.
+ trace_iterations : List[List]
+ The trace content obtained by running a flow on a task.
+ """
+ self.run_id = run_id
+ self.trace_iterations = trace_iterations
+
+ def get_selected_iteration(self, fold: int, repeat: int) -> int:
+ """
+ Returns the trace iteration that was marked as selected. In
+ case multiple are marked as selected (should not happen) the
+ first of these is returned
+
+ Parameters
+ ----------
+ fold: int
+
+ repeat: int
+
+ Returns
+ -------
+ int
+ The trace iteration from the given fold and repeat that was
+ selected as the best iteration by the search procedure
+ """
+ for r, f, i in self.trace_iterations:
+ if r == repeat and f == fold and self.trace_iterations[(r, f, i)].selected is True:
+ return i
+ raise ValueError(f"Could not find the selected iteration for rep/fold {repeat}/{fold}")
+
+ @classmethod
+ def generate(
+ cls,
+ attributes: list[tuple[str, str]],
+ content: list[list[int | float | str]],
+ ) -> OpenMLRunTrace:
+ """Generates an OpenMLRunTrace.
+
+ Generates the trace object from the attributes and content extracted
+ while running the underlying flow.
+
+ Parameters
+ ----------
+ attributes : list
+ List of tuples describing the arff attributes.
+
+ content : list
+ List of lists containing information about the individual tuning
+ runs.
+
+ Returns
+ -------
+ OpenMLRunTrace
+ """
+ if content is None:
+ raise ValueError("Trace content not available.")
+ if attributes is None:
+ raise ValueError("Trace attributes not available.")
+ if len(content) == 0:
+ raise ValueError("Trace content is empty.")
+ if len(attributes) != len(content[0]):
+ raise ValueError(
+ f"Trace_attributes and trace_content not compatible: {attributes} vs {content[0]}",
+ )
+
+ return cls._trace_from_arff_struct(
+ attributes=attributes,
+ content=content,
+ error_message="setup_string not allowed when constructing a "
+ "trace object from run results.",
+ )
+
+ @classmethod
+ def _from_filesystem(cls, file_path: str | Path) -> OpenMLRunTrace:
+ """
+ Logic to deserialize the trace from the filesystem.
+
+ Parameters
+ ----------
+ file_path: str | Path
+ File path where the trace arff is stored.
+
+ Returns
+ -------
+ OpenMLRunTrace
+ """
+ file_path = Path(file_path)
+
+ if not file_path.exists():
+ raise ValueError("Trace file doesn't exist")
+
+ with file_path.open("r") as fp:
+ trace_arff = arff.load(fp)
+
+ for trace_idx in range(len(trace_arff["data"])):
+ # iterate over first three entrees of a trace row
+ # (fold, repeat, trace_iteration) these should be int
+ for line_idx in range(3):
+ trace_arff["data"][trace_idx][line_idx] = int(
+ trace_arff["data"][trace_idx][line_idx],
+ )
+
+ return cls.trace_from_arff(trace_arff)
+
+ def _to_filesystem(self, file_path: str | Path) -> None:
+ """Serialize the trace object to the filesystem.
+
+ Serialize the trace object as an arff.
+
+ Parameters
+ ----------
+ file_path: str | Path
+ File path where the trace arff will be stored.
+ """
+ trace_path = Path(file_path) / "trace.arff"
+
+ trace_arff = arff.dumps(self.trace_to_arff())
+ with trace_path.open("w") as f:
+ f.write(trace_arff)
+
+ def trace_to_arff(self) -> dict[str, Any]:
+ """Generate the arff dictionary for uploading predictions to the server.
+
+ Uses the trace object to generate an arff dictionary representation.
+
+ Returns
+ -------
+ arff_dict : dict
+ Dictionary representation of the ARFF file that will be uploaded.
+ Contains information about the optimization trace.
+ """
+ if self.trace_iterations is None:
+ raise ValueError("trace_iterations missing from the trace object")
+
+ # attributes that will be in trace arff
+ trace_attributes = [
+ ("repeat", "NUMERIC"),
+ ("fold", "NUMERIC"),
+ ("iteration", "NUMERIC"),
+ ("evaluation", "NUMERIC"),
+ ("selected", ["true", "false"]),
+ ]
+ trace_attributes.extend(
+ [
+ (PREFIX + parameter, "STRING")
+ for parameter in next(iter(self.trace_iterations.values())).get_parameters()
+ ],
+ )
+
+ arff_dict: dict[str, Any] = {}
+ data = []
+ for trace_iteration in self.trace_iterations.values():
+ tmp_list = []
+ for _attr, _ in trace_attributes:
+ if _attr.startswith(PREFIX):
+ attr = _attr[len(PREFIX) :]
+ value = trace_iteration.get_parameters()[attr]
+ else:
+ attr = _attr
+ value = getattr(trace_iteration, attr)
+
+ if attr == "selected":
+ tmp_list.append("true" if value else "false")
+ else:
+ tmp_list.append(value)
+ data.append(tmp_list)
+
+ arff_dict["attributes"] = trace_attributes
+ arff_dict["data"] = data
+ # TODO allow to pass a trace description when running a flow
+ arff_dict["relation"] = "Trace"
+ return arff_dict
+
+ @classmethod
+ def trace_from_arff(cls, arff_obj: dict[str, Any]) -> OpenMLRunTrace:
+ """Generate trace from arff trace.
+
+ Creates a trace file from arff object (for example, generated by a
+ local run).
+
+ Parameters
+ ----------
+ arff_obj : dict
+ LIAC arff obj, dict containing attributes, relation, data.
+
+ Returns
+ -------
+ OpenMLRunTrace
+ """
+ attributes = arff_obj["attributes"]
+ content = arff_obj["data"]
+ return cls._trace_from_arff_struct(
+ attributes=attributes,
+ content=content,
+ error_message="setup_string not supported for arff serialization",
+ )
+
+ @classmethod
+ def _trace_from_arff_struct(
+ cls,
+ attributes: list[tuple[str, str]],
+ content: list[list[int | float | str]],
+ error_message: str,
+ ) -> Self:
+ """Generate a trace dictionary from ARFF structure.
+
+ Parameters
+ ----------
+ cls : type
+ The trace object to be created.
+ attributes : list[tuple[str, str]]
+ Attribute descriptions.
+ content : list[list[int | float | str]]]
+ List of instances.
+ error_message : str
+ Error message to raise if `setup_string` is in `attributes`.
+
+ Returns
+ -------
+ OrderedDict
+ A dictionary representing the trace.
+ """
+ trace = OrderedDict()
+ attribute_idx = {att[0]: idx for idx, att in enumerate(attributes)}
+
+ for required_attribute in REQUIRED_ATTRIBUTES:
+ if required_attribute not in attribute_idx:
+ raise ValueError(f"arff misses required attribute: {required_attribute}")
+ if "setup_string" in attribute_idx:
+ raise ValueError(error_message)
+
+ # note that the required attributes can not be duplicated because
+ # they are not parameters
+ parameter_attributes = []
+ for attribute in attribute_idx:
+ if attribute in REQUIRED_ATTRIBUTES or attribute == "setup_string":
+ continue
+
+ if not attribute.startswith(PREFIX):
+ raise ValueError(
+ f"Encountered unknown attribute {attribute} that does not start "
+ f"with prefix {PREFIX}",
+ )
+
+ parameter_attributes.append(attribute)
+
+ for itt in content:
+ repeat = int(itt[attribute_idx["repeat"]])
+ fold = int(itt[attribute_idx["fold"]])
+ iteration = int(itt[attribute_idx["iteration"]])
+ evaluation = float(itt[attribute_idx["evaluation"]])
+ selected_value = itt[attribute_idx["selected"]]
+ if selected_value == "true":
+ selected = True
+ elif selected_value == "false":
+ selected = False
+ else:
+ raise ValueError(
+ 'expected {"true", "false"} value for selected field, '
+ f"received: {selected_value}",
+ )
+
+ parameters = {
+ attribute: itt[attribute_idx[attribute]] for attribute in parameter_attributes
+ }
+
+ current = OpenMLTraceIteration(
+ repeat=repeat,
+ fold=fold,
+ iteration=iteration,
+ setup_string=None,
+ evaluation=evaluation,
+ selected=selected,
+ parameters=parameters,
+ )
+ trace[(repeat, fold, iteration)] = current
+
+ return cls(None, trace)
+
+ @classmethod
+ def trace_from_xml(cls, xml: str | Path | IO) -> OpenMLRunTrace:
+ """Generate trace from xml.
+
+ Creates a trace file from the xml description.
+
+ Parameters
+ ----------
+ xml : string | file-like object
+ An xml description that can be either a `string` or a file-like
+ object.
+
+ Returns
+ -------
+ run : OpenMLRunTrace
+ Object containing the run id and a dict containing the trace
+ iterations.
+ """
+ if isinstance(xml, Path):
+ xml = str(xml.absolute())
+
+ result_dict = xmltodict.parse(xml, force_list=("oml:trace_iteration",))["oml:trace"]
+
+ run_id = result_dict["oml:run_id"]
+ trace = OrderedDict()
+
+ if "oml:trace_iteration" not in result_dict:
+ raise ValueError("Run does not contain valid trace. ")
+ if not isinstance(result_dict["oml:trace_iteration"], list):
+ raise TypeError(type(result_dict["oml:trace_iteration"]))
+
+ for itt in result_dict["oml:trace_iteration"]:
+ repeat = int(itt["oml:repeat"])
+ fold = int(itt["oml:fold"])
+ iteration = int(itt["oml:iteration"])
+ setup_string = json.loads(itt["oml:setup_string"])
+ evaluation = float(itt["oml:evaluation"])
+ selected_value = itt["oml:selected"]
+ if selected_value == "true":
+ selected = True
+ elif selected_value == "false":
+ selected = False
+ else:
+ raise ValueError(
+ 'expected {"true", "false"} value for '
+ f"selected field, received: {selected_value}",
+ )
+
+ current = OpenMLTraceIteration(
+ repeat=repeat,
+ fold=fold,
+ iteration=iteration,
+ setup_string=setup_string,
+ evaluation=evaluation,
+ selected=selected,
+ )
+ trace[(repeat, fold, iteration)] = current
+
+ return cls(run_id, trace)
+
+ @classmethod
+ def merge_traces(cls, traces: list[OpenMLRunTrace]) -> OpenMLRunTrace:
+ """Merge multiple traces into a single trace.
+
+ Parameters
+ ----------
+ cls : type
+ Type of the trace object to be created.
+ traces : List[OpenMLRunTrace]
+ List of traces to merge.
+
+ Returns
+ -------
+ OpenMLRunTrace
+ A trace object representing the merged traces.
+
+ Raises
+ ------
+ ValueError
+ If the parameters in the iterations of the traces being merged are not equal.
+ If a key (repeat, fold, iteration) is encountered twice while merging the traces.
+ """
+ merged_trace: dict[tuple[int, int, int], OpenMLTraceIteration] = {}
+
+ previous_iteration = None
+ for trace in traces:
+ for iteration in trace:
+ key = (iteration.repeat, iteration.fold, iteration.iteration)
+
+ assert iteration.parameters is not None
+ param_keys = iteration.parameters.keys()
+
+ if previous_iteration is not None:
+ trace_itr = merged_trace[previous_iteration]
+
+ assert trace_itr.parameters is not None
+ trace_itr_keys = trace_itr.parameters.keys()
+
+ if list(param_keys) != list(trace_itr_keys):
+ raise ValueError(
+ "Cannot merge traces because the parameters are not equal: "
+ f"{list(trace_itr.parameters.keys())} vs "
+ f"{list(iteration.parameters.keys())}",
+ )
+
+ if key in merged_trace:
+ raise ValueError(
+ f"Cannot merge traces because key '{key}' was encountered twice",
+ )
+
+ merged_trace[key] = iteration
+ previous_iteration = key
+
+ return cls(None, merged_trace)
+
+ def __repr__(self) -> str:
+ return (
+ f"[Run id: {-1 if self.run_id is None else self.run_id}, "
+ f"{len(self.trace_iterations)} trace iterations]"
+ )
+
+ def __iter__(self) -> Iterator[OpenMLTraceIteration]:
+ yield from self.trace_iterations.values()
diff --git a/openml/setups/__init__.py b/openml/setups/__init__.py
new file mode 100644
index 000000000..fa4072059
--- /dev/null
+++ b/openml/setups/__init__.py
@@ -0,0 +1,13 @@
+# License: BSD 3-Clause
+
+from .functions import get_setup, initialize_model, list_setups, setup_exists
+from .setup import OpenMLParameter, OpenMLSetup
+
+__all__ = [
+ "OpenMLParameter",
+ "OpenMLSetup",
+ "get_setup",
+ "initialize_model",
+ "list_setups",
+ "setup_exists",
+]
diff --git a/openml/setups/functions.py b/openml/setups/functions.py
new file mode 100644
index 000000000..4bf279ed1
--- /dev/null
+++ b/openml/setups/functions.py
@@ -0,0 +1,347 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+from collections import OrderedDict
+from collections.abc import Iterable
+from functools import partial
+from itertools import chain
+from pathlib import Path
+from typing import Any, Literal
+
+import pandas as pd
+import xmltodict
+
+import openml
+import openml.exceptions
+import openml.utils
+from openml import config
+from openml.flows import OpenMLFlow, flow_exists
+
+from .setup import OpenMLParameter, OpenMLSetup
+
+
+def setup_exists(flow: OpenMLFlow) -> int:
+ """
+ Checks whether a hyperparameter configuration already exists on the server.
+
+ Parameters
+ ----------
+ flow : OpenMLFlow
+ The openml flow object. Should have flow id present for the main flow
+ and all subflows (i.e., it should be downloaded from the server by
+ means of flow.get, and not instantiated locally)
+
+ Returns
+ -------
+ setup_id : int
+ setup id iff exists, False otherwise
+ """
+ # sadly, this api call relies on a run object
+ openml.flows.functions._check_flow_for_server_id(flow)
+ if flow.model is None:
+ raise ValueError("Flow should have model field set with the actual model.")
+ if flow.extension is None:
+ raise ValueError("Flow should have model field set with the correct extension.")
+
+ # checks whether the flow exists on the server and flow ids align
+ exists = flow_exists(flow.name, flow.external_version)
+ if exists != flow.flow_id:
+ raise ValueError(
+ f"Local flow id ({flow.id}) differs from server id ({exists}). "
+ "If this issue persists, please contact the developers.",
+ )
+
+ openml_param_settings = flow.extension.obtain_parameter_values(flow)
+ description = xmltodict.unparse(_to_dict(flow.flow_id, openml_param_settings), pretty=True)
+ file_elements = {
+ "description": ("description.arff", description),
+ } # type: openml._api_calls.FILE_ELEMENTS_TYPE
+ result = openml._api_calls._perform_api_call(
+ "/setup/exists/",
+ "post",
+ file_elements=file_elements,
+ )
+ result_dict = xmltodict.parse(result)
+ setup_id = int(result_dict["oml:setup_exists"]["oml:id"])
+ return setup_id if setup_id > 0 else False
+
+
+def _get_cached_setup(setup_id: int) -> OpenMLSetup:
+ """Load a run from the cache.
+
+ Parameters
+ ----------
+ setup_id : int
+ ID of the setup to be loaded.
+
+ Returns
+ -------
+ OpenMLSetup
+ The loaded setup object.
+
+ Raises
+ ------
+ OpenMLCacheException
+ If the setup file for the given setup ID is not cached.
+ """
+ cache_dir = Path(config.get_cache_directory())
+ setup_cache_dir = cache_dir / "setups" / str(setup_id)
+ try:
+ setup_file = setup_cache_dir / "description.xml"
+ with setup_file.open(encoding="utf8") as fh:
+ setup_xml = xmltodict.parse(fh.read())
+ return _create_setup_from_xml(setup_xml)
+
+ except OSError as e:
+ raise openml.exceptions.OpenMLCacheException(
+ f"Setup file for setup id {setup_id} not cached",
+ ) from e
+
+
+def get_setup(setup_id: int) -> OpenMLSetup:
+ """
+ Downloads the setup (configuration) description from OpenML
+ and returns a structured object
+
+ Parameters
+ ----------
+ setup_id : int
+ The Openml setup_id
+
+ Returns
+ -------
+ OpenMLSetup (an initialized openml setup object)
+ """
+ setup_dir = Path(config.get_cache_directory()) / "setups" / str(setup_id)
+ setup_dir.mkdir(exist_ok=True, parents=True)
+
+ setup_file = setup_dir / "description.xml"
+
+ try:
+ return _get_cached_setup(setup_id)
+ except openml.exceptions.OpenMLCacheException:
+ url_suffix = f"/setup/{setup_id}"
+ setup_xml = openml._api_calls._perform_api_call(url_suffix, "get")
+ with setup_file.open("w", encoding="utf8") as fh:
+ fh.write(setup_xml)
+
+ result_dict = xmltodict.parse(setup_xml)
+ return _create_setup_from_xml(result_dict)
+
+
+def list_setups( # noqa: PLR0913
+ offset: int | None = None,
+ size: int | None = None,
+ flow: int | None = None,
+ tag: str | None = None,
+ setup: Iterable[int] | None = None,
+ output_format: Literal["object", "dataframe"] = "object",
+) -> dict[int, OpenMLSetup] | pd.DataFrame:
+ """
+ List all setups matching all of the given filters.
+
+ Parameters
+ ----------
+ offset : int, optional
+ size : int, optional
+ flow : int, optional
+ tag : str, optional
+ setup : Iterable[int], optional
+ output_format: str, optional (default='object')
+ The parameter decides the format of the output.
+ - If 'dataframe' the output is a pandas DataFrame
+ - If 'object' the output is a dictionary of OpenMLSetup objects
+
+ Returns
+ -------
+ dict or dataframe
+ """
+ if output_format not in ["dataframe", "object"]:
+ raise ValueError(
+ "Invalid output format selected. Only 'object', or 'dataframe' applicable.",
+ )
+
+ listing_call = partial(_list_setups, flow=flow, tag=tag, setup=setup)
+ batches = openml.utils._list_all(
+ listing_call,
+ batch_size=1_000, # batch size for setups is lower
+ offset=offset,
+ limit=size,
+ )
+ flattened = list(chain.from_iterable(batches))
+ if output_format == "object":
+ return {setup.setup_id: setup for setup in flattened}
+
+ records = [setup._to_dict() for setup in flattened]
+ return pd.DataFrame.from_records(records, index="setup_id")
+
+
+def _list_setups(
+ limit: int,
+ offset: int,
+ *,
+ setup: Iterable[int] | None = None,
+ flow: int | None = None,
+ tag: str | None = None,
+) -> list[OpenMLSetup]:
+ """Perform API call `/setup/list/{filters}`
+
+ Parameters
+ ----------
+ The setup argument that is a list is separated from the single value
+ filters which are put into the kwargs.
+
+ limit : int
+ offset : int
+ setup : list(int), optional
+ flow : int, optional
+ tag : str, optional
+
+ Returns
+ -------
+ The setups that match the filters, going from id to the OpenMLSetup object.
+ """
+ api_call = "setup/list"
+ if limit is not None:
+ api_call += f"/limit/{limit}"
+ if offset is not None:
+ api_call += f"/offset/{offset}"
+ if setup is not None:
+ api_call += f"/setup/{','.join([str(int(i)) for i in setup])}"
+ if flow is not None:
+ api_call += f"/flow/{flow}"
+ if tag is not None:
+ api_call += f"/tag/{tag}"
+
+ return __list_setups(api_call=api_call)
+
+
+def __list_setups(api_call: str) -> list[OpenMLSetup]:
+ """Helper function to parse API calls which are lists of setups"""
+ xml_string = openml._api_calls._perform_api_call(api_call, "get")
+ setups_dict = xmltodict.parse(xml_string, force_list=("oml:setup",))
+ openml_uri = "http://openml.org/openml"
+ # Minimalistic check if the XML is useful
+ if "oml:setups" not in setups_dict:
+ raise ValueError(
+ f'Error in return XML, does not contain "oml:setups": {setups_dict!s}',
+ )
+
+ if "@xmlns:oml" not in setups_dict["oml:setups"]:
+ raise ValueError(
+ f'Error in return XML, does not contain "oml:setups"/@xmlns:oml: {setups_dict!s}',
+ )
+
+ if setups_dict["oml:setups"]["@xmlns:oml"] != openml_uri:
+ raise ValueError(
+ "Error in return XML, value of "
+ '"oml:seyups"/@xmlns:oml is not '
+ f'"{openml_uri}": {setups_dict!s}',
+ )
+
+ assert isinstance(setups_dict["oml:setups"]["oml:setup"], list), type(setups_dict["oml:setups"])
+
+ return [
+ _create_setup_from_xml({"oml:setup_parameters": setup_})
+ for setup_ in setups_dict["oml:setups"]["oml:setup"]
+ ]
+
+
+def initialize_model(setup_id: int, *, strict_version: bool = True) -> Any:
+ """
+ Initialized a model based on a setup_id (i.e., using the exact
+ same parameter settings)
+
+ Parameters
+ ----------
+ setup_id : int
+ The Openml setup_id
+ strict_version: bool (default=True)
+ See `flow_to_model` strict_version.
+
+ Returns
+ -------
+ model
+ """
+ setup = get_setup(setup_id)
+ flow = openml.flows.get_flow(setup.flow_id)
+
+ # instead of using scikit-learns or any other library's "set_params" function, we override the
+ # OpenMLFlow objects default parameter value so we can utilize the
+ # Extension.flow_to_model() function to reinitialize the flow with the set defaults.
+ if setup.parameters is not None:
+ for hyperparameter in setup.parameters.values():
+ structure = flow.get_structure("flow_id")
+ if len(structure[hyperparameter.flow_id]) > 0:
+ subflow = flow.get_subflow(structure[hyperparameter.flow_id])
+ else:
+ subflow = flow
+ subflow.parameters[hyperparameter.parameter_name] = hyperparameter.value
+
+ return flow.extension.flow_to_model(flow, strict_version=strict_version)
+
+
+def _to_dict(flow_id: int, openml_parameter_settings: list[dict[str, Any]]) -> OrderedDict:
+ """Convert a flow ID and a list of OpenML parameter settings to
+ a dictionary representation that can be serialized to XML.
+
+ Parameters
+ ----------
+ flow_id : int
+ ID of the flow.
+ openml_parameter_settings : list[dict[str, Any]]
+ A list of OpenML parameter settings.
+
+ Returns
+ -------
+ OrderedDict
+ A dictionary representation of the flow ID and parameter settings.
+ """
+ # for convenience, this function (ab)uses the run object.
+ xml: OrderedDict = OrderedDict()
+ xml["oml:run"] = OrderedDict()
+ xml["oml:run"]["@xmlns:oml"] = "http://openml.org/openml"
+ xml["oml:run"]["oml:flow_id"] = flow_id
+ xml["oml:run"]["oml:parameter_setting"] = openml_parameter_settings
+
+ return xml
+
+
+def _create_setup_from_xml(result_dict: dict) -> OpenMLSetup:
+ """Turns an API xml result into a OpenMLSetup object (or dict)"""
+ setup_id = int(result_dict["oml:setup_parameters"]["oml:setup_id"])
+ flow_id = int(result_dict["oml:setup_parameters"]["oml:flow_id"])
+
+ if "oml:parameter" not in result_dict["oml:setup_parameters"]:
+ return OpenMLSetup(setup_id, flow_id, parameters=None)
+
+ xml_parameters = result_dict["oml:setup_parameters"]["oml:parameter"]
+ if isinstance(xml_parameters, dict):
+ parameters = {
+ int(xml_parameters["oml:id"]): _create_setup_parameter_from_xml(xml_parameters),
+ }
+ elif isinstance(xml_parameters, list):
+ parameters = {
+ int(xml_parameter["oml:id"]): _create_setup_parameter_from_xml(xml_parameter)
+ for xml_parameter in xml_parameters
+ }
+ else:
+ raise ValueError(
+ f"Expected None, list or dict, received something else: {type(xml_parameters)!s}",
+ )
+
+ return OpenMLSetup(setup_id, flow_id, parameters)
+
+
+def _create_setup_parameter_from_xml(result_dict: dict[str, str]) -> OpenMLParameter:
+ """Create an OpenMLParameter object or a dictionary from an API xml result."""
+ return OpenMLParameter(
+ input_id=int(result_dict["oml:id"]),
+ flow_id=int(result_dict["oml:flow_id"]),
+ flow_name=result_dict["oml:flow_name"],
+ full_name=result_dict["oml:full_name"],
+ parameter_name=result_dict["oml:parameter_name"],
+ data_type=result_dict["oml:data_type"],
+ default_value=result_dict["oml:default_value"],
+ value=result_dict["oml:value"],
+ )
diff --git a/openml/setups/setup.py b/openml/setups/setup.py
new file mode 100644
index 000000000..170838138
--- /dev/null
+++ b/openml/setups/setup.py
@@ -0,0 +1,154 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+from dataclasses import asdict, dataclass
+from typing import Any
+
+import openml.config
+import openml.flows
+
+
+@dataclass
+class OpenMLSetup:
+ """Setup object (a.k.a. Configuration).
+
+ Parameters
+ ----------
+ setup_id : int
+ The OpenML setup id
+ flow_id : int
+ The flow that it is build upon
+ parameters : dict
+ The setting of the parameters
+ """
+
+ setup_id: int
+ flow_id: int
+ parameters: dict[int, Any] | None
+
+ def __post_init__(self) -> None:
+ if not isinstance(self.setup_id, int):
+ raise ValueError("setup id should be int")
+
+ if not isinstance(self.flow_id, int):
+ raise ValueError("flow id should be int")
+
+ if self.parameters is not None and not isinstance(self.parameters, dict):
+ raise ValueError("parameters should be dict")
+
+ def _to_dict(self) -> dict[str, Any]:
+ return {
+ "setup_id": self.setup_id,
+ "flow_id": self.flow_id,
+ "parameters": {p.id: p._to_dict() for p in self.parameters.values()}
+ if self.parameters is not None
+ else None,
+ }
+
+ def __repr__(self) -> str:
+ header = "OpenML Setup"
+ header = f"{header}\n{'=' * len(header)}\n"
+
+ fields = {
+ "Setup ID": self.setup_id,
+ "Flow ID": self.flow_id,
+ "Flow URL": openml.flows.OpenMLFlow.url_for_id(self.flow_id),
+ "# of Parameters": (
+ len(self.parameters) if self.parameters is not None else float("nan")
+ ),
+ }
+
+ # determines the order in which the information will be printed
+ order = ["Setup ID", "Flow ID", "Flow URL", "# of Parameters"]
+ _fields = [(key, fields[key]) for key in order if key in fields]
+
+ longest_field_name_length = max(len(name) for name, _ in _fields)
+ field_line_format = f"{{:.<{longest_field_name_length}}}: {{}}"
+ body = "\n".join(field_line_format.format(name, value) for name, value in _fields)
+ return header + body
+
+
+@dataclass
+class OpenMLParameter:
+ """Parameter object (used in setup).
+
+ Parameters
+ ----------
+ input_id : int
+ The input id from the openml database
+ flow id : int
+ The flow to which this parameter is associated
+ flow name : str
+ The name of the flow (no version number) to which this parameter
+ is associated
+ full_name : str
+ The name of the flow and parameter combined
+ parameter_name : str
+ The name of the parameter
+ data_type : str
+ The datatype of the parameter. generally unused for sklearn flows
+ default_value : str
+ The default value. For sklearn parameters, this is unknown and a
+ default value is selected arbitrarily
+ value : str
+ If the parameter was set, the value that it was set to.
+ """
+
+ input_id: int
+ flow_id: int
+ flow_name: str
+ full_name: str
+ parameter_name: str
+ data_type: str
+ default_value: str
+ value: str
+
+ def __post_init__(self) -> None:
+ # Map input_id to id for backward compatibility
+ self.id = self.input_id
+
+ def _to_dict(self) -> dict[str, Any]:
+ result = asdict(self)
+ # Replaces input_id with id for backward compatibility
+ result["id"] = result.pop("input_id")
+ return result
+
+ def __repr__(self) -> str:
+ header = "OpenML Parameter"
+ header = f"{header}\n{'=' * len(header)}\n"
+
+ fields = {
+ "ID": self.id,
+ "Flow ID": self.flow_id,
+ # "Flow Name": self.flow_name,
+ "Flow Name": self.full_name,
+ "Flow URL": openml.flows.OpenMLFlow.url_for_id(self.flow_id),
+ "Parameter Name": self.parameter_name,
+ }
+ # indented prints for parameter attributes
+ # indention = 2 spaces + 1 | + 2 underscores
+ indent = f"{' ' * 2}|{'_' * 2}"
+ parameter_data_type = f"{indent}Data Type"
+ fields[parameter_data_type] = self.data_type
+ parameter_default = f"{indent}Default"
+ fields[parameter_default] = self.default_value
+ parameter_value = f"{indent}Value"
+ fields[parameter_value] = self.value
+
+ # determines the order in which the information will be printed
+ order = [
+ "ID",
+ "Flow ID",
+ "Flow Name",
+ "Flow URL",
+ "Parameter Name",
+ parameter_data_type,
+ parameter_default,
+ parameter_value,
+ ]
+ _fields = [(key, fields[key]) for key in order if key in fields]
+
+ longest_field_name_length = max(len(name) for name, _ in _fields)
+ field_line_format = f"{{:.<{longest_field_name_length}}}: {{}}"
+ body = "\n".join(field_line_format.format(name, value) for name, value in _fields)
+ return header + body
diff --git a/openml/study/__init__.py b/openml/study/__init__.py
new file mode 100644
index 000000000..37a6d376a
--- /dev/null
+++ b/openml/study/__init__.py
@@ -0,0 +1,38 @@
+# License: BSD 3-Clause
+
+from .functions import (
+ attach_to_study,
+ attach_to_suite,
+ create_benchmark_suite,
+ create_study,
+ delete_study,
+ delete_suite,
+ detach_from_study,
+ detach_from_suite,
+ get_study,
+ get_suite,
+ list_studies,
+ list_suites,
+ update_study_status,
+ update_suite_status,
+)
+from .study import OpenMLBenchmarkSuite, OpenMLStudy
+
+__all__ = [
+ "OpenMLBenchmarkSuite",
+ "OpenMLStudy",
+ "attach_to_study",
+ "attach_to_suite",
+ "create_benchmark_suite",
+ "create_study",
+ "delete_study",
+ "delete_suite",
+ "detach_from_study",
+ "detach_from_suite",
+ "get_study",
+ "get_suite",
+ "list_studies",
+ "list_suites",
+ "update_study_status",
+ "update_suite_status",
+]
diff --git a/openml/study/functions.py b/openml/study/functions.py
new file mode 100644
index 000000000..bb24ddcff
--- /dev/null
+++ b/openml/study/functions.py
@@ -0,0 +1,608 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+import warnings
+from functools import partial
+from typing import TYPE_CHECKING, Any
+
+import pandas as pd
+import xmltodict
+
+import openml._api_calls
+import openml.config
+import openml.utils
+from openml.study.study import OpenMLBenchmarkSuite, OpenMLStudy
+
+if TYPE_CHECKING:
+ from openml.study.study import BaseStudy
+
+
+def get_suite(suite_id: int | str) -> OpenMLBenchmarkSuite:
+ """
+ Retrieves all relevant information of an OpenML benchmarking suite from the server.
+
+ Parameters
+ ----------
+ study id : int, str
+ study id (numeric or alias)
+
+ Returns
+ -------
+ OpenMLSuite
+ The OpenML suite object
+ """
+ study = _get_study(suite_id, entity_type="task")
+ assert isinstance(study, OpenMLBenchmarkSuite)
+
+ return study
+
+
+def get_study(
+ study_id: int | str,
+ arg_for_backwards_compat: str | None = None, # noqa: ARG001
+) -> OpenMLStudy: # F401
+ """
+ Retrieves all relevant information of an OpenML study from the server.
+
+ Parameters
+ ----------
+ study id : int, str
+ study id (numeric or alias)
+
+ arg_for_backwards_compat : str, optional
+ The example given in https://arxiv.org/pdf/1708.03731.pdf uses an older version of the
+ API which required specifying the type of study, i.e. tasks. We changed the
+ implementation of studies since then and split them up into suites (collections of tasks)
+ and studies (collections of runs) so this argument is no longer needed.
+
+ Returns
+ -------
+ OpenMLStudy
+ The OpenML study object
+ """
+ if study_id == "OpenML100":
+ message = (
+ "It looks like you are running code from the OpenML100 paper. It still works, but lots "
+ "of things have changed since then. Please use `get_suite('OpenML100')` instead."
+ )
+ warnings.warn(message, DeprecationWarning, stacklevel=2)
+ openml.config.logger.warning(message)
+ study = _get_study(study_id, entity_type="task")
+ assert isinstance(study, OpenMLBenchmarkSuite)
+
+ return study # type: ignore
+
+ study = _get_study(study_id, entity_type="run")
+ assert isinstance(study, OpenMLStudy)
+ return study
+
+
+def _get_study(id_: int | str, entity_type: str) -> BaseStudy:
+ xml_string = openml._api_calls._perform_api_call(f"study/{id_}", "get")
+ force_list_tags = (
+ "oml:data_id",
+ "oml:flow_id",
+ "oml:task_id",
+ "oml:setup_id",
+ "oml:run_id",
+ "oml:tag", # legacy.
+ )
+ result_dict = xmltodict.parse(xml_string, force_list=force_list_tags)["oml:study"]
+ study_id = int(result_dict["oml:id"])
+ alias = result_dict.get("oml:alias", None)
+ main_entity_type = result_dict["oml:main_entity_type"]
+
+ if entity_type != main_entity_type:
+ raise ValueError(
+ f"Unexpected entity type '{main_entity_type}' reported by the server"
+ f", expected '{entity_type}'"
+ )
+
+ benchmark_suite = result_dict.get("oml:benchmark_suite", None)
+ name = result_dict["oml:name"]
+ description = result_dict["oml:description"]
+ status = result_dict["oml:status"]
+ creation_date = result_dict["oml:creation_date"]
+ creator = result_dict["oml:creator"]
+
+ # tags is legacy. remove once no longer needed.
+ tags = []
+ if "oml:tag" in result_dict:
+ for tag in result_dict["oml:tag"]:
+ current_tag = {"name": tag["oml:name"], "write_access": tag["oml:write_access"]}
+ if "oml:window_start" in tag:
+ current_tag["window_start"] = tag["oml:window_start"]
+ tags.append(current_tag)
+
+ def get_nested_ids_from_result_dict(key: str, subkey: str) -> list[int] | None:
+ """Extracts a list of nested IDs from a result dictionary.
+
+ Parameters
+ ----------
+ key : str
+ Nested OpenML IDs.
+ subkey : str
+ The subkey contains the nested OpenML IDs.
+
+ Returns
+ -------
+ Optional[List]
+ A list of nested OpenML IDs, or None if the key is not present in the dictionary.
+ """
+ if result_dict.get(key) is not None:
+ return [int(oml_id) for oml_id in result_dict[key][subkey]]
+ return None
+
+ datasets = get_nested_ids_from_result_dict("oml:data", "oml:data_id")
+ tasks = get_nested_ids_from_result_dict("oml:tasks", "oml:task_id")
+
+ if main_entity_type in ["runs", "run"]:
+ flows = get_nested_ids_from_result_dict("oml:flows", "oml:flow_id")
+ setups = get_nested_ids_from_result_dict("oml:setups", "oml:setup_id")
+ runs = get_nested_ids_from_result_dict("oml:runs", "oml:run_id")
+
+ study = OpenMLStudy(
+ study_id=study_id,
+ alias=alias,
+ benchmark_suite=benchmark_suite,
+ name=name,
+ description=description,
+ status=status,
+ creation_date=creation_date,
+ creator=creator,
+ tags=tags,
+ data=datasets,
+ tasks=tasks,
+ flows=flows,
+ setups=setups,
+ runs=runs,
+ ) # type: BaseStudy
+
+ elif main_entity_type in ["tasks", "task"]:
+ study = OpenMLBenchmarkSuite(
+ suite_id=study_id,
+ alias=alias,
+ name=name,
+ description=description,
+ status=status,
+ creation_date=creation_date,
+ creator=creator,
+ tags=tags,
+ data=datasets,
+ tasks=tasks,
+ )
+
+ else:
+ raise ValueError(f"Unknown entity type {main_entity_type}")
+
+ return study
+
+
+def create_study(
+ name: str,
+ description: str,
+ run_ids: list[int] | None = None,
+ alias: str | None = None,
+ benchmark_suite: int | None = None,
+) -> OpenMLStudy:
+ """
+ Creates an OpenML study (collection of data, tasks, flows, setups and run),
+ where the runs are the main entity (collection consists of runs and all
+ entities (flows, tasks, etc) that are related to these runs)
+
+ Parameters
+ ----------
+ benchmark_suite : int (optional)
+ the benchmark suite (another study) upon which this study is ran.
+ name : str
+ the name of the study (meta-info)
+ description : str
+ brief description (meta-info)
+ run_ids : list, optional
+ a list of run ids associated with this study,
+ these can also be added later with ``attach_to_study``.
+ alias : str (optional)
+ a string ID, unique on server (url-friendly)
+ benchmark_suite: int (optional)
+ the ID of the suite for which this study contains run results
+
+ Returns
+ -------
+ OpenMLStudy
+ A local OpenML study object (call publish method to upload to server)
+ """
+ return OpenMLStudy(
+ study_id=None,
+ alias=alias,
+ benchmark_suite=benchmark_suite,
+ name=name,
+ description=description,
+ status=None,
+ creation_date=None,
+ creator=None,
+ tags=None,
+ data=None,
+ tasks=None,
+ flows=None,
+ runs=run_ids if run_ids != [] else None,
+ setups=None,
+ )
+
+
+def create_benchmark_suite(
+ name: str,
+ description: str,
+ task_ids: list[int],
+ alias: str | None = None,
+) -> OpenMLBenchmarkSuite:
+ """
+ Creates an OpenML benchmark suite (collection of entity types, where
+ the tasks are the linked entity)
+
+ Parameters
+ ----------
+ name : str
+ the name of the study (meta-info)
+ description : str
+ brief description (meta-info)
+ task_ids : list
+ a list of task ids associated with this study
+ more can be added later with ``attach_to_suite``.
+ alias : str (optional)
+ a string ID, unique on server (url-friendly)
+
+ Returns
+ -------
+ OpenMLStudy
+ A local OpenML study object (call publish method to upload to server)
+ """
+ return OpenMLBenchmarkSuite(
+ suite_id=None,
+ alias=alias,
+ name=name,
+ description=description,
+ status=None,
+ creation_date=None,
+ creator=None,
+ tags=None,
+ data=None,
+ tasks=task_ids,
+ )
+
+
+def update_suite_status(suite_id: int, status: str) -> None:
+ """
+ Updates the status of a study to either 'active' or 'deactivated'.
+
+ Parameters
+ ----------
+ suite_id : int
+ The data id of the dataset
+ status : str,
+ 'active' or 'deactivated'
+ """
+ return update_study_status(suite_id, status)
+
+
+def update_study_status(study_id: int, status: str) -> None:
+ """
+ Updates the status of a study to either 'active' or 'deactivated'.
+
+ Parameters
+ ----------
+ study_id : int
+ The data id of the dataset
+ status : str,
+ 'active' or 'deactivated'
+ """
+ legal_status = {"active", "deactivated"}
+ if status not in legal_status:
+ raise ValueError(f"Illegal status value. Legal values: {legal_status}")
+ data = {"study_id": study_id, "status": status} # type: openml._api_calls.DATA_TYPE
+ result_xml = openml._api_calls._perform_api_call("study/status/update", "post", data=data)
+ result = xmltodict.parse(result_xml)
+ server_study_id = result["oml:study_status_update"]["oml:id"]
+ server_status = result["oml:study_status_update"]["oml:status"]
+ if status != server_status or int(study_id) != int(server_study_id):
+ # This should never happen
+ raise ValueError("Study id/status does not collide")
+
+
+def delete_suite(suite_id: int) -> bool:
+ """Deletes a study from the OpenML server.
+
+ Parameters
+ ----------
+ suite_id : int
+ OpenML id of the study
+
+ Returns
+ -------
+ bool
+ True iff the deletion was successful. False otherwise
+ """
+ return delete_study(suite_id)
+
+
+def delete_study(study_id: int) -> bool:
+ """Deletes a study from the OpenML server.
+
+ Parameters
+ ----------
+ study_id : int
+ OpenML id of the study
+
+ Returns
+ -------
+ bool
+ True iff the deletion was successful. False otherwise
+ """
+ return openml.utils._delete_entity("study", study_id)
+
+
+def attach_to_suite(suite_id: int, task_ids: list[int]) -> int:
+ """Attaches a set of tasks to a benchmarking suite.
+
+ Parameters
+ ----------
+ suite_id : int
+ OpenML id of the study
+
+ task_ids : list (int)
+ List of entities to link to the collection
+
+ Returns
+ -------
+ int
+ new size of the suite (in terms of explicitly linked entities)
+ """
+ return attach_to_study(suite_id, task_ids)
+
+
+def attach_to_study(study_id: int, run_ids: list[int]) -> int:
+ """Attaches a set of runs to a study.
+
+ Parameters
+ ----------
+ study_id : int
+ OpenML id of the study
+
+ run_ids : list (int)
+ List of entities to link to the collection
+
+ Returns
+ -------
+ int
+ new size of the study (in terms of explicitly linked entities)
+ """
+ # Interestingly, there's no need to tell the server about the entity type, it knows by itself
+ result_xml = openml._api_calls._perform_api_call(
+ call=f"study/{study_id}/attach",
+ request_method="post",
+ data={"ids": ",".join(str(x) for x in run_ids)},
+ )
+ result = xmltodict.parse(result_xml)["oml:study_attach"]
+ return int(result["oml:linked_entities"])
+
+
+def detach_from_suite(suite_id: int, task_ids: list[int]) -> int:
+ """Detaches a set of task ids from a suite.
+
+ Parameters
+ ----------
+ suite_id : int
+ OpenML id of the study
+
+ task_ids : list (int)
+ List of entities to unlink from the collection
+
+ Returns
+ -------
+ int
+ new size of the study (in terms of explicitly linked entities)
+ """
+ return detach_from_study(suite_id, task_ids)
+
+
+def detach_from_study(study_id: int, run_ids: list[int]) -> int:
+ """Detaches a set of run ids from a study.
+
+ Parameters
+ ----------
+ study_id : int
+ OpenML id of the study
+
+ run_ids : list (int)
+ List of entities to unlink from the collection
+
+ Returns
+ -------
+ int
+ new size of the study (in terms of explicitly linked entities)
+ """
+ # Interestingly, there's no need to tell the server about the entity type, it knows by itself
+ uri = f"study/{study_id}/detach"
+ post_variables = {"ids": ",".join(str(x) for x in run_ids)} # type: openml._api_calls.DATA_TYPE
+ result_xml = openml._api_calls._perform_api_call(
+ call=uri,
+ request_method="post",
+ data=post_variables,
+ )
+ result = xmltodict.parse(result_xml)["oml:study_detach"]
+ return int(result["oml:linked_entities"])
+
+
+def list_suites(
+ offset: int | None = None,
+ size: int | None = None,
+ status: str | None = None,
+ uploader: list[int] | None = None,
+) -> pd.DataFrame:
+ """
+ Return a list of all suites which are on OpenML.
+
+ Parameters
+ ----------
+ offset : int, optional
+ The number of suites to skip, starting from the first.
+ size : int, optional
+ The maximum number of suites to show.
+ status : str, optional
+ Should be {active, in_preparation, deactivated, all}. By default active
+ suites are returned.
+ uploader : list (int), optional
+ Result filter. Will only return suites created by these users.
+
+ Returns
+ -------
+ datasets : dataframe
+ Every row is represented by a dictionary containing the following information:
+ - id
+ - alias (optional)
+ - name
+ - main_entity_type
+ - status
+ - creator
+ - creation_date
+ """
+ listing_call = partial(
+ _list_studies,
+ main_entity_type="task",
+ status=status,
+ uploader=uploader,
+ )
+ batches = openml.utils._list_all(listing_call, limit=size, offset=offset)
+ if len(batches) == 0:
+ return pd.DataFrame()
+
+ return pd.concat(batches)
+
+
+def list_studies(
+ offset: int | None = None,
+ size: int | None = None,
+ status: str | None = None,
+ uploader: list[str] | None = None,
+ benchmark_suite: int | None = None,
+) -> pd.DataFrame:
+ """
+ Return a list of all studies which are on OpenML.
+
+ Parameters
+ ----------
+ offset : int, optional
+ The number of studies to skip, starting from the first.
+ size : int, optional
+ The maximum number of studies to show.
+ status : str, optional
+ Should be {active, in_preparation, deactivated, all}. By default active
+ studies are returned.
+ uploader : list (int), optional
+ Result filter. Will only return studies created by these users.
+ benchmark_suite : int, optional
+
+ Returns
+ -------
+ datasets : dataframe
+ Every dataset is represented by a dictionary containing
+ the following information:
+ - id
+ - alias (optional)
+ - name
+ - benchmark_suite (optional)
+ - status
+ - creator
+ - creation_date
+ If qualities are calculated for the dataset, some of
+ these are also returned.
+ """
+ listing_call = partial(
+ _list_studies,
+ main_entity_type="run",
+ status=status,
+ uploader=uploader,
+ benchmark_suite=benchmark_suite,
+ )
+ batches = openml.utils._list_all(listing_call, offset=offset, limit=size)
+ if len(batches) == 0:
+ return pd.DataFrame()
+
+ return pd.concat(batches)
+
+
+def _list_studies(limit: int, offset: int, **kwargs: Any) -> pd.DataFrame:
+ """Perform api call to return a list of studies.
+
+ Parameters
+ ----------
+ limit: int
+ The maximum number of studies to return.
+ offset: int
+ The number of studies to skip, starting from the first.
+ kwargs : dict, optional
+ Legal filter operators (keys in the dict):
+ status, main_entity_type, uploader, benchmark_suite
+
+ Returns
+ -------
+ studies : dataframe
+ """
+ api_call = "study/list"
+ if limit is not None:
+ api_call += f"/limit/{limit}"
+ if offset is not None:
+ api_call += f"/offset/{offset}"
+ if kwargs is not None:
+ for operator, value in kwargs.items():
+ if value is not None:
+ api_call += f"/{operator}/{value}"
+ return __list_studies(api_call=api_call)
+
+
+def __list_studies(api_call: str) -> pd.DataFrame:
+ """Retrieves the list of OpenML studies and
+ returns it in a dictionary or a Pandas DataFrame.
+
+ Parameters
+ ----------
+ api_call : str
+ The API call for retrieving the list of OpenML studies.
+
+ Returns
+ -------
+ pd.DataFrame
+ A Pandas DataFrame of OpenML studies
+ """
+ xml_string = openml._api_calls._perform_api_call(api_call, "get")
+ study_dict = xmltodict.parse(xml_string, force_list=("oml:study",))
+
+ # Minimalistic check if the XML is useful
+ assert isinstance(study_dict["oml:study_list"]["oml:study"], list), type(
+ study_dict["oml:study_list"],
+ )
+ assert study_dict["oml:study_list"]["@xmlns:oml"] == "http://openml.org/openml", study_dict[
+ "oml:study_list"
+ ]["@xmlns:oml"]
+
+ studies = {}
+ for study_ in study_dict["oml:study_list"]["oml:study"]:
+ # maps from xml name to a tuple of (dict name, casting fn)
+ expected_fields = {
+ "oml:id": ("id", int),
+ "oml:alias": ("alias", str),
+ "oml:main_entity_type": ("main_entity_type", str),
+ "oml:benchmark_suite": ("benchmark_suite", int),
+ "oml:name": ("name", str),
+ "oml:status": ("status", str),
+ "oml:creation_date": ("creation_date", str),
+ "oml:creator": ("creator", int),
+ }
+ study_id = int(study_["oml:id"])
+ current_study = {}
+ for oml_field_name, (real_field_name, cast_fn) in expected_fields.items():
+ if oml_field_name in study_:
+ current_study[real_field_name] = cast_fn(study_[oml_field_name])
+ current_study["id"] = int(current_study["id"])
+ studies[study_id] = current_study
+
+ return pd.DataFrame.from_dict(studies, orient="index")
diff --git a/openml/study/study.py b/openml/study/study.py
new file mode 100644
index 000000000..7a9c80bbe
--- /dev/null
+++ b/openml/study/study.py
@@ -0,0 +1,345 @@
+# License: BSD 3-Clause
+# TODO(eddiebergman): Begging for dataclassses to shorten this all
+from __future__ import annotations
+
+from collections.abc import Sequence
+from typing import Any
+
+from openml.base import OpenMLBase
+from openml.config import get_server_base_url
+
+
+class BaseStudy(OpenMLBase):
+ """
+ An OpenMLStudy represents the OpenML concept of a study. It contains
+ the following information: name, id, description, creation date,
+ creator id and a set of tags.
+
+ According to this list of tags, the study object receives a list of
+ OpenML object ids (datasets, flows, tasks and setups).
+
+ Can be used to obtain all relevant information from a study at once.
+
+ Parameters
+ ----------
+ study_id : int
+ the study id
+ alias : str (optional)
+ a string ID, unique on server (url-friendly)
+ main_entity_type : str
+ the entity type (e.g., task, run) that is core in this study.
+ only entities of this type can be added explicitly
+ benchmark_suite : int (optional)
+ the benchmark suite (another study) upon which this study is ran.
+ can only be active if main entity type is runs.
+ name : str
+ the name of the study (meta-info)
+ description : str
+ brief description (meta-info)
+ status : str
+ Whether the study is in preparation, active or deactivated
+ creation_date : str
+ date of creation (meta-info)
+ creator : int
+ openml user id of the owner / creator
+ tags : list(dict)
+ The list of tags shows which tags are associated with the study.
+ Each tag is a dict of (tag) name, window_start and write_access.
+ data : list
+ a list of data ids associated with this study
+ tasks : list
+ a list of task ids associated with this study
+ flows : list
+ a list of flow ids associated with this study
+ runs : list
+ a list of run ids associated with this study
+ setups : list
+ a list of setup ids associated with this study
+ """
+
+ def __init__( # noqa: PLR0913
+ self,
+ study_id: int | None,
+ alias: str | None,
+ main_entity_type: str,
+ benchmark_suite: int | None,
+ name: str,
+ description: str,
+ status: str | None,
+ creation_date: str | None,
+ creator: int | None,
+ tags: list[dict] | None,
+ data: list[int] | None,
+ tasks: list[int] | None,
+ flows: list[int] | None,
+ runs: list[int] | None,
+ setups: list[int] | None,
+ ):
+ self.study_id = study_id
+ self.alias = alias
+ self.main_entity_type = main_entity_type
+ self.benchmark_suite = benchmark_suite
+ self.name = name
+ self.description = description
+ self.status = status
+ self.creation_date = creation_date
+ self.creator = creator
+ self.tags = tags # LEGACY. Can be removed soon
+ self.data = data
+ self.tasks = tasks
+ self.flows = flows
+ self.setups = setups
+ self.runs = runs
+
+ @classmethod
+ def _entity_letter(cls) -> str:
+ return "s"
+
+ @property
+ def id(self) -> int | None:
+ """Return the id of the study."""
+ return self.study_id
+
+ def _get_repr_body_fields(self) -> Sequence[tuple[str, str | int | list[str]]]:
+ """Collect all information to display in the __repr__ body."""
+ fields: dict[str, Any] = {
+ "Name": self.name,
+ "Status": self.status,
+ "Main Entity Type": self.main_entity_type,
+ }
+ if self.study_id is not None:
+ fields["ID"] = self.study_id
+ fields["Study URL"] = self.openml_url
+ if self.creator is not None:
+ fields["Creator"] = f"{get_server_base_url()}/u/{self.creator}"
+ if self.creation_date is not None:
+ fields["Upload Time"] = self.creation_date.replace("T", " ")
+ if self.data is not None:
+ fields["# of Data"] = len(self.data)
+ if self.tasks is not None:
+ fields["# of Tasks"] = len(self.tasks)
+ if self.flows is not None:
+ fields["# of Flows"] = len(self.flows)
+ if self.runs is not None:
+ fields["# of Runs"] = len(self.runs)
+
+ # determines the order in which the information will be printed
+ order = [
+ "ID",
+ "Name",
+ "Status",
+ "Main Entity Type",
+ "Study URL",
+ "# of Data",
+ "# of Tasks",
+ "# of Flows",
+ "# of Runs",
+ "Creator",
+ "Upload Time",
+ ]
+ return [(key, fields[key]) for key in order if key in fields]
+
+ def _parse_publish_response(self, xml_response: dict) -> None:
+ """Parse the id from the xml_response and assign it to self."""
+ self.study_id = int(xml_response["oml:study_upload"]["oml:id"])
+
+ def _to_dict(self) -> dict[str, dict]:
+ """Creates a dictionary representation of self."""
+ # some can not be uploaded, e.g., id, creator, creation_date
+ simple_props = ["alias", "main_entity_type", "name", "description"]
+
+ # TODO(eddiebergman): Begging for a walrus if we can drop 3.7
+ simple_prop_values = {}
+ for prop_name in simple_props:
+ content = getattr(self, prop_name, None)
+ if content is not None:
+ simple_prop_values["oml:" + prop_name] = content
+
+ # maps from attribute name (which is used as outer tag name) to immer
+ # tag name e.g., self.tasks -> 1987
+ complex_props = {"tasks": "task_id", "runs": "run_id"}
+
+ # TODO(eddiebergman): Begging for a walrus if we can drop 3.7
+ complex_prop_values = {}
+ for prop_name, inner_name in complex_props.items():
+ content = getattr(self, prop_name, None)
+ if content is not None:
+ complex_prop_values["oml:" + prop_name] = {"oml:" + inner_name: content}
+
+ return {
+ "oml:study": {
+ "@xmlns:oml": "http://openml.org/openml",
+ **simple_prop_values,
+ **complex_prop_values,
+ }
+ }
+
+ def push_tag(self, tag: str) -> None:
+ """Add a tag to the study."""
+ raise NotImplementedError(
+ "Tag management for studies is not yet supported. "
+ "The OpenML Python SDK does not currently provide functionality"
+ "for adding tags to studies."
+ "For updates on this feature, please refer to the GitHub issues at: "
+ "https://github.com/openml/openml-python/issues"
+ )
+
+ def remove_tag(self, tag: str) -> None:
+ """Remove a tag from the study."""
+ raise NotImplementedError(
+ "Tag management for studies is not yet supported. "
+ "The OpenML Python SDK does not currently provide functionality"
+ "for removing tags from studies. "
+ "For updates on this feature, please refer to the GitHub issues at: "
+ "https://github.com/openml/openml-python/issues"
+ )
+
+
+class OpenMLStudy(BaseStudy):
+ """
+ An OpenMLStudy represents the OpenML concept of a study (a collection of runs).
+
+ It contains the following information: name, id, description, creation date,
+ creator id and a list of run ids.
+
+ According to this list of run ids, the study object receives a list of
+ OpenML object ids (datasets, flows, tasks and setups).
+
+ Parameters
+ ----------
+ study_id : int
+ the study id
+ alias : str (optional)
+ a string ID, unique on server (url-friendly)
+ benchmark_suite : int (optional)
+ the benchmark suite (another study) upon which this study is ran.
+ can only be active if main entity type is runs.
+ name : str
+ the name of the study (meta-info)
+ description : str
+ brief description (meta-info)
+ status : str
+ Whether the study is in preparation, active or deactivated
+ creation_date : str
+ date of creation (meta-info)
+ creator : int
+ openml user id of the owner / creator
+ tags : list(dict)
+ The list of tags shows which tags are associated with the study.
+ Each tag is a dict of (tag) name, window_start and write_access.
+ data : list
+ a list of data ids associated with this study
+ tasks : list
+ a list of task ids associated with this study
+ flows : list
+ a list of flow ids associated with this study
+ runs : list
+ a list of run ids associated with this study
+ setups : list
+ a list of setup ids associated with this study
+ """
+
+ def __init__( # noqa: PLR0913
+ self,
+ study_id: int | None,
+ alias: str | None,
+ benchmark_suite: int | None,
+ name: str,
+ description: str,
+ status: str | None,
+ creation_date: str | None,
+ creator: int | None,
+ tags: list[dict] | None,
+ data: list[int] | None,
+ tasks: list[int] | None,
+ flows: list[int] | None,
+ runs: list[int] | None,
+ setups: list[int] | None,
+ ):
+ super().__init__(
+ study_id=study_id,
+ alias=alias,
+ main_entity_type="run",
+ benchmark_suite=benchmark_suite,
+ name=name,
+ description=description,
+ status=status,
+ creation_date=creation_date,
+ creator=creator,
+ tags=tags,
+ data=data,
+ tasks=tasks,
+ flows=flows,
+ runs=runs,
+ setups=setups,
+ )
+
+
+class OpenMLBenchmarkSuite(BaseStudy):
+ """
+ An OpenMLBenchmarkSuite represents the OpenML concept of a suite (a collection of tasks).
+
+ It contains the following information: name, id, description, creation date,
+ creator id and the task ids.
+
+ According to this list of task ids, the suite object receives a list of
+ OpenML object ids (datasets).
+
+ Parameters
+ ----------
+ suite_id : int
+ the study id
+ alias : str (optional)
+ a string ID, unique on server (url-friendly)
+ main_entity_type : str
+ the entity type (e.g., task, run) that is core in this study.
+ only entities of this type can be added explicitly
+ name : str
+ the name of the study (meta-info)
+ description : str
+ brief description (meta-info)
+ status : str
+ Whether the study is in preparation, active or deactivated
+ creation_date : str
+ date of creation (meta-info)
+ creator : int
+ openml user id of the owner / creator
+ tags : list(dict)
+ The list of tags shows which tags are associated with the study.
+ Each tag is a dict of (tag) name, window_start and write_access.
+ data : list
+ a list of data ids associated with this study
+ tasks : list
+ a list of task ids associated with this study
+ """
+
+ def __init__( # noqa: PLR0913
+ self,
+ suite_id: int | None,
+ alias: str | None,
+ name: str,
+ description: str,
+ status: str | None,
+ creation_date: str | None,
+ creator: int | None,
+ tags: list[dict] | None,
+ data: list[int] | None,
+ tasks: list[int] | None,
+ ):
+ super().__init__(
+ study_id=suite_id,
+ alias=alias,
+ main_entity_type="task",
+ benchmark_suite=None,
+ name=name,
+ description=description,
+ status=status,
+ creation_date=creation_date,
+ creator=creator,
+ tags=tags,
+ data=data,
+ tasks=tasks,
+ flows=None,
+ runs=None,
+ setups=None,
+ )
diff --git a/openml/tasks/__init__.py b/openml/tasks/__init__.py
index 26e82ac07..34c994e3a 100644
--- a/openml/tasks/__init__.py
+++ b/openml/tasks/__init__.py
@@ -1,5 +1,35 @@
-from .task import OpenMLTask
+# License: BSD 3-Clause
+
+from .functions import (
+ create_task,
+ delete_task,
+ get_task,
+ get_tasks,
+ list_tasks,
+)
from .split import OpenMLSplit
-from .functions import (get_task, list_tasks)
+from .task import (
+ OpenMLClassificationTask,
+ OpenMLClusteringTask,
+ OpenMLLearningCurveTask,
+ OpenMLRegressionTask,
+ OpenMLSupervisedTask,
+ OpenMLTask,
+ TaskType,
+)
-__all__ = ['OpenMLTask', 'get_task', 'list_tasks', 'OpenMLSplit']
+__all__ = [
+ "OpenMLClassificationTask",
+ "OpenMLClusteringTask",
+ "OpenMLLearningCurveTask",
+ "OpenMLRegressionTask",
+ "OpenMLSplit",
+ "OpenMLSupervisedTask",
+ "OpenMLTask",
+ "TaskType",
+ "create_task",
+ "delete_task",
+ "get_task",
+ "get_tasks",
+ "list_tasks",
+]
diff --git a/openml/tasks/functions.py b/openml/tasks/functions.py
index c363c32bb..3fbc7adee 100644
--- a/openml/tasks/functions.py
+++ b/openml/tasks/functions.py
@@ -1,259 +1,632 @@
-import io
+# License: BSD 3-Clause
+from __future__ import annotations
+
import os
import re
-from collections import OrderedDict
+import warnings
+from functools import partial
+from typing import Any
+
+import pandas as pd
import xmltodict
-from ..util import URLError
-from ..exceptions import OpenMLCacheException
-from .. import datasets
-from .task import OpenMLTask, _create_task_cache_dir
-from .. import config
-from .._api_calls import _perform_api_call
+import openml._api_calls
+import openml.utils
+from openml.datasets import get_dataset
+from openml.exceptions import OpenMLCacheException
+from .task import (
+ OpenMLClassificationTask,
+ OpenMLClusteringTask,
+ OpenMLLearningCurveTask,
+ OpenMLRegressionTask,
+ OpenMLSupervisedTask,
+ OpenMLTask,
+ TaskType,
+)
-def _get_cached_tasks():
- tasks = OrderedDict()
- for cache_dir in [config.get_cache_directory(), config.get_private_directory()]:
+TASKS_CACHE_DIR_NAME = "tasks"
- task_cache_dir = os.path.join(cache_dir, "tasks")
- directory_content = os.listdir(task_cache_dir)
- directory_content.sort()
- # Find all dataset ids for which we have downloaded the dataset
- # description
+def _get_cached_tasks() -> dict[int, OpenMLTask]:
+ """Return a dict of all the tasks which are cached locally.
- for filename in directory_content:
- if not re.match(r"[0-9]*", filename):
- continue
+ Returns
+ -------
+ tasks : OrderedDict
+ A dict of all the cached tasks. Each task is an instance of
+ OpenMLTask.
+ """
+ task_cache_dir = openml.utils._create_cache_directory(TASKS_CACHE_DIR_NAME)
+ directory_content = os.listdir(task_cache_dir) # noqa: PTH208
+ directory_content.sort()
- tid = int(filename)
- tasks[tid] = _get_cached_task(tid)
+ # Find all dataset ids for which we have downloaded the dataset
+ # description
+ tids = (int(did) for did in directory_content if re.match(r"[0-9]*", did))
+ return {tid: _get_cached_task(tid) for tid in tids}
- return tasks
+def _get_cached_task(tid: int) -> OpenMLTask:
+ """Return a cached task based on the given id.
-def _get_cached_task(tid):
- for cache_dir in [config.get_cache_directory(), config.get_private_directory()]:
- task_cache_dir = os.path.join(cache_dir, "tasks")
- task_file = os.path.join(task_cache_dir, str(tid), "task.xml")
+ Parameters
+ ----------
+ tid : int
+ Id of the task.
- try:
- with io.open(task_file, encoding='utf8') as fh:
- task = _create_task_from_xml(xml=fh.read())
- return task
- except (OSError, IOError):
- continue
+ Returns
+ -------
+ OpenMLTask
+ """
+ tid_cache_dir = openml.utils._create_cache_directory_for_id(TASKS_CACHE_DIR_NAME, tid)
- raise OpenMLCacheException("Task file for tid %d not "
- "cached" % tid)
+ task_xml_path = tid_cache_dir / "task.xml"
+ try:
+ with task_xml_path.open(encoding="utf8") as fh:
+ return _create_task_from_xml(fh.read())
+ except OSError as e:
+ openml.utils._remove_cache_dir_for_id(TASKS_CACHE_DIR_NAME, tid_cache_dir)
+ raise OpenMLCacheException(f"Task file for tid {tid} not cached") from e
-def _get_estimation_procedure_list():
+def _get_estimation_procedure_list() -> list[dict[str, Any]]:
"""Return a list of all estimation procedures which are on OpenML.
Returns
-------
procedures : list
- A list of all estimation procedures. Every procedure is represented by a
- dictionary containing the following information: id,
- task type id, name, type, repeats, folds, stratified.
+ A list of all estimation procedures. Every procedure is represented by
+ a dictionary containing the following information: id, task type id,
+ name, type, repeats, folds, stratified.
"""
+ url_suffix = "estimationprocedure/list"
+ xml_string = openml._api_calls._perform_api_call(url_suffix, "get")
- return_code, xml_string = _perform_api_call(
- "estimationprocedure/list")
procs_dict = xmltodict.parse(xml_string)
# Minimalistic check if the XML is useful
- if 'oml:estimationprocedures' not in procs_dict:
- raise ValueError('Error in return XML, does not contain tag '
- 'oml:estimationprocedures.')
- elif '@xmlns:oml' not in procs_dict['oml:estimationprocedures']:
- raise ValueError('Error in return XML, does not contain tag '
- '@xmlns:oml as a child of oml:estimationprocedures.')
- elif procs_dict['oml:estimationprocedures']['@xmlns:oml'] != \
- 'http://openml.org/openml':
- raise ValueError('Error in return XML, value of '
- 'oml:estimationprocedures/@xmlns:oml is not '
- 'http://openml.org/openml, but %s' %
- str(procs_dict['oml:estimationprocedures']['@xmlns:oml']))
-
- procs = []
- for proc_ in procs_dict['oml:estimationprocedures']['oml:estimationprocedure']:
- proc = {'id': int(proc_['oml:id']),
- 'task_type_id': int(proc_['oml:ttid']),
- 'name': proc_['oml:name'],
- 'type': proc_['oml:type']}
-
- procs.append(proc)
+ if "oml:estimationprocedures" not in procs_dict:
+ raise ValueError("Error in return XML, does not contain tag oml:estimationprocedures.")
+
+ if "@xmlns:oml" not in procs_dict["oml:estimationprocedures"]:
+ raise ValueError(
+ "Error in return XML, does not contain tag "
+ "@xmlns:oml as a child of oml:estimationprocedures.",
+ )
+
+ if procs_dict["oml:estimationprocedures"]["@xmlns:oml"] != "http://openml.org/openml":
+ raise ValueError(
+ "Error in return XML, value of "
+ "oml:estimationprocedures/@xmlns:oml is not "
+ "http://openml.org/openml, but {}".format(
+ str(procs_dict["oml:estimationprocedures"]["@xmlns:oml"])
+ ),
+ )
+
+ procs: list[dict[str, Any]] = []
+ for proc_ in procs_dict["oml:estimationprocedures"]["oml:estimationprocedure"]:
+ task_type_int = int(proc_["oml:ttid"])
+ try:
+ task_type_id = TaskType(task_type_int)
+ procs.append(
+ {
+ "id": int(proc_["oml:id"]),
+ "task_type_id": task_type_id,
+ "name": proc_["oml:name"],
+ "type": proc_["oml:type"],
+ },
+ )
+ except ValueError as e:
+ warnings.warn(
+ f"Could not create task type id for {task_type_int} due to error {e}",
+ RuntimeWarning,
+ stacklevel=2,
+ )
return procs
-def list_tasks(task_type_id=None, offset=None, size=None, tag=None):
- """Return a number of tasks having the given tag and task_type_id
+def list_tasks( # noqa: PLR0913
+ task_type: TaskType | None = None,
+ offset: int | None = None,
+ size: int | None = None,
+ tag: str | None = None,
+ data_tag: str | None = None,
+ status: str | None = None,
+ data_name: str | None = None,
+ data_id: int | None = None,
+ number_instances: int | None = None,
+ number_features: int | None = None,
+ number_classes: int | None = None,
+ number_missing_values: int | None = None,
+) -> pd.DataFrame:
+ """
+ Return a number of tasks having the given tag and task_type
Parameters
----------
- task_type_id : int, optional
- ID of the task type as detailed
- `here `_.
+ Filter task_type is separated from the other filters because
+ it is used as task_type in the task description, but it is named
+ type when used as a filter in list tasks call.
offset : int, optional
the number of tasks to skip, starting from the first
+ task_type : TaskType, optional
+ Refers to the type of task.
size : int, optional
the maximum number of tasks to show
tag : str, optional
the tag to include
+ data_tag : str, optional
+ the tag of the dataset
+ data_id : int, optional
+ status : str, optional
+ data_name : str, optional
+ number_instances : int, optional
+ number_features : int, optional
+ number_classes : int, optional
+ number_missing_values : int, optional
Returns
-------
- list
- A list of all tasks having the given task_type_id and the give tag.
- Every task is represented by a dictionary containing the following
- information: task id, dataset id, task_type and status. If qualities
- are calculated for the associated dataset, some of these are also
- returned.
+ dataframe
+ All tasks having the given task_type and the give tag. Every task is
+ represented by a row in the data frame containing the following information
+ as columns: task id, dataset id, task_type and status. If qualities are
+ calculated for the associated dataset, some of these are also returned.
"""
- api_call = "task/list"
- if task_type_id is not None:
- api_call += "/type/%d" % int(task_type_id)
+ listing_call = partial(
+ _list_tasks,
+ task_type=task_type,
+ tag=tag,
+ data_tag=data_tag,
+ status=status,
+ data_id=data_id,
+ data_name=data_name,
+ number_instances=number_instances,
+ number_features=number_features,
+ number_classes=number_classes,
+ number_missing_values=number_missing_values,
+ )
+ batches = openml.utils._list_all(listing_call, offset=offset, limit=size)
+ if len(batches) == 0:
+ return pd.DataFrame()
+
+ return pd.concat(batches)
+
+
+def _list_tasks(
+ limit: int,
+ offset: int,
+ task_type: TaskType | int | None = None,
+ **kwargs: Any,
+) -> pd.DataFrame:
+ """
+ Perform the api call to return a number of tasks having the given filters.
+
+ Parameters
+ ----------
+ Filter task_type is separated from the other filters because
+ it is used as task_type in the task description, but it is named
+ type when used as a filter in list tasks call.
+ limit: int
+ offset: int
+ task_type : TaskType, optional
+ Refers to the type of task.
+ kwargs: dict, optional
+ Legal filter operators: tag, task_id (list), data_tag, status, limit,
+ offset, data_id, data_name, number_instances, number_features,
+ number_classes, number_missing_values.
+ Returns
+ -------
+ dataframe
+ """
+ api_call = "task/list"
+ if limit is not None:
+ api_call += f"/limit/{limit}"
if offset is not None:
- api_call += "/offset/%d" % int(offset)
+ api_call += f"/offset/{offset}"
+ if task_type is not None:
+ tvalue = task_type.value if isinstance(task_type, TaskType) else task_type
+ api_call += f"/type/{tvalue}"
+ if kwargs is not None:
+ for operator, value in kwargs.items():
+ if value is not None:
+ if operator == "task_id":
+ value = ",".join([str(int(i)) for i in value]) # noqa: PLW2901
+ api_call += f"/{operator}/{value}"
- if size is not None:
- api_call += "/limit/%d" % int(size)
+ return __list_tasks(api_call=api_call)
- if tag is not None:
- api_call += "/tag/%s" % tag
- return _list_tasks(api_call)
+def __list_tasks(api_call: str) -> pd.DataFrame: # noqa: C901, PLR0912
+ """Returns a Pandas DataFrame with information about OpenML tasks.
+ Parameters
+ ----------
+ api_call : str
+ The API call specifying which tasks to return.
-def _list_tasks(api_call):
- return_code, xml_string = _perform_api_call(api_call)
- with open('/tmp/list_tasks.xml', 'w') as fh:
- fh.write(xml_string)
- tasks_dict = xmltodict.parse(xml_string)
+ Returns
+ -------
+ A Pandas DataFrame with information about OpenML tasks.
+
+ Raises
+ ------
+ ValueError
+ If the XML returned by the OpenML API does not contain 'oml:tasks', '@xmlns:oml',
+ or has an incorrect value for '@xmlns:oml'.
+ KeyError
+ If an invalid key is found in the XML for a task.
+ """
+ xml_string = openml._api_calls._perform_api_call(api_call, "get")
+ tasks_dict = xmltodict.parse(xml_string, force_list=("oml:task", "oml:input"))
# Minimalistic check if the XML is useful
- if 'oml:tasks' not in tasks_dict:
- raise ValueError('Error in return XML, does not contain "oml:runs": %s'
- % str(tasks_dict))
- elif '@xmlns:oml' not in tasks_dict['oml:tasks']:
- raise ValueError('Error in return XML, does not contain '
- '"oml:runs"/@xmlns:oml: %s'
- % str(tasks_dict))
- elif tasks_dict['oml:tasks']['@xmlns:oml'] != 'http://openml.org/openml':
- raise ValueError('Error in return XML, value of '
- '"oml:runs"/@xmlns:oml is not '
- '"http://openml.org/openml": %s'
- % str(tasks_dict))
+ if "oml:tasks" not in tasks_dict:
+ raise ValueError(f'Error in return XML, does not contain "oml:runs": {tasks_dict}')
- try:
- tasks = dict();
- procs = _get_estimation_procedure_list()
- proc_dict = dict((x['id'], x) for x in procs)
- for task_ in tasks_dict['oml:tasks']['oml:task']:
- tid = int(task_['oml:task_id'])
- task = {'tid': tid,
- 'ttid': int(task_['oml:task_type_id']),
- 'did': int(task_['oml:did']),
- 'name': task_['oml:name'],
- 'task_type': task_['oml:task_type'],
- 'status': task_['oml:status']}
+ if "@xmlns:oml" not in tasks_dict["oml:tasks"]:
+ raise ValueError(
+ f'Error in return XML, does not contain "oml:runs"/@xmlns:oml: {tasks_dict}'
+ )
+
+ if tasks_dict["oml:tasks"]["@xmlns:oml"] != "http://openml.org/openml":
+ raise ValueError(
+ "Error in return XML, value of "
+ '"oml:runs"/@xmlns:oml is not '
+ f'"http://openml.org/openml": {tasks_dict!s}',
+ )
+
+ assert isinstance(tasks_dict["oml:tasks"]["oml:task"], list), type(tasks_dict["oml:tasks"])
+
+ tasks = {}
+ procs = _get_estimation_procedure_list()
+ proc_dict = {x["id"]: x for x in procs}
+
+ for task_ in tasks_dict["oml:tasks"]["oml:task"]:
+ tid = None
+ try:
+ tid = int(task_["oml:task_id"])
+ task_type_int = int(task_["oml:task_type_id"])
+ try:
+ task_type_id = TaskType(task_type_int)
+ except ValueError as e:
+ warnings.warn(
+ f"Could not create task type id for {task_type_int} due to error {e}",
+ RuntimeWarning,
+ stacklevel=2,
+ )
+ continue
+
+ task = {
+ "tid": tid,
+ "ttid": task_type_id,
+ "did": int(task_["oml:did"]),
+ "name": task_["oml:name"],
+ "task_type": task_["oml:task_type"],
+ "status": task_["oml:status"],
+ }
# Other task inputs
- for input in task_.get('oml:input', list()):
- if input['@name'] == 'estimation_procedure':
- task[input['@name']] = proc_dict[int(input['#text'])]['name']
+ for _input in task_.get("oml:input", []):
+ if _input["@name"] == "estimation_procedure":
+ task[_input["@name"]] = proc_dict[int(_input["#text"])]["name"]
else:
- value = input.get('#text')
- task[input['@name']] = value
+ value = _input.get("#text")
+ task[_input["@name"]] = value
# The number of qualities can range from 0 to infinity
- for quality in task_.get('oml:quality', list()):
- quality['#text'] = float(quality['#text'])
- if abs(int(quality['#text']) - quality['#text']) < 0.0000001:
- quality['#text'] = int(quality['#text'])
- task[quality['@name']] = quality['#text']
+ for quality in task_.get("oml:quality", []):
+ if "#text" not in quality:
+ quality_value = 0.0
+ else:
+ quality["#text"] = float(quality["#text"])
+ if abs(int(quality["#text"]) - quality["#text"]) < 0.0000001:
+ quality["#text"] = int(quality["#text"])
+ quality_value = quality["#text"]
+ task[quality["@name"]] = quality_value
tasks[tid] = task
- except KeyError as e:
- raise KeyError("Invalid xml for task: %s" % e)
+ except KeyError as e:
+ if tid is not None:
+ warnings.warn(
+ f"Invalid xml for task {tid}: {e}\nFrom {task_}",
+ RuntimeWarning,
+ stacklevel=2,
+ )
+ else:
+ warnings.warn(f"Could not find key {e} in {task_}!", RuntimeWarning, stacklevel=2)
+
+ return pd.DataFrame.from_dict(tasks, orient="index")
+
+
+def get_tasks(
+ task_ids: list[int],
+ download_data: bool | None = None,
+ download_qualities: bool | None = None,
+) -> list[OpenMLTask]:
+ """Download tasks.
+ This function iterates :meth:`openml.tasks.get_task`.
+
+ Parameters
+ ----------
+ task_ids : List[int]
+ A list of task ids to download.
+ download_data : bool (default = True)
+ Option to trigger download of data along with the meta data.
+ download_qualities : bool (default=True)
+ Option to download 'qualities' meta-data in addition to the minimal dataset description.
+
+ Returns
+ -------
+ list
+ """
+ if download_data is None:
+ warnings.warn(
+ "`download_data` will default to False starting in 0.16. "
+ "Please set `download_data` explicitly to suppress this warning.",
+ stacklevel=1,
+ )
+ download_data = True
+
+ if download_qualities is None:
+ warnings.warn(
+ "`download_qualities` will default to False starting in 0.16. "
+ "Please set `download_qualities` explicitly to suppress this warning.",
+ stacklevel=1,
+ )
+ download_qualities = True
+
+ tasks = []
+ for task_id in task_ids:
+ tasks.append(
+ get_task(task_id, download_data=download_data, download_qualities=download_qualities)
+ )
return tasks
-def get_task(task_id):
- """Download the OpenML task for a given task ID.
+@openml.utils.thread_safe_if_oslo_installed
+def get_task(
+ task_id: int,
+ download_splits: bool = False, # noqa: FBT002
+ **get_dataset_kwargs: Any,
+) -> OpenMLTask:
+ """Download OpenML task for a given task ID.
+
+ Downloads the task representation.
+
+ Use the `download_splits` parameter to control whether the splits are downloaded.
+ Moreover, you may pass additional parameter (args or kwargs) that are passed to
+ :meth:`openml.datasets.get_dataset`.
Parameters
----------
task_id : int
- The OpenML task id.
+ The OpenML task id of the task to download.
+ download_splits: bool (default=False)
+ Whether to download the splits as well.
+ get_dataset_kwargs :
+ Args and kwargs can be used pass optional parameters to :meth:`openml.datasets.get_dataset`.
+
+ Returns
+ -------
+ task: OpenMLTask
"""
+ if not isinstance(task_id, int):
+ raise TypeError(f"Task id should be integer, is {type(task_id)}")
+
+ task_cache_directory = openml.utils._create_cache_directory_for_id(
+ TASKS_CACHE_DIR_NAME, task_id
+ )
+ task_cache_directory_existed = task_cache_directory.exists()
try:
- task_id = int(task_id)
- except:
- raise ValueError("Task ID is neither an Integer nor can be "
- "cast to an Integer.")
+ task = _get_task_description(task_id)
+ dataset = get_dataset(task.dataset_id, **get_dataset_kwargs)
+ # List of class labels available in dataset description
+ # Including class labels as part of task meta data handles
+ # the case where data download was initially disabled
+ if isinstance(task, (OpenMLClassificationTask, OpenMLLearningCurveTask)):
+ assert task.target_name is not None, (
+ "Supervised tasks must define a target feature before retrieving class labels."
+ )
+ task.class_labels = dataset.retrieve_class_labels(task.target_name)
+ # Clustering tasks do not have class labels
+ # and do not offer download_split
+ if download_splits and isinstance(task, OpenMLSupervisedTask):
+ task.download_split()
+ except Exception as e:
+ if not task_cache_directory_existed:
+ openml.utils._remove_cache_dir_for_id(TASKS_CACHE_DIR_NAME, task_cache_directory)
+ raise e
- xml_file = os.path.join(_create_task_cache_dir(task_id),
- "task.xml")
+ return task
- try:
- with io.open(xml_file, encoding='utf8') as fh:
- task = _create_task_from_xml(fh.read())
- except (OSError, IOError):
- try:
- return_code, task_xml = _perform_api_call(
- "task/%d" % task_id)
- except (URLError, UnicodeEncodeError) as e:
- print(e)
- raise e
+def _get_task_description(task_id: int) -> OpenMLTask:
+ try:
+ return _get_cached_task(task_id)
+ except OpenMLCacheException:
+ _cache_dir = openml.utils._create_cache_directory_for_id(TASKS_CACHE_DIR_NAME, task_id)
+ xml_file = _cache_dir / "task.xml"
+ task_xml = openml._api_calls._perform_api_call(f"task/{task_id}", "get")
- with io.open(xml_file, "w", encoding='utf8') as fh:
+ with xml_file.open("w", encoding="utf8") as fh:
fh.write(task_xml)
+ return _create_task_from_xml(task_xml)
- task = _create_task_from_xml(task_xml)
- # TODO extract this to a function
- task.download_split()
- dataset = datasets.get_dataset(task.dataset_id)
-
- # TODO look into either adding the class labels to task xml, or other
- # way of reading it.
- class_labels = dataset.retrieve_class_labels(task.target_name)
- task.class_labels = class_labels
- return task
+def _create_task_from_xml(xml: str) -> OpenMLTask:
+ """Create a task given a xml string.
+ Parameters
+ ----------
+ xml : string
+ Task xml representation.
-def _create_task_from_xml(xml):
+ Returns
+ -------
+ OpenMLTask
+ """
dic = xmltodict.parse(xml)["oml:task"]
-
- estimation_parameters = dict()
- inputs = dict()
+ estimation_parameters = {}
+ inputs = {}
# Due to the unordered structure we obtain, we first have to extract
# the possible keys of oml:input; dic["oml:input"] is a list of
# OrderedDicts
- for input_ in dic["oml:input"]:
- name = input_["@name"]
- inputs[name] = input_
-
- # Convert some more parameters
- for parameter in \
- inputs["estimation_procedure"]["oml:estimation_procedure"][
- "oml:parameter"]:
- name = parameter["@name"]
- text = parameter.get("#text", "")
- estimation_parameters[name] = text
-
- return OpenMLTask(
- dic["oml:task_id"], dic['oml:task_type_id'], dic["oml:task_type"],
- inputs["source_data"]["oml:data_set"]["oml:data_set_id"],
- inputs["source_data"]["oml:data_set"]["oml:target_feature"],
- inputs["estimation_procedure"]["oml:estimation_procedure"][
- "oml:type"],
- inputs["estimation_procedure"]["oml:estimation_procedure"][
- "oml:data_splits_url"], estimation_parameters,
- inputs["evaluation_measures"]["oml:evaluation_measures"][
- "oml:evaluation_measure"], None)
+
+ # Check if there is a list of inputs
+ if isinstance(dic["oml:input"], list):
+ for input_ in dic["oml:input"]:
+ name = input_["@name"]
+ inputs[name] = input_
+ # Single input case
+ elif isinstance(dic["oml:input"], dict):
+ name = dic["oml:input"]["@name"]
+ inputs[name] = dic["oml:input"]
+
+ evaluation_measures = None
+ if "evaluation_measures" in inputs:
+ evaluation_measures = inputs["evaluation_measures"]["oml:evaluation_measures"][
+ "oml:evaluation_measure"
+ ]
+
+ task_type = TaskType(int(dic["oml:task_type_id"]))
+ common_kwargs = {
+ "task_id": dic["oml:task_id"],
+ "task_type": dic["oml:task_type"],
+ "task_type_id": task_type,
+ "data_set_id": inputs["source_data"]["oml:data_set"]["oml:data_set_id"],
+ "evaluation_measure": evaluation_measures,
+ }
+ # TODO: add OpenMLClusteringTask?
+ if task_type in (
+ TaskType.SUPERVISED_CLASSIFICATION,
+ TaskType.SUPERVISED_REGRESSION,
+ TaskType.LEARNING_CURVE,
+ ):
+ # Convert some more parameters
+ for parameter in inputs["estimation_procedure"]["oml:estimation_procedure"][
+ "oml:parameter"
+ ]:
+ name = parameter["@name"]
+ text = parameter.get("#text", "")
+ estimation_parameters[name] = text
+
+ common_kwargs["estimation_procedure_type"] = inputs["estimation_procedure"][
+ "oml:estimation_procedure"
+ ]["oml:type"]
+ common_kwargs["estimation_procedure_id"] = int(
+ inputs["estimation_procedure"]["oml:estimation_procedure"]["oml:id"]
+ )
+
+ common_kwargs["estimation_parameters"] = estimation_parameters
+ common_kwargs["target_name"] = inputs["source_data"]["oml:data_set"]["oml:target_feature"]
+ common_kwargs["data_splits_url"] = inputs["estimation_procedure"][
+ "oml:estimation_procedure"
+ ]["oml:data_splits_url"]
+
+ cls = {
+ TaskType.SUPERVISED_CLASSIFICATION: OpenMLClassificationTask,
+ TaskType.SUPERVISED_REGRESSION: OpenMLRegressionTask,
+ TaskType.CLUSTERING: OpenMLClusteringTask,
+ TaskType.LEARNING_CURVE: OpenMLLearningCurveTask,
+ }.get(task_type)
+ if cls is None:
+ raise NotImplementedError(
+ f"Task type '{common_kwargs['task_type']}' is not supported. "
+ f"Supported task types: SUPERVISED_CLASSIFICATION,"
+ f"SUPERVISED_REGRESSION, CLUSTERING, LEARNING_CURVE."
+ f"Please check the OpenML documentation for available task types."
+ )
+ return cls(**common_kwargs) # type: ignore
+
+
+# TODO(eddiebergman): overload on `task_type`
+def create_task(
+ task_type: TaskType,
+ dataset_id: int,
+ estimation_procedure_id: int,
+ target_name: str | None = None,
+ evaluation_measure: str | None = None,
+ **kwargs: Any,
+) -> (
+ OpenMLClassificationTask | OpenMLRegressionTask | OpenMLLearningCurveTask | OpenMLClusteringTask
+):
+ """Create a task based on different given attributes.
+
+ Builds a task object with the function arguments as
+ attributes. The type of the task object built is
+ determined from the task type id.
+ More information on how the arguments (task attributes),
+ relate to the different possible tasks can be found in
+ the individual task objects at the openml.tasks.task
+ module.
+
+ Parameters
+ ----------
+ task_type : TaskType
+ Id of the task type.
+ dataset_id : int
+ The id of the dataset for the task.
+ target_name : str, optional
+ The name of the feature used as a target.
+ At the moment, only optional for the clustering tasks.
+ estimation_procedure_id : int
+ The id of the estimation procedure.
+ evaluation_measure : str, optional
+ The name of the evaluation measure.
+ kwargs : dict, optional
+ Other task attributes that are not mandatory
+ for task upload.
+
+ Returns
+ -------
+ OpenMLClassificationTask, OpenMLRegressionTask,
+ OpenMLLearningCurveTask, OpenMLClusteringTask
+ """
+ if task_type == TaskType.CLUSTERING:
+ task_cls = OpenMLClusteringTask
+ elif task_type == TaskType.LEARNING_CURVE:
+ task_cls = OpenMLLearningCurveTask # type: ignore
+ elif task_type == TaskType.SUPERVISED_CLASSIFICATION:
+ task_cls = OpenMLClassificationTask # type: ignore
+ elif task_type == TaskType.SUPERVISED_REGRESSION:
+ task_cls = OpenMLRegressionTask # type: ignore
+ else:
+ raise NotImplementedError(
+ f"Task type ID {task_type:d} is not supported. "
+ f"Supported task type IDs: {TaskType.SUPERVISED_CLASSIFICATION.value},"
+ f"{TaskType.SUPERVISED_REGRESSION.value}, "
+ f"{TaskType.CLUSTERING.value}, {TaskType.LEARNING_CURVE.value}. "
+ f"Please refer to the TaskType enum for valid task type identifiers."
+ )
+
+ return task_cls(
+ task_id=None,
+ task_type_id=task_type,
+ task_type="None", # TODO: refactor to get task type string from ID.
+ data_set_id=dataset_id,
+ target_name=target_name, # type: ignore
+ estimation_procedure_id=estimation_procedure_id,
+ evaluation_measure=evaluation_measure,
+ **kwargs,
+ )
+
+
+def delete_task(task_id: int) -> bool:
+ """Delete task with id `task_id` from the OpenML server.
+
+ You can only delete tasks which you created and have
+ no runs associated with them.
+
+ Parameters
+ ----------
+ task_id : int
+ OpenML id of the task
+
+ Returns
+ -------
+ bool
+ True if the deletion was successful. False otherwise.
+ """
+ return openml.utils._delete_entity("task", task_id)
diff --git a/openml/tasks/split.py b/openml/tasks/split.py
index 0ce427ba9..464e41b2a 100644
--- a/openml/tasks/split.py
+++ b/openml/tasks/split.py
@@ -1,123 +1,192 @@
-from collections import namedtuple, OrderedDict
-import os
-import sys
+# License: BSD 3-Clause
+from __future__ import annotations
+
+import pickle
+from collections import OrderedDict
+from pathlib import Path
+from typing import Any
+from typing_extensions import NamedTuple
+
+import arff # type: ignore
import numpy as np
-import scipy.io.arff
-if sys.version_info[0] > 3:
- import pickle
-else:
- try:
- import cPickle as pickle
- except:
- import pickle
+class Split(NamedTuple):
+ """A single split of a dataset."""
+
+ train: np.ndarray
+ test: np.ndarray
-Split = namedtuple("Split", ["train", "test"])
+class OpenMLSplit: # noqa: PLW1641
+ """OpenML Split object.
-class OpenMLSplit(object):
+ This class manages train-test splits for a dataset across multiple
+ repetitions, folds, and samples.
- def __init__(self, name, description, split):
+ Parameters
+ ----------
+ name : int or str
+ The name or ID of the split.
+ description : str
+ A description of the split.
+ split : dict
+ A dictionary containing the splits organized by repetition, fold,
+ and sample.
+ """
+
+ def __init__(
+ self,
+ name: int | str,
+ description: str,
+ split: dict[int, dict[int, dict[int, tuple[np.ndarray, np.ndarray]]]],
+ ):
self.description = description
self.name = name
- self.split = dict()
+ self.split: dict[int, dict[int, dict[int, tuple[np.ndarray, np.ndarray]]]] = {}
# Add splits according to repetition
for repetition in split:
- repetition = int(repetition)
- self.split[repetition] = OrderedDict()
- for fold in split[repetition]:
- self.split[repetition][fold] = split[repetition][fold]
+ _rep = int(repetition)
+ self.split[_rep] = OrderedDict()
+ for fold in split[_rep]:
+ self.split[_rep][fold] = OrderedDict()
+ for sample in split[_rep][fold]:
+ self.split[_rep][fold][sample] = split[_rep][fold][sample]
self.repeats = len(self.split)
- if any([len(self.split[0]) != len(self.split[i])
- for i in range(self.repeats)]):
- raise ValueError('')
- self.folds = len(self.split[0])
- def __eq__(self, other):
- if type(self) != type(other):
- return False
- elif self.name != other.name:
- return False
- elif self.description != other.description:
- return False
- elif self.split.keys() != other.split.keys():
+ # TODO(eddiebergman): Better error message
+ if any(len(self.split[0]) != len(self.split[i]) for i in range(self.repeats)):
+ raise ValueError("")
+
+ self.folds = len(self.split[0])
+ self.samples = len(self.split[0][0])
+
+ def __eq__(self, other: Any) -> bool:
+ if (
+ (not isinstance(self, type(other)))
+ or self.name != other.name
+ or self.description != other.description
+ or self.split.keys() != other.split.keys()
+ or any(
+ self.split[repetition].keys() != other.split[repetition].keys()
+ for repetition in self.split
+ )
+ ):
return False
- else:
- for repetition in self.split:
- if self.split[repetition].keys() != other.split[repetition].keys():
- return False
- else:
- for fold in self.split[repetition]:
- if np.all(self.split[repetition][fold].test !=
- other.split[repetition][fold].test)\
- and \
- np.all(self.split[repetition][fold].train
- != other.split[repetition][fold].train):
- return False
+
+ samples = [
+ (repetition, fold, sample)
+ for repetition in self.split
+ for fold in self.split[repetition]
+ for sample in self.split[repetition][fold]
+ ]
+
+ for repetition, fold, sample in samples:
+ self_train, self_test = self.split[repetition][fold][sample]
+ other_train, other_test = other.split[repetition][fold][sample]
+ if not (np.all(self_train == other_train) and np.all(self_test == other_test)):
+ return False
return True
@classmethod
- def _from_arff_file(cls, filename, cache=True):
+ def _from_arff_file(cls, filename: Path) -> OpenMLSplit: # noqa: C901, PLR0912
repetitions = None
- pkl_filename = filename.replace(".arff", ".pkl")
- if cache:
- if os.path.exists(pkl_filename):
- with open(pkl_filename, "rb") as fh:
- _ = pickle.load(fh)
- repetitions = _["repetitions"]
- name = _["name"]
+ name = None
+
+ pkl_filename = filename.with_suffix(".pkl.py3")
+
+ if pkl_filename.exists():
+ with pkl_filename.open("rb") as fh:
+ # TODO(eddiebergman): Would be good to figure out what _split is and assert it is
+ _split = pickle.load(fh) # noqa: S301
+ repetitions = _split["repetitions"]
+ name = _split["name"]
# Cache miss
if repetitions is None:
# Faster than liac-arff and sufficient in this situation!
- splits, meta = scipy.io.arff.loadarff(filename)
- name = meta.name
+ if not filename.exists():
+ raise FileNotFoundError(f"Split arff {filename} does not exist!")
+
+ file_data = arff.load(filename.open("r"), return_type=arff.DENSE_GEN)
+ splits = file_data["data"]
+ name = file_data["relation"]
+ attrnames = [attr[0] for attr in file_data["attributes"]]
repetitions = OrderedDict()
+
+ type_idx = attrnames.index("type")
+ rowid_idx = attrnames.index("rowid")
+ repeat_idx = attrnames.index("repeat")
+ fold_idx = attrnames.index("fold")
+ sample_idx = attrnames.index("sample") if "sample" in attrnames else None
+
for line in splits:
# A line looks like type, rowid, repeat, fold
- repetition = int(line[2])
- fold = int(line[3])
+ repetition = int(line[repeat_idx])
+ fold = int(line[fold_idx])
+ sample = 0
+ if sample_idx is not None:
+ sample = int(line[sample_idx])
if repetition not in repetitions:
repetitions[repetition] = OrderedDict()
if fold not in repetitions[repetition]:
- repetitions[repetition][fold] = ([], [])
-
- type_ = line[0].decode('utf-8')
- if type_ == 'TRAIN':
- repetitions[repetition][fold][0].append(line[1])
- elif type_ == 'TEST':
- repetitions[repetition][fold][1].append(line[1])
+ repetitions[repetition][fold] = OrderedDict()
+ if sample not in repetitions[repetition][fold]:
+ repetitions[repetition][fold][sample] = ([], [])
+ split = repetitions[repetition][fold][sample]
+
+ type_ = line[type_idx]
+ if type_ == "TRAIN":
+ split[0].append(line[rowid_idx])
+ elif type_ == "TEST":
+ split[1].append(line[rowid_idx])
else:
raise ValueError(type_)
for repetition in repetitions:
for fold in repetitions[repetition]:
- repetitions[repetition][fold] = Split(
- np.array(repetitions[repetition][fold][0], dtype=np.int32),
- np.array(repetitions[repetition][fold][1], dtype=np.int32))
-
- if cache:
- with open(pkl_filename, "wb") as fh:
- pickle.dump({"name": name, "repetitions": repetitions}, fh,
- protocol=2)
-
- return cls(name, '', repetitions)
-
- def from_dataset(self, X, Y, folds, repeats):
- raise NotImplementedError()
-
- def get(self, repeat=0, fold=0):
+ for sample in repetitions[repetition][fold]:
+ repetitions[repetition][fold][sample] = Split(
+ np.array(repetitions[repetition][fold][sample][0], dtype=np.int32),
+ np.array(repetitions[repetition][fold][sample][1], dtype=np.int32),
+ )
+
+ with pkl_filename.open("wb") as fh:
+ pickle.dump({"name": name, "repetitions": repetitions}, fh, protocol=2)
+
+ assert name is not None
+ return cls(name, "", repetitions)
+
+ def get(self, repeat: int = 0, fold: int = 0, sample: int = 0) -> tuple[np.ndarray, np.ndarray]:
+ """Returns the specified data split from the CrossValidationSplit object.
+
+ Parameters
+ ----------
+ repeat : int
+ Index of the repeat to retrieve.
+ fold : int
+ Index of the fold to retrieve.
+ sample : int
+ Index of the sample to retrieve.
+
+ Returns
+ -------
+ numpy.ndarray
+ The data split for the specified repeat, fold, and sample.
+
+ Raises
+ ------
+ ValueError
+ If the specified repeat, fold, or sample is not known.
+ """
if repeat not in self.split:
- raise ValueError("Repeat %s not known" % str(repeat))
+ raise ValueError(f"Repeat {repeat!s} not known")
if fold not in self.split[repeat]:
- raise ValueError("Fold %s not known" % str(fold))
- return self.split[repeat][fold]
-
- def iterate_splits(self):
- for rep in range(self.repeats):
- yield (self.get(repeat=rep, fold=fold) for fold in range(self.folds))
+ raise ValueError(f"Fold {fold!s} not known")
+ if sample not in self.split[repeat][fold]:
+ raise ValueError(f"Sample {sample!s} not known")
+ return self.split[repeat][fold][sample]
diff --git a/openml/tasks/task.py b/openml/tasks/task.py
index 7fad1b245..385b1f949 100644
--- a/openml/tasks/task.py
+++ b/openml/tasks/task.py
@@ -1,114 +1,529 @@
-import io
-import os
+# License: BSD 3-Clause
+from __future__ import annotations
+
+import warnings
+from abc import ABC
+from collections.abc import Sequence
+from enum import Enum
+from pathlib import Path
+from typing import TYPE_CHECKING, Any, ClassVar
+from typing_extensions import TypedDict
+
+import openml._api_calls
+import openml.config
+from openml import datasets
+from openml.base import OpenMLBase
+from openml.utils import _create_cache_directory_for_id
-from .. import config
-from .. import datasets
-from ..util import URLError
from .split import OpenMLSplit
-from .._api_calls import _read_url
+if TYPE_CHECKING:
+ import numpy as np
+ import pandas as pd
+
+
+# TODO(eddiebergman): Should use `auto()` but might be too late if these numbers are used
+# and stored on server.
+class TaskType(Enum):
+ """Possible task types as defined in OpenML."""
+
+ SUPERVISED_CLASSIFICATION = 1
+ SUPERVISED_REGRESSION = 2
+ LEARNING_CURVE = 3
+ SUPERVISED_DATASTREAM_CLASSIFICATION = 4
+ CLUSTERING = 5
+ MACHINE_LEARNING_CHALLENGE = 6
+ SURVIVAL_ANALYSIS = 7
+ SUBGROUP_DISCOVERY = 8
+ MULTITASK_REGRESSION = 9
+
+
+class _EstimationProcedure(TypedDict):
+ type: str | None
+ parameters: dict[str, str] | None
+ data_splits_url: str | None
+
+
+class OpenMLTask(OpenMLBase):
+ """OpenML Task object.
+
+ Parameters
+ ----------
+ task_id: Union[int, None]
+ Refers to the unique identifier of OpenML task.
+ task_type_id: TaskType
+ Refers to the type of OpenML task.
+ task_type: str
+ Refers to the OpenML task.
+ data_set_id: int
+ Refers to the data.
+ estimation_procedure_id: int
+ Refers to the type of estimates used.
+ estimation_procedure_type: str, default=None
+ Refers to the type of estimation procedure used for the OpenML task.
+ estimation_parameters: [Dict[str, str]], default=None
+ Estimation parameters used for the OpenML task.
+ evaluation_measure: str, default=None
+ Refers to the evaluation measure.
+ data_splits_url: str, default=None
+ Refers to the URL of the data splits used for the OpenML task.
+ """
-class OpenMLTask(object):
- def __init__(self, task_id, task_type_id, task_type, data_set_id,
- target_name, estimation_procedure_type, data_splits_url,
- estimation_parameters, evaluation_measure, cost_matrix,
- class_labels=None):
- self.task_id = int(task_id)
+ DEFAULT_ESTIMATION_PROCEDURE_ID: ClassVar[int] = 1
+
+ def __init__( # noqa: PLR0913
+ self,
+ task_id: int | None,
+ task_type_id: TaskType,
+ task_type: str,
+ data_set_id: int,
+ estimation_procedure_id: int | None = None,
+ estimation_procedure_type: str | None = None,
+ estimation_parameters: dict[str, str] | None = None,
+ evaluation_measure: str | None = None,
+ data_splits_url: str | None = None,
+ target_name: str | None = None,
+ ):
+ self.task_id = int(task_id) if task_id is not None else None
+ self.task_type_id = task_type_id
self.task_type = task_type
self.dataset_id = int(data_set_id)
self.target_name = target_name
- self.estimation_procedure = dict()
- self.estimation_procedure["type"] = estimation_procedure_type
- self.estimation_procedure["data_splits_url"] = data_splits_url
- self.estimation_procedure["parameters"] = estimation_parameters
- #
- self.estimation_parameters = estimation_parameters
+ resolved_estimation_procedure_id = self._resolve_estimation_procedure_id(
+ estimation_procedure_id,
+ )
self.evaluation_measure = evaluation_measure
- self.cost_matrix = cost_matrix
- self.class_labels = class_labels
+ self.estimation_procedure: _EstimationProcedure = {
+ "type": estimation_procedure_type,
+ "parameters": estimation_parameters,
+ "data_splits_url": data_splits_url,
+ }
+ self.estimation_procedure_id = resolved_estimation_procedure_id
+ self.split: OpenMLSplit | None = None
- if cost_matrix is not None:
- raise NotImplementedError("Costmatrix")
+ def _resolve_estimation_procedure_id(self, estimation_procedure_id: int | None) -> int:
+ return (
+ estimation_procedure_id
+ if estimation_procedure_id is not None
+ else self.DEFAULT_ESTIMATION_PROCEDURE_ID
+ )
- def get_dataset(self):
- """Download dataset associated with task"""
- return datasets.get_dataset(self.dataset_id)
+ @classmethod
+ def _entity_letter(cls) -> str:
+ return "t"
- def get_X_and_y(self):
- dataset = self.get_dataset()
- # Replace with retrieve from cache
- if 'Supervised Classification'.lower() in self.task_type.lower():
- target_dtype = int
- elif 'Supervised Regression'.lower() in self.task_type.lower():
- target_dtype = float
- else:
- raise NotImplementedError(self.task_type)
- X_and_y = dataset.get_data(target=self.target_name,
- target_dtype=target_dtype)
- return X_and_y
-
- def get_train_test_split_indices(self, fold=0, repeat=0):
+ @property
+ def id(self) -> int | None:
+ """Return the OpenML ID of this task."""
+ return self.task_id
+
+ def _get_repr_body_fields(self) -> Sequence[tuple[str, str | int | list[str]]]:
+ """Collect all information to display in the __repr__ body."""
+ base_server_url = openml.config.get_server_base_url()
+ fields: dict[str, Any] = {
+ "Task Type Description": f"{base_server_url}/tt/{self.task_type_id}"
+ }
+ if self.task_id is not None:
+ fields["Task ID"] = self.task_id
+ fields["Task URL"] = self.openml_url
+ if self.evaluation_measure is not None:
+ fields["Evaluation Measure"] = self.evaluation_measure
+ if self.estimation_procedure is not None:
+ fields["Estimation Procedure"] = self.estimation_procedure["type"]
+
+ # TODO(eddiebergman): Subclasses could advertise/provide this, instead of having to
+ # have the base class know about it's subclasses.
+ target_name = getattr(self, "target_name", None)
+ if target_name is not None:
+ fields["Target Feature"] = target_name
+
+ class_labels = getattr(self, "class_labels", None)
+ if class_labels is not None:
+ fields["# of Classes"] = len(class_labels)
+
+ cost_matrix = getattr(self, "cost_matrix", None)
+ if cost_matrix is not None:
+ fields["Cost Matrix"] = "Available"
+
+ # determines the order in which the information will be printed
+ order = [
+ "Task Type Description",
+ "Task ID",
+ "Task URL",
+ "Estimation Procedure",
+ "Evaluation Measure",
+ "Target Feature",
+ "# of Classes",
+ "Cost Matrix",
+ ]
+ return [(key, fields[key]) for key in order if key in fields]
+
+ def get_dataset(self, **kwargs: Any) -> datasets.OpenMLDataset:
+ """Download dataset associated with task.
+
+ Accepts the same keyword arguments as the `openml.datasets.get_dataset`.
+ """
+ return datasets.get_dataset(self.dataset_id, **kwargs)
+
+ def get_train_test_split_indices(
+ self,
+ fold: int = 0,
+ repeat: int = 0,
+ sample: int = 0,
+ ) -> tuple[np.ndarray, np.ndarray]:
+ """Get the indices of the train and test splits for a given task."""
# Replace with retrieve from cache
- split = self.download_split()
- train_indices, test_indices = split.get(repeat=repeat, fold=fold)
- return train_indices, test_indices
-
- def iterate_repeats(self):
- split = self.download_split()
- for rep in split.iterate_splits():
- yield rep
-
- def iterate_all_splits(self):
- split = self.download_split()
- for rep in split.iterate_splits():
- for fold in rep:
- yield fold
-
- def _download_split(self, cache_file):
+ if self.split is None:
+ self.split = self.download_split()
+
+ return self.split.get(repeat=repeat, fold=fold, sample=sample)
+
+ def _download_split(self, cache_file: Path) -> None:
+ # TODO(eddiebergman): Not sure about this try to read and error approach
try:
- with io.open(cache_file, encoding='utf8'):
+ with cache_file.open(encoding="utf8"):
pass
- except (OSError, IOError):
+ except OSError:
split_url = self.estimation_procedure["data_splits_url"]
- try:
- return_code, split_arff = _read_url(split_url)
- except (URLError, UnicodeEncodeError) as e:
- print(e, split_url)
- raise e
-
- with io.open(cache_file, "w", encoding='utf8') as fh:
- fh.write(split_arff)
- del split_arff
-
- def download_split(self):
- """Download the OpenML split for a given task.
-
- Parameters
- ----------
- task_id : Task
- An entity of :class:`openml.OpenMLTask`.
- """
- cached_split_file = os.path.join(
- _create_task_cache_dir(self.task_id), "datasplits.arff")
+ openml._api_calls._download_text_file(
+ source=str(split_url),
+ output_path=str(cache_file),
+ )
+
+ def download_split(self) -> OpenMLSplit:
+ """Download the OpenML split for a given task."""
+ # TODO(eddiebergman): Can this every be `None`?
+ assert self.task_id is not None
+ cache_dir = _create_cache_directory_for_id("tasks", self.task_id)
+ cached_split_file = cache_dir / "datasplits.arff"
try:
split = OpenMLSplit._from_arff_file(cached_split_file)
- # Add FileNotFoundError in python3 version (which should be a
- # subclass of OSError.
- except (OSError, IOError):
+ except OSError:
# Next, download and cache the associated split file
self._download_split(cached_split_file)
split = OpenMLSplit._from_arff_file(cached_split_file)
return split
+ def get_split_dimensions(self) -> tuple[int, int, int]:
+ """Get the (repeats, folds, samples) of the split for a given task."""
+ if self.split is None:
+ self.split = self.download_split()
+
+ return self.split.repeats, self.split.folds, self.split.samples
+
+ # TODO(eddiebergman): Really need some better typing on all this
+ def _to_dict(self) -> dict[str, dict[str, int | str | list[dict[str, Any]]]]:
+ """Creates a dictionary representation of self in a string format (for XML parsing)."""
+ oml_input = [
+ {"@name": "source_data", "#text": str(self.dataset_id)},
+ {"@name": "estimation_procedure", "#text": str(self.estimation_procedure_id)},
+ ]
+ if self.evaluation_measure is not None:
+ oml_input.append({"@name": "evaluation_measures", "#text": self.evaluation_measure})
+
+ return {
+ "oml:task_inputs": {
+ "@xmlns:oml": "http://openml.org/openml",
+ "oml:task_type_id": self.task_type_id.value, # This is an int from the enum?
+ "oml:input": oml_input,
+ }
+ }
+
+ def _parse_publish_response(self, xml_response: dict) -> None:
+ """Parse the id from the xml_response and assign it to self."""
+ self.task_id = int(xml_response["oml:upload_task"]["oml:id"])
+
+
+class OpenMLSupervisedTask(OpenMLTask, ABC):
+ """OpenML Supervised Classification object.
+
+ Parameters
+ ----------
+ task_type_id : TaskType
+ ID of the task type.
+ task_type : str
+ Name of the task type.
+ data_set_id : int
+ ID of the OpenML dataset associated with the task.
+ target_name : str
+ Name of the target feature (the class variable).
+ estimation_procedure_id : int, default=None
+ ID of the estimation procedure for the task.
+ estimation_procedure_type : str, default=None
+ Type of the estimation procedure for the task.
+ estimation_parameters : dict, default=None
+ Estimation parameters for the task.
+ evaluation_measure : str, default=None
+ Name of the evaluation measure for the task.
+ data_splits_url : str, default=None
+ URL of the data splits for the task.
+ task_id: Union[int, None]
+ Refers to the unique identifier of task.
+ """
+
+ DEFAULT_ESTIMATION_PROCEDURE_ID: ClassVar[int] = 1
+
+ def __init__( # noqa: PLR0913
+ self,
+ task_type_id: TaskType,
+ task_type: str,
+ data_set_id: int,
+ target_name: str,
+ estimation_procedure_id: int | None = None,
+ estimation_procedure_type: str | None = None,
+ estimation_parameters: dict[str, str] | None = None,
+ evaluation_measure: str | None = None,
+ data_splits_url: str | None = None,
+ task_id: int | None = None,
+ ):
+ super().__init__(
+ task_id=task_id,
+ task_type_id=task_type_id,
+ task_type=task_type,
+ data_set_id=data_set_id,
+ estimation_procedure_id=estimation_procedure_id,
+ estimation_procedure_type=estimation_procedure_type,
+ estimation_parameters=estimation_parameters,
+ evaluation_measure=evaluation_measure,
+ data_splits_url=data_splits_url,
+ target_name=target_name,
+ )
+
+ def get_X_and_y(self) -> tuple[pd.DataFrame, pd.Series | pd.DataFrame | None]:
+ """Get data associated with the current task.
+
+ Returns
+ -------
+ tuple - X and y
+
+ """
+ dataset = self.get_dataset()
+ if self.task_type_id not in (
+ TaskType.SUPERVISED_CLASSIFICATION,
+ TaskType.SUPERVISED_REGRESSION,
+ TaskType.LEARNING_CURVE,
+ ):
+ raise NotImplementedError(
+ f"Task type '{self.task_type}' is not implemented for get_X_and_y(). "
+ f"Supported types: SUPERVISED_CLASSIFICATION, SUPERVISED_REGRESSION,"
+ f"LEARNING_CURVE."
+ f"Task ID: {getattr(self, 'task_id', 'unknown')}. "
+ )
+
+ X, y, _, _ = dataset.get_data(target=self.target_name)
+ return X, y
+
+ def _to_dict(self) -> dict[str, dict]:
+ task_container = super()._to_dict()
+ oml_input = task_container["oml:task_inputs"]["oml:input"] # type: ignore
+ assert isinstance(oml_input, list)
+
+ oml_input.append({"@name": "target_feature", "#text": self.target_name})
+ return task_container
+
+ @property
+ def estimation_parameters(self) -> dict[str, str] | None:
+ """Return the estimation parameters for the task."""
+ warnings.warn(
+ "The estimation_parameters attribute will be "
+ "deprecated in the future, please use "
+ "estimation_procedure['parameters'] instead",
+ PendingDeprecationWarning,
+ stacklevel=2,
+ )
+ return self.estimation_procedure["parameters"]
+
+ @estimation_parameters.setter
+ def estimation_parameters(self, est_parameters: dict[str, str] | None) -> None:
+ self.estimation_procedure["parameters"] = est_parameters
+
+
+class OpenMLClassificationTask(OpenMLSupervisedTask):
+ """OpenML Classification object.
+
+ Parameters
+ ----------
+ task_id : Union[int, None]
+ ID of the Classification task (if it already exists on OpenML).
+ task_type_id : TaskType
+ ID of the Classification task type.
+ task_type : str
+ Name of the Classification task type.
+ data_set_id : int
+ ID of the OpenML dataset associated with the Classification task.
+ target_name : str
+ Name of the target variable.
+ estimation_procedure_id : int, default=1
+ ID of the estimation procedure for the Classification task.
+ estimation_procedure_type : str, default=None
+ Type of the estimation procedure.
+ estimation_parameters : dict, default=None
+ Estimation parameters for the Classification task.
+ evaluation_measure : str, default=None
+ Name of the evaluation measure.
+ data_splits_url : str, default=None
+ URL of the data splits for the Classification task.
+ class_labels : List of str, default=None
+ A list of class labels (for classification tasks).
+ cost_matrix : array, default=None
+ A cost matrix (for classification tasks).
+ """
+
+ DEFAULT_ESTIMATION_PROCEDURE_ID: ClassVar[int] = 1
+
+ def __init__( # noqa: PLR0913
+ self,
+ task_type_id: TaskType,
+ task_type: str,
+ data_set_id: int,
+ target_name: str,
+ estimation_procedure_id: int | None = None,
+ estimation_procedure_type: str | None = None,
+ estimation_parameters: dict[str, str] | None = None,
+ evaluation_measure: str | None = None,
+ data_splits_url: str | None = None,
+ task_id: int | None = None,
+ class_labels: list[str] | None = None,
+ cost_matrix: np.ndarray | None = None,
+ ):
+ super().__init__(
+ task_type_id=task_type_id,
+ task_type=task_type,
+ data_set_id=data_set_id,
+ target_name=target_name,
+ estimation_procedure_id=estimation_procedure_id,
+ estimation_procedure_type=estimation_procedure_type,
+ estimation_parameters=estimation_parameters,
+ evaluation_measure=evaluation_measure,
+ data_splits_url=data_splits_url,
+ task_id=task_id,
+ )
+ self.class_labels = class_labels
+ self.cost_matrix = cost_matrix
+ if cost_matrix is not None:
+ raise NotImplementedError("Costmatrix functionality is not yet implemented.")
+
+
+class OpenMLRegressionTask(OpenMLSupervisedTask):
+ """OpenML Regression object.
+
+ Parameters
+ ----------
+ task_id : Union[int, None]
+ ID of the OpenML Regression task.
+ task_type_id : TaskType
+ Task type ID of the OpenML Regression task.
+ task_type : str
+ Task type of the OpenML Regression task.
+ data_set_id : int
+ ID of the OpenML dataset.
+ target_name : str
+ Name of the target feature used in the Regression task.
+ estimation_procedure_id : int, default=7
+ ID of the OpenML estimation procedure.
+ estimation_procedure_type : str, default=None
+ Type of the OpenML estimation procedure.
+ estimation_parameters : dict, default=None
+ Parameters used by the OpenML estimation procedure.
+ data_splits_url : str, default=None
+ URL of the OpenML data splits for the Regression task.
+ evaluation_measure : str, default=None
+ Evaluation measure used in the Regression task.
+ """
+
+ DEFAULT_ESTIMATION_PROCEDURE_ID: ClassVar[int] = 7
+
+
+class OpenMLClusteringTask(OpenMLTask):
+ """OpenML Clustering object.
+
+ Parameters
+ ----------
+ task_id : Union[int, None]
+ ID of the OpenML clustering task.
+ task_type_id : TaskType
+ Task type ID of the OpenML clustering task.
+ task_type : str
+ Task type of the OpenML clustering task.
+ data_set_id : int
+ ID of the OpenML dataset used in clustering the task.
+ estimation_procedure_id : int, default=17
+ ID of the OpenML estimation procedure.
+ estimation_procedure_type : str, default=None
+ Type of the OpenML estimation procedure used in the clustering task.
+ estimation_parameters : dict, default=None
+ Parameters used by the OpenML estimation procedure.
+ data_splits_url : str, default=None
+ URL of the OpenML data splits for the clustering task.
+ evaluation_measure : str, default=None
+ Evaluation measure used in the clustering task.
+ target_name : str, default=None
+ Name of the target feature (class) that is not part of the
+ feature set for the clustering task.
+ """
+
+ DEFAULT_ESTIMATION_PROCEDURE_ID: ClassVar[int] = 17
+
+ def get_X(self) -> pd.DataFrame:
+ """Get data associated with the current task.
+
+ Returns
+ -------
+ The X data as a dataframe
+ """
+ dataset = self.get_dataset()
+ data, *_ = dataset.get_data(target=None)
+ return data
+
+ def _to_dict(self) -> dict[str, dict[str, int | str | list[dict[str, Any]]]]:
+ # Right now, it is not supported as a feature.
+ # Uncomment if it is supported on the server
+ # in the future.
+ # https://github.com/openml/OpenML/issues/925
+ """
+ task_dict = task_container['oml:task_inputs']
+ if self.target_name is not None:
+ task_dict['oml:input'].append(
+ OrderedDict([
+ ('@name', 'target_feature'),
+ ('#text', self.target_name)
+ ])
+ )
+ """
+ return super()._to_dict()
+
+
+class OpenMLLearningCurveTask(OpenMLClassificationTask):
+ """OpenML Learning Curve object.
-def _create_task_cache_dir(task_id):
- task_cache_dir = os.path.join(config.get_cache_directory(), "tasks", str(task_id))
+ Parameters
+ ----------
+ task_id : Union[int, None]
+ ID of the Learning Curve task.
+ task_type_id : TaskType
+ ID of the Learning Curve task.
+ task_type : str
+ Name of the Learning Curve task.
+ data_set_id : int
+ ID of the dataset that this task is associated with.
+ target_name : str
+ Name of the target feature in the dataset.
+ estimation_procedure_id : int, default=13
+ ID of the estimation procedure to use for evaluating models.
+ estimation_procedure_type : str, default=None
+ Type of the estimation procedure.
+ estimation_parameters : dict, default=None
+ Additional parameters for the estimation procedure.
+ data_splits_url : str, default=None
+ URL of the file containing the data splits for Learning Curve task.
+ evaluation_measure : str, default=None
+ Name of the evaluation measure to use for evaluating models.
+ class_labels : list of str, default=None
+ Class labels for Learning Curve tasks.
+ cost_matrix : numpy array, default=None
+ Cost matrix for Learning Curve tasks.
+ """
- try:
- os.makedirs(task_cache_dir)
- except (IOError, OSError):
- # TODO add debug information!
- pass
- return task_cache_dir
+ DEFAULT_ESTIMATION_PROCEDURE_ID: ClassVar[int] = 13
diff --git a/openml/testing.py b/openml/testing.py
index 94c92775d..9f694f9bf 100644
--- a/openml/testing.py
+++ b/openml/testing.py
@@ -1,8 +1,31 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+import hashlib
import inspect
+import logging
import os
+import pathlib
import shutil
+import time
import unittest
+from pathlib import Path
+from typing import ClassVar
+
+import requests
+
import openml
+from openml.exceptions import OpenMLServerException
+from openml.tasks import TaskType
+
+
+def _check_dataset(dataset: dict) -> None:
+ assert isinstance(dataset, dict)
+ assert len(dataset) >= 2
+ assert "did" in dataset
+ assert isinstance(dataset["did"], int)
+ assert "status" in dataset
+ assert dataset["status"] in ["in_preparation", "active", "deactivated"]
class TestBase(unittest.TestCase):
@@ -14,40 +37,328 @@ class TestBase(unittest.TestCase):
Hopefully soon allows using a test server, not the production server.
"""
- def setUp(self):
+ # TODO: This could be made more explcit with a TypedDict instead of list[str | int]
+ publish_tracker: ClassVar[dict[str, list[str | int]]] = {
+ "run": [],
+ "data": [],
+ "flow": [],
+ "task": [],
+ "study": [],
+ "user": [],
+ }
+ flow_name_tracker: ClassVar[list[str]] = []
+ test_server = f"{openml.config.TEST_SERVER_URL}/api/v1/xml"
+ admin_key = os.environ.get(openml.config.OPENML_TEST_SERVER_ADMIN_KEY_ENV_VAR)
+ user_key = openml.config._TEST_SERVER_NORMAL_USER_KEY
+
+ # creating logger for tracking files uploaded to test server
+ logger = logging.getLogger("unit_tests_published_entities")
+ logger.setLevel(logging.DEBUG)
+
+ def setUp(self, n_levels: int = 1, tmpdir_suffix: str = "") -> None:
+ """Setup variables and temporary directories.
+
+ In particular, this methods:
+
+ * creates a temporary working directory
+ * figures out a path to a few static test files
+ * set the default server to be the test server
+ * set a static API key for the test server
+ * increases the maximal number of retries
+
+ Parameters
+ ----------
+ n_levels : int
+ Number of nested directories the test is in. Necessary to resolve the path to the
+ ``files`` directory, which is located directly under the ``tests`` directory.
+ """
# This cache directory is checked in to git to simulate a populated
# cache
- self.static_cache_dir = None
- static_cache_dir = os.path.dirname(os.path.abspath(inspect.getfile(self.__class__)))
-
- static_cache_dir = os.path.abspath(os.path.join(static_cache_dir, '..'))
- content = os.listdir(static_cache_dir)
- if 'files' in content:
- self.static_cache_dir = os.path.join(static_cache_dir, 'files')
+ self.maxDiff = None
+ abspath_this_file = Path(inspect.getfile(self.__class__)).absolute()
+ static_cache_dir = abspath_this_file.parent
+ for _ in range(n_levels):
+ static_cache_dir = static_cache_dir.parent.absolute()
- if self.static_cache_dir is None:
- raise ValueError('Cannot find test cache dir!')
+ content = os.listdir(static_cache_dir) # noqa: PTH208
+ if "files" in content:
+ static_cache_dir = static_cache_dir / "files"
+ else:
+ raise ValueError(
+ f"Cannot find test cache dir, expected it to be {static_cache_dir}!",
+ )
- self.cwd = os.getcwd()
- workdir = os.path.dirname(os.path.abspath(__file__))
- self.workdir = os.path.join(workdir, "tmp")
- try:
- shutil.rmtree(self.workdir)
- except:
- pass
+ self.static_cache_dir = static_cache_dir
+ self.cwd = Path.cwd()
+ workdir = Path(__file__).parent.absolute()
+ tmp_dir_name = self.id() + tmpdir_suffix
+ self.workdir = workdir / tmp_dir_name
+ shutil.rmtree(self.workdir, ignore_errors=True)
- os.mkdir(self.workdir)
+ self.workdir.mkdir(exist_ok=True)
os.chdir(self.workdir)
self.cached = True
- # amueller's read/write key that he will throw away later
- openml.config.apikey = "610344db6388d9ba34f6db45a3cf71de"
- #openml.config.server = "http://capa.win.tue.nl/api/v1/xml"
- openml.config.server = "https://test.openml.org/api/v1/xml"
- openml.config.set_cache_directory(self.workdir, self.workdir)
+ openml.config.apikey = TestBase.user_key
+ self.production_server = "https://www.openml.org/api/v1/xml"
+ openml.config.set_root_cache_directory(str(self.workdir))
+
+ # Increase the number of retries to avoid spurious server failures
+ self.retry_policy = openml.config.retry_policy
+ self.connection_n_retries = openml.config.connection_n_retries
+ openml.config.set_retry_policy("robot", n_retries=20)
+
+ def use_production_server(self) -> None:
+ """
+ Use the production server for the OpenML API calls.
- def tearDown(self):
+ Please use this sparingly - it is better to use the test server.
+ """
+ openml.config.server = self.production_server
+ openml.config.apikey = ""
+
+ def tearDown(self) -> None:
+ """Tear down the test"""
os.chdir(self.cwd)
- shutil.rmtree(self.workdir)
+ try:
+ shutil.rmtree(self.workdir)
+ except PermissionError as e:
+ if os.name != "nt":
+ # one of the files may still be used by another process
+ raise e
+
+ openml.config.connection_n_retries = self.connection_n_retries
+ openml.config.retry_policy = self.retry_policy
+
+ @classmethod
+ def _mark_entity_for_removal(
+ cls,
+ entity_type: str,
+ entity_id: int,
+ entity_name: str | None = None,
+ ) -> None:
+ """Static record of entities uploaded to test server
+
+ Dictionary of lists where the keys are 'entity_type'.
+ Each such dictionary is a list of integer IDs.
+ For entity_type='flow', each list element is a tuple
+ of the form (Flow ID, Flow Name).
+ """
+ if entity_type not in TestBase.publish_tracker:
+ TestBase.publish_tracker[entity_type] = [entity_id]
+ else:
+ TestBase.publish_tracker[entity_type].append(entity_id)
+ if isinstance(entity_type, openml.flows.OpenMLFlow):
+ assert entity_name is not None
+ cls.flow_name_tracker.append(entity_name)
+
+ @classmethod
+ def _delete_entity_from_tracker(cls, entity_type: str, entity: int) -> None:
+ """Deletes entity records from the static file_tracker
+
+ Given an entity type and corresponding ID, deletes all entries, including
+ duplicate entries of the ID for the entity type.
+ """
+ if entity_type in TestBase.publish_tracker:
+ # removes duplicate entries
+ TestBase.publish_tracker[entity_type] = list(set(TestBase.publish_tracker[entity_type]))
+ if entity_type == "flow":
+ delete_index = next(
+ i
+ for i, (id_, _) in enumerate(
+ zip(
+ TestBase.publish_tracker[entity_type],
+ TestBase.flow_name_tracker,
+ strict=False,
+ ),
+ )
+ if id_ == entity
+ )
+ else:
+ delete_index = next(
+ i
+ for i, id_ in enumerate(TestBase.publish_tracker[entity_type])
+ if id_ == entity
+ )
+ TestBase.publish_tracker[entity_type].pop(delete_index)
+
+ def _get_sentinel(self, sentinel: str | None = None) -> str:
+ if sentinel is None:
+ # Create a unique prefix for the flow. Necessary because the flow
+ # is identified by its name and external version online. Having a
+ # unique name allows us to publish the same flow in each test run.
+ md5 = hashlib.md5() # noqa: S324
+ md5.update(str(time.time()).encode("utf-8"))
+ md5.update(str(os.getpid()).encode("utf-8"))
+ sentinel = md5.hexdigest()[:10]
+ sentinel = f"TEST{sentinel}"
+ return sentinel
+
+ def _add_sentinel_to_flow_name(
+ self,
+ flow: openml.flows.OpenMLFlow,
+ sentinel: str | None = None,
+ ) -> tuple[openml.flows.OpenMLFlow, str]:
+ sentinel = self._get_sentinel(sentinel=sentinel)
+ flows_to_visit = []
+ flows_to_visit.append(flow)
+ while len(flows_to_visit) > 0:
+ current_flow = flows_to_visit.pop()
+ current_flow.name = f"{sentinel}{current_flow.name}"
+ for subflow in current_flow.components.values():
+ flows_to_visit.append(subflow)
+
+ return flow, sentinel
+
+ def _check_dataset(self, dataset: dict[str, str | int]) -> None:
+ _check_dataset(dataset)
+ assert isinstance(dataset, dict)
+ assert len(dataset) >= 2
+ assert "did" in dataset
+ assert isinstance(dataset["did"], int)
+ assert "status" in dataset
+ assert isinstance(dataset["status"], str)
+ assert dataset["status"] in ["in_preparation", "active", "deactivated"]
+
+ def _check_fold_timing_evaluations( # noqa: PLR0913
+ self,
+ fold_evaluations: dict[str, dict[int, dict[int, float]]],
+ num_repeats: int,
+ num_folds: int,
+ *,
+ max_time_allowed: float = 60000.0,
+ task_type: TaskType = TaskType.SUPERVISED_CLASSIFICATION,
+ check_scores: bool = True,
+ ) -> None:
+ """
+ Checks whether the right timing measures are attached to the run
+ (before upload). Test is only performed for versions >= Python3.3
+
+ In case of check_n_jobs(clf) == false, please do not perform this
+ check (check this condition outside of this function. )
+ default max_time_allowed (per fold, in milli seconds) = 1 minute,
+ quite pessimistic
+ """
+ # a dict mapping from openml measure to a tuple with the minimum and
+ # maximum allowed value
+ check_measures = {
+ # should take at least one millisecond (?)
+ "usercpu_time_millis_testing": (0, max_time_allowed),
+ "usercpu_time_millis_training": (0, max_time_allowed),
+ "usercpu_time_millis": (0, max_time_allowed),
+ "wall_clock_time_millis_training": (0, max_time_allowed),
+ "wall_clock_time_millis_testing": (0, max_time_allowed),
+ "wall_clock_time_millis": (0, max_time_allowed),
+ }
+
+ if check_scores:
+ if task_type in (TaskType.SUPERVISED_CLASSIFICATION, TaskType.LEARNING_CURVE):
+ check_measures["predictive_accuracy"] = (0, 1.0)
+ elif task_type == TaskType.SUPERVISED_REGRESSION:
+ check_measures["mean_absolute_error"] = (0, float("inf"))
+
+ assert isinstance(fold_evaluations, dict)
+ assert set(fold_evaluations.keys()) == set(check_measures.keys())
+
+ for measure in check_measures:
+ if measure in fold_evaluations:
+ num_rep_entrees = len(fold_evaluations[measure])
+ assert num_rep_entrees == num_repeats
+ min_val = check_measures[measure][0]
+ max_val = check_measures[measure][1]
+ for rep in range(num_rep_entrees):
+ num_fold_entrees = len(fold_evaluations[measure][rep])
+ assert num_fold_entrees == num_folds
+ for fold in range(num_fold_entrees):
+ evaluation = fold_evaluations[measure][rep][fold]
+ assert isinstance(evaluation, float)
+ assert evaluation >= min_val
+ assert evaluation <= max_val
+
+
+def check_task_existence(
+ task_type: TaskType,
+ dataset_id: int,
+ target_name: str,
+ **kwargs: dict[str, str | int | dict[str, str | int | openml.tasks.TaskType]],
+) -> int | None:
+ """Checks if any task with exists on test server that matches the meta data.
+
+ Parameter
+ ---------
+ task_type : openml.tasks.TaskType
+ dataset_id : int
+ target_name : str
+
+ Return
+ ------
+ int, None
+ """
+ return_val = None
+ tasks = openml.tasks.list_tasks(task_type=task_type)
+ if len(tasks) == 0:
+ return None
+ tasks = tasks.loc[tasks["did"] == dataset_id]
+ if len(tasks) == 0:
+ return None
+ tasks = tasks.loc[tasks["target_feature"] == target_name]
+ if len(tasks) == 0:
+ return None
+ task_match = []
+ for task_id in tasks["tid"].to_list():
+ task_match.append(task_id)
+ try:
+ task = openml.tasks.get_task(task_id)
+ except OpenMLServerException:
+ # can fail if task_id deleted by another parallely run unit test
+ task_match.pop(-1)
+ return_val = None
+ continue
+ for k, v in kwargs.items():
+ if getattr(task, k) != v:
+ # even if one of the meta-data key mismatches, then task_id is not a match
+ task_match.pop(-1)
+ break
+ # if task_id is retained in the task_match list, it passed all meta key-value matches
+ if len(task_match) == 1:
+ return_val = task_id
+ break
+ if len(task_match) == 0:
+ return_val = None
+ return return_val
+
+
+try:
+ from sklearn.impute import SimpleImputer
+except ImportError:
+ from sklearn.preprocessing import Imputer as SimpleImputer
+
+
+class CustomImputer(SimpleImputer):
+ """Duplicate class alias for sklearn's SimpleImputer
+
+ Helps bypass the sklearn extension duplicate operation check
+ """
+
+
+def create_request_response(
+ *,
+ status_code: int,
+ content_filepath: pathlib.Path,
+) -> requests.Response:
+ with content_filepath.open("r") as xml_response:
+ response_body = xml_response.read()
+
+ response = requests.Response()
+ response.status_code = status_code
+ response._content = response_body.encode()
+ return response
+
-__all__ = ['TestBase']
+__all__ = [
+ "CustomImputer",
+ "SimpleImputer",
+ "TestBase",
+ "check_task_existence",
+ "create_request_response",
+]
diff --git a/openml/util.py b/openml/util.py
deleted file mode 100644
index 488004d35..000000000
--- a/openml/util.py
+++ /dev/null
@@ -1,15 +0,0 @@
-import sys
-
-if sys.version_info[0] < 3:
- from urllib2 import URLError
-else:
- from urllib.error import URLError
-
-
-def is_string(obj):
- try:
- return isinstance(obj, basestring)
- except NameError:
- return isinstance(obj, str)
-
-__all__ = ['URLError', 'is_string']
diff --git a/openml/utils/__init__.py b/openml/utils/__init__.py
new file mode 100644
index 000000000..1e74a3684
--- /dev/null
+++ b/openml/utils/__init__.py
@@ -0,0 +1,39 @@
+"""Utilities module."""
+
+from openml.utils._openml import (
+ ProgressBar,
+ ReprMixin,
+ _create_cache_directory,
+ _create_cache_directory_for_id,
+ _create_lockfiles_dir,
+ _delete_entity,
+ _get_cache_dir_for_id,
+ _get_cache_dir_for_key,
+ _get_rest_api_type_alias,
+ _list_all,
+ _remove_cache_dir_for_id,
+ _tag_entity,
+ _tag_openml_base,
+ extract_xml_tags,
+ get_cache_size,
+ thread_safe_if_oslo_installed,
+)
+
+__all__ = [
+ "ProgressBar",
+ "ReprMixin",
+ "_create_cache_directory",
+ "_create_cache_directory_for_id",
+ "_create_lockfiles_dir",
+ "_delete_entity",
+ "_get_cache_dir_for_id",
+ "_get_cache_dir_for_key",
+ "_get_rest_api_type_alias",
+ "_list_all",
+ "_remove_cache_dir_for_id",
+ "_tag_entity",
+ "_tag_openml_base",
+ "extract_xml_tags",
+ "get_cache_size",
+ "thread_safe_if_oslo_installed",
+]
diff --git a/openml/utils/_openml.py b/openml/utils/_openml.py
new file mode 100644
index 000000000..f18dbe3e0
--- /dev/null
+++ b/openml/utils/_openml.py
@@ -0,0 +1,545 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+import contextlib
+import re
+import shutil
+import warnings
+from abc import ABC, abstractmethod
+from collections.abc import Callable, Iterable, Mapping, Sequence, Sized
+from functools import wraps
+from pathlib import Path
+from typing import (
+ TYPE_CHECKING,
+ Any,
+ Literal,
+ TypeVar,
+ overload,
+)
+from typing_extensions import ParamSpec
+
+import numpy as np
+import xmltodict
+from minio.helpers import ProgressType
+from tqdm import tqdm
+
+import openml
+import openml._api_calls
+import openml.exceptions
+from openml import config
+
+# Avoid import cycles: https://mypy.readthedocs.io/en/latest/common_issues.html#import-cycles
+if TYPE_CHECKING:
+ from openml.base import OpenMLBase
+
+ P = ParamSpec("P")
+ R = TypeVar("R")
+ _SizedT = TypeVar("_SizedT", bound=Sized)
+
+
+@overload
+def extract_xml_tags(
+ xml_tag_name: str,
+ node: Mapping[str, Any],
+ *,
+ allow_none: Literal[True] = ...,
+) -> Any | None: ...
+
+
+@overload
+def extract_xml_tags(
+ xml_tag_name: str,
+ node: Mapping[str, Any],
+ *,
+ allow_none: Literal[False],
+) -> Any: ...
+
+
+def extract_xml_tags(
+ xml_tag_name: str,
+ node: Mapping[str, Any],
+ *,
+ allow_none: bool = True,
+) -> Any | None:
+ """Helper to extract xml tags from xmltodict.
+
+ Parameters
+ ----------
+ xml_tag_name : str
+ Name of the xml tag to extract from the node.
+
+ node : Mapping[str, Any]
+ Node object returned by ``xmltodict`` from which ``xml_tag_name``
+ should be extracted.
+
+ allow_none : bool
+ If ``False``, the tag needs to exist in the node. Will raise a
+ ``ValueError`` if it does not.
+
+ Returns
+ -------
+ object
+ """
+ if xml_tag_name in node and node[xml_tag_name] is not None:
+ if isinstance(node[xml_tag_name], (dict, str)):
+ return [node[xml_tag_name]]
+ if isinstance(node[xml_tag_name], list):
+ return node[xml_tag_name]
+
+ raise ValueError("Received not string and non list as tag item")
+
+ if allow_none:
+ return None
+
+ raise ValueError(f"Could not find tag '{xml_tag_name}' in node '{node!s}'")
+
+
+def _get_rest_api_type_alias(oml_object: OpenMLBase) -> str:
+ """Return the alias of the openml entity as it is defined for the REST API."""
+ rest_api_mapping: list[tuple[type | tuple, str]] = [
+ (openml.datasets.OpenMLDataset, "data"),
+ (openml.flows.OpenMLFlow, "flow"),
+ (openml.tasks.OpenMLTask, "task"),
+ (openml.runs.OpenMLRun, "run"),
+ ((openml.study.OpenMLStudy, openml.study.OpenMLBenchmarkSuite), "study"),
+ ]
+ _, api_type_alias = next(
+ (python_type, api_alias)
+ for (python_type, api_alias) in rest_api_mapping
+ if isinstance(oml_object, python_type)
+ )
+ return api_type_alias
+
+
+def _tag_openml_base(oml_object: OpenMLBase, tag: str, untag: bool = False) -> None: # noqa: FBT002
+ api_type_alias = _get_rest_api_type_alias(oml_object)
+ if oml_object.id is None:
+ raise openml.exceptions.ObjectNotPublishedError(
+ f"Cannot tag an {api_type_alias} that has not been published yet."
+ "Please publish the object first before being able to tag it."
+ f"\n{oml_object}",
+ )
+ _tag_entity(entity_type=api_type_alias, entity_id=oml_object.id, tag=tag, untag=untag)
+
+
+def _tag_entity(entity_type: str, entity_id: int, tag: str, *, untag: bool = False) -> list[str]:
+ """
+ Function that tags or untags a given entity on OpenML. As the OpenML
+ API tag functions all consist of the same format, this function covers
+ all entity types (currently: dataset, task, flow, setup, run). Could
+ be used in a partial to provide dataset_tag, dataset_untag, etc.
+
+ Parameters
+ ----------
+ entity_type : str
+ Name of the entity to tag (e.g., run, flow, data)
+
+ entity_id : int
+ OpenML id of the entity
+
+ tag : str
+ The tag
+
+ untag : bool
+ Set to true if needed to untag, rather than tag
+
+ Returns
+ -------
+ tags : list
+ List of tags that the entity is (still) tagged with
+ """
+ legal_entities = {"data", "task", "flow", "setup", "run"}
+ if entity_type not in legal_entities:
+ raise ValueError(f"Can't tag a {entity_type}")
+
+ if untag:
+ uri = f"{entity_type}/untag"
+ main_tag = f"oml:{entity_type}_untag"
+ else:
+ uri = f"{entity_type}/tag"
+ main_tag = f"oml:{entity_type}_tag"
+
+ result_xml = openml._api_calls._perform_api_call(
+ uri,
+ "post",
+ {f"{entity_type}_id": entity_id, "tag": tag},
+ )
+
+ result = xmltodict.parse(result_xml, force_list={"oml:tag"})[main_tag]
+
+ if "oml:tag" in result:
+ return result["oml:tag"] # type: ignore
+
+ # no tags, return empty list
+ return []
+
+
+# TODO(eddiebergman): Maybe this can be made more specific with a Literal
+def _delete_entity(entity_type: str, entity_id: int) -> bool:
+ """
+ Function that deletes a given entity on OpenML. As the OpenML
+ API tag functions all consist of the same format, this function covers
+ all entity types that can be deleted (currently: dataset, task, flow,
+ run, study and user).
+
+ Parameters
+ ----------
+ entity_type : str
+ Name of the entity to tag (e.g., run, flow, data)
+
+ entity_id : int
+ OpenML id of the entity
+
+ Returns
+ -------
+ bool
+ True iff the deletion was successful. False otherwse
+ """
+ legal_entities = {
+ "data",
+ "flow",
+ "task",
+ "run",
+ "study",
+ "user",
+ }
+ if entity_type not in legal_entities:
+ raise ValueError(f"Can't delete a {entity_type}")
+
+ url_suffix = f"{entity_type}/{entity_id}"
+ try:
+ result_xml = openml._api_calls._perform_api_call(url_suffix, "delete")
+ result = xmltodict.parse(result_xml)
+ return f"oml:{entity_type}_delete" in result
+ except openml.exceptions.OpenMLServerException as e:
+ # https://github.com/openml/OpenML/blob/21f6188d08ac24fcd2df06ab94cf421c946971b0/openml_OS/views/pages/api_new/v1/xml/pre.php
+ # Most exceptions are descriptive enough to be raised as their standard
+ # OpenMLServerException, however there are two cases where we add information:
+ # - a generic "failed" message, we direct them to the right issue board
+ # - when the user successfully authenticates with the server,
+ # but user is not allowed to take the requested action,
+ # in which case we specify a OpenMLNotAuthorizedError.
+ by_other_user = [323, 353, 393, 453, 594]
+ has_dependent_entities = [324, 326, 327, 328, 354, 454, 464, 595]
+ unknown_reason = [325, 355, 394, 455, 593]
+ if e.code in by_other_user:
+ raise openml.exceptions.OpenMLNotAuthorizedError(
+ message=(
+ f"The {entity_type} can not be deleted because it was not uploaded by you."
+ ),
+ ) from e
+ if e.code in has_dependent_entities:
+ raise openml.exceptions.OpenMLNotAuthorizedError(
+ message=(
+ f"The {entity_type} can not be deleted because "
+ f"it still has associated entities: {e.message}"
+ ),
+ ) from e
+ if e.code in unknown_reason:
+ raise openml.exceptions.OpenMLServerError(
+ message=(
+ f"The {entity_type} can not be deleted for unknown reason,"
+ " please open an issue at: https://github.com/openml/openml/issues/new"
+ ),
+ ) from e
+ raise e
+
+
+def _list_all( # noqa: C901
+ listing_call: Callable[[int, int], _SizedT],
+ *,
+ limit: int | None = None,
+ offset: int | None = None,
+ batch_size: int | None = 10_000,
+) -> list[_SizedT]:
+ """Helper to handle paged listing requests.
+
+ Example usage:
+
+ ``evaluations = list_all(list_evaluations, "predictive_accuracy", task=mytask)``
+
+ Parameters
+ ----------
+ listing_call : callable
+ Call listing, e.g. list_evaluations. Takes two positional
+ arguments: batch_size and offset.
+ batch_size : int, optional
+ The batch size to use for the listing call.
+ offset : int, optional
+ The initial offset to use for the listing call.
+ limit : int, optional
+ The total size of the listing. If not provided, the function will
+ request the first batch and then continue until no more results are
+ returned
+
+ Returns
+ -------
+ List of types returned from type of the listing call
+ """
+ page = 0
+ results: list[_SizedT] = []
+
+ offset = offset if offset is not None else 0
+ batch_size = batch_size if batch_size is not None else 10_000
+
+ LIMIT = limit
+ BATCH_SIZE_ORIG = batch_size
+
+ # Default batch size per paging.
+ # This one can be set in filters (batch_size), but should not be
+ # changed afterwards. The derived batch_size can be changed.
+ if not isinstance(BATCH_SIZE_ORIG, int):
+ raise ValueError(f"'batch_size' should be an integer but got {BATCH_SIZE_ORIG}")
+
+ if (LIMIT is not None) and (not isinstance(LIMIT, int)) and (not np.isinf(LIMIT)):
+ raise ValueError(f"'limit' should be an integer or inf but got {LIMIT}")
+
+ # If our batch size is larger than the limit, we should only
+ # request one batch of size of LIMIT
+ if LIMIT is not None and BATCH_SIZE_ORIG > LIMIT:
+ BATCH_SIZE_ORIG = LIMIT
+
+ if not isinstance(offset, int):
+ raise ValueError(f"'offset' should be an integer but got {offset}")
+
+ batch_size = BATCH_SIZE_ORIG
+ while True:
+ try:
+ current_offset = offset + BATCH_SIZE_ORIG * page
+ new_batch = listing_call(batch_size, current_offset)
+ except openml.exceptions.OpenMLServerNoResult:
+ # NOTE: This above statement may not actually happen, but we could just return here
+ # to enforce it...
+ break
+
+ results.append(new_batch)
+
+ # If the batch is less than our requested batch_size, that's the last batch
+ # and we can bail out.
+ if len(new_batch) < batch_size:
+ break
+
+ page += 1
+ if LIMIT is not None:
+ # check if the number of required results has been achieved
+ # always do a 'bigger than' check,
+ # in case of bugs to prevent infinite loops
+ n_received = sum(len(result) for result in results)
+ if n_received >= LIMIT:
+ break
+
+ # check if there are enough results to fulfill a batch
+ if LIMIT - n_received < BATCH_SIZE_ORIG:
+ batch_size = LIMIT - n_received
+
+ return results
+
+
+def _get_cache_dir_for_key(key: str) -> Path:
+ return Path(config.get_cache_directory()) / key
+
+
+def _create_cache_directory(key: str) -> Path:
+ cache_dir = _get_cache_dir_for_key(key)
+
+ try:
+ cache_dir.mkdir(exist_ok=True, parents=True)
+ except Exception as e:
+ raise openml.exceptions.OpenMLCacheException(
+ f"Cannot create cache directory {cache_dir}."
+ ) from e
+
+ return cache_dir
+
+
+def _get_cache_dir_for_id(key: str, id_: int, create: bool = False) -> Path: # noqa: FBT002
+ cache_dir = _create_cache_directory(key) if create else _get_cache_dir_for_key(key)
+ return Path(cache_dir) / str(id_)
+
+
+def _create_cache_directory_for_id(key: str, id_: int) -> Path:
+ """Create the cache directory for a specific ID
+
+ In order to have a clearer cache structure and because every task
+ is cached in several files (description, split), there
+ is a directory for each task witch the task ID being the directory
+ name. This function creates this cache directory.
+
+ This function is NOT thread/multiprocessing safe.
+
+ Parameters
+ ----------
+ key : str
+
+ id_ : int
+
+ Returns
+ -------
+ cache_dir : Path
+ Path of the created dataset cache directory.
+ """
+ cache_dir = _get_cache_dir_for_id(key, id_, create=True)
+ if cache_dir.exists() and not cache_dir.is_dir():
+ raise ValueError(f"{key} cache dir exists but is not a directory!")
+
+ cache_dir.mkdir(exist_ok=True, parents=True)
+ return cache_dir
+
+
+def _remove_cache_dir_for_id(key: str, cache_dir: Path) -> None:
+ """Remove the task cache directory
+
+ This function is NOT thread/multiprocessing safe.
+
+ Parameters
+ ----------
+ key : str
+
+ cache_dir : str
+ """
+ try:
+ shutil.rmtree(cache_dir)
+ except OSError as e:
+ raise ValueError(
+ f"Cannot remove faulty {key} cache directory {cache_dir}. Please do this manually!",
+ ) from e
+
+
+def thread_safe_if_oslo_installed(func: Callable[P, R]) -> Callable[P, R]:
+ try:
+ # Currently, importing oslo raises a lot of warning that it will stop working
+ # under python3.8; remove this once they disappear
+ with warnings.catch_warnings():
+ warnings.simplefilter("ignore")
+ from oslo_concurrency import lockutils
+
+ @wraps(func)
+ def safe_func(*args: P.args, **kwargs: P.kwargs) -> R:
+ # Lock directories use the id that is passed as either positional or keyword argument.
+ id_parameters = [parameter_name for parameter_name in kwargs if "_id" in parameter_name]
+ if len(id_parameters) == 1:
+ id_ = kwargs[id_parameters[0]]
+ elif len(args) > 0:
+ id_ = args[0]
+ else:
+ raise RuntimeError(
+ f"An id must be specified for {func.__name__}, was passed: ({args}, {kwargs}).",
+ )
+ # The [7:] gets rid of the 'openml.' prefix
+ lock_name = f"{func.__module__[7:]}.{func.__name__}:{id_}"
+ with lockutils.external_lock(name=lock_name, lock_path=_create_lockfiles_dir()):
+ return func(*args, **kwargs)
+
+ return safe_func
+ except ImportError:
+ return func
+
+
+def get_cache_size() -> int:
+ """Calculate the size of OpenML cache directory
+
+ Returns
+ -------
+ cache_size: int
+ Total size of cache in bytes
+ """
+ path = Path(config.get_cache_directory())
+ return sum(f.stat().st_size for f in path.rglob("*") if f.is_file())
+
+
+def _create_lockfiles_dir() -> Path:
+ path = Path(config.get_cache_directory()) / "locks"
+ # TODO(eddiebergman): Not sure why this is allowed to error and ignore???
+ with contextlib.suppress(OSError):
+ path.mkdir(exist_ok=True, parents=True)
+ return path
+
+
+class ProgressBar(ProgressType):
+ """Progressbar for MinIO function's `progress` parameter."""
+
+ def __init__(self) -> None:
+ self._object_name = ""
+ self._progress_bar: tqdm | None = None
+
+ def set_meta(self, object_name: str, total_length: int) -> None:
+ """Initializes the progress bar.
+
+ Parameters
+ ----------
+ object_name: str
+ Not used.
+
+ total_length: int
+ File size of the object in bytes.
+ """
+ self._object_name = object_name
+ self._progress_bar = tqdm(total=total_length, unit_scale=True, unit="B")
+
+ def update(self, length: int) -> None:
+ """Updates the progress bar.
+
+ Parameters
+ ----------
+ length: int
+ Number of bytes downloaded since last `update` call.
+ """
+ if not self._progress_bar:
+ raise RuntimeError("Call `set_meta` before calling `update`.")
+ self._progress_bar.update(length)
+ if self._progress_bar.total <= self._progress_bar.n:
+ self._progress_bar.close()
+
+
+class ReprMixin(ABC):
+ """A mixin class that provides a customizable string representation for OpenML objects.
+
+ This mixin standardizes the __repr__ output format across OpenML classes.
+ Classes inheriting from this mixin should implement the
+ _get_repr_body_fields method to specify which fields to display.
+ """
+
+ def __repr__(self) -> str:
+ body_fields = self._get_repr_body_fields()
+ return self._apply_repr_template(body_fields)
+
+ @abstractmethod
+ def _get_repr_body_fields(self) -> Sequence[tuple[str, str | int | list[str] | None]]:
+ """Collect all information to display in the __repr__ body.
+
+ Returns
+ -------
+ body_fields : List[Tuple[str, Union[str, int, List[str]]]]
+ A list of (name, value) pairs to display in the body of the __repr__.
+ E.g.: [('metric', 'accuracy'), ('dataset', 'iris')]
+ If value is a List of str, then each item of the list will appear in a separate row.
+ """
+ # Should be implemented in the base class.
+
+ def _apply_repr_template(
+ self,
+ body_fields: Iterable[tuple[str, str | int | list[str] | None]],
+ ) -> str:
+ """Generates the header and formats the body for string representation of the object.
+
+ Parameters
+ ----------
+ body_fields: List[Tuple[str, str]]
+ A list of (name, value) pairs to display in the body of the __repr__.
+ """
+ # We add spaces between capitals, e.g. ClassificationTask -> Classification Task
+ name_with_spaces = re.sub(
+ r"(\w)([A-Z])",
+ r"\1 \2",
+ self.__class__.__name__[len("OpenML") :],
+ )
+ header_text = f"OpenML {name_with_spaces}"
+ header = f"{header_text}\n{'=' * len(header_text)}\n"
+
+ _body_fields: list[tuple[str, str | int | list[str]]] = [
+ (k, "None" if v is None else v) for k, v in body_fields
+ ]
+ longest_field_name_length = max(len(name) for name, _ in _body_fields)
+ field_line_format = f"{{:.<{longest_field_name_length}}}: {{}}"
+ body = "\n".join(field_line_format.format(name, value) for name, value in _body_fields)
+ return header + body
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 000000000..47013271d
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,340 @@
+# -*- coding: utf-8 -*-
+
+# License: BSD 3-Clause
+[build-system]
+requires = ["setuptools >= 61.0"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "openml"
+dynamic = ["version"] # Will take it from the __version__ file, update there
+dependencies = [
+ "liac-arff>=2.4.0",
+ "xmltodict",
+ "requests",
+ "scikit-learn>=0.18",
+ "python-dateutil", # Installed through pandas anyway.
+ "pandas>=1.0.0",
+ "scipy>=0.13.3",
+ "numpy>=1.6.2",
+ "minio",
+ "pyarrow",
+ "tqdm", # For MinIO download progress bars
+]
+requires-python = ">=3.10,<3.15"
+maintainers = [
+ { name = "Pieter Gijsbers", email="p.gijsbers@tue.nl"},
+ { name = "Lennart Purucker"},
+]
+authors = [
+ { name = "Matthias Feurer"},
+ { name = "Jan van Rijn" },
+ { name = "Arlind Kadra" },
+ { name = "Pieter Gijsbers" },
+ { name = "Neeratyoy Mallik" },
+ { name = "Sahithya Ravi" },
+ { name = "Andreas Müller" },
+ { name = "Joaquin Vanschoren " },
+ { name = "Frank Hutter" },
+]
+readme = "README.md"
+description = "Python API for OpenML"
+classifiers = [
+ "Intended Audience :: Science/Research",
+ "Intended Audience :: Developers",
+ "License :: OSI Approved :: BSD License",
+ "Programming Language :: Python",
+ "Topic :: Software Development",
+ "Topic :: Scientific/Engineering",
+ "Operating System :: POSIX",
+ "Operating System :: Unix",
+ "Operating System :: MacOS",
+ "Programming Language :: Python :: 3",
+ "Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
+ "Programming Language :: Python :: 3.12",
+ "Programming Language :: Python :: 3.13",
+ "Programming Language :: Python :: 3.14",
+]
+license = { file = "LICENSE" }
+
+[project.scripts]
+openml = "openml.cli:main"
+
+[project.optional-dependencies]
+test=[
+ "nbconvert",
+ "jupyter_client",
+ "matplotlib",
+ "pytest",
+ "pytest-xdist",
+ "pytest-timeout",
+ "nbformat",
+ "oslo.concurrency",
+ "flaky",
+ "pre-commit",
+ "pytest-cov",
+ "pytest-rerunfailures",
+ "mypy",
+ "ruff",
+ "requests-mock",
+ "openml-sklearn",
+ "packaging",
+ "pytest-mock",
+ "openml-sklearn",
+]
+examples=[
+ "matplotlib",
+ "jupyter",
+ "notebook",
+ "nbconvert",
+ "nbformat",
+ "jupyter_client",
+ "ipython",
+ "ipykernel",
+ "seaborn",
+]
+docs=[
+ "mkdocs",
+ "numpydoc",
+ "mkdocs-material",
+ "mkdocs-autorefs",
+ "mkdocstrings[python]",
+ "mkdocs-gen-files",
+ "mkdocs-literate-nav",
+ "mkdocs-section-index",
+ "mkdocs-jupyter",
+ "mkdocs-linkcheck",
+ "mike"
+]
+
+[project.urls]
+home="https://openml.org/"
+documentation = "https://openml.github.io/openml-python/"
+source = "https://github.com/openml/openml-python"
+
+[tool.setuptools.packages.find]
+where = [""]
+include = ["openml*"]
+namespaces = false
+
+[tool.setuptools.package-data]
+openml = ["*.txt", "*.md", "py.typed"]
+
+[tool.setuptools.dynamic]
+version = {attr = "openml.__version__.__version__"}
+
+# https://docs.pytest.org/en/7.2.x/reference/reference.html#ini-options-ref
+[tool.pytest.ini_options]
+testpaths = ["tests"]
+minversion = "7.0"
+xfail_strict = true
+filterwarnings=[
+ "ignore:the matrix subclass:PendingDeprecationWarning"
+]
+markers = [
+ "upload: anything that uploads to a server",
+ "production_server: any interaction with the production server",
+ "cache: anything that interacts with the (test) cache",
+ "test_server: tests that require the OpenML test server",
+]
+
+# https://github.com/charliermarsh/ruff
+[tool.ruff]
+target-version = "py310"
+line-length = 100
+output-format = "grouped"
+src = ["openml", "tests", "examples"]
+unsafe-fixes = true
+
+exclude = [
+ # TODO(eddiebergman): Tests should be re-enabled after the refactor
+ "tests",
+ #
+ ".bzr",
+ ".direnv",
+ ".eggs",
+ ".git",
+ ".hg",
+ ".mypy_cache",
+ ".nox",
+ ".pants.d",
+ ".ruff_cache",
+ ".svn",
+ ".tox",
+ ".venv",
+ "__pypackages__",
+ "_build",
+ "buck-out",
+ "build",
+ "dist",
+ "node_modules",
+ "venv",
+ "docs",
+]
+
+# Exclude a variety of commonly ignored directories.
+[tool.ruff.lint.per-file-ignores]
+"tests/*.py" = [
+ "D100", # Undocumented public module
+ "D101", # Missing docstring in public class
+ "D102", # Missing docstring in public method
+ "D103", # Missing docstring in public function
+ "S101", # Use of assert
+ "ANN201", # Missing return type annotation for public function
+ "FBT001", # Positional boolean argument
+ "PLR2004",# No use of magic numbers
+ "PD901", # X is a bad variable name. (pandas)
+ "TCH", # https://docs.astral.sh/ruff/rules/#flake8-type-checking-tch
+ "N803", # Argument name {name} should be lowercase
+]
+"openml/cli.py" = [
+ "T201", # print found
+ "T203", # pprint found
+]
+"openml/__version__.py" = [
+ "D100", # Undocumented public module
+]
+"__init__.py" = [
+ "I002", # Missing required import (i.e. from __future__ import annotations)
+]
+"examples/*.py" = [
+ "D101", # Missing docstring in public class
+ "D102", # Missing docstring in public method
+ "D103", # Missing docstring in public function
+ "D415", # First line should end with a . or ? or !
+ "INP001", # File is part of an implicit namespace package, add an __init__.py
+ "I002", # Missing required import (i.e. from __future__ import annotations)
+ "E741", # Ambigiuous variable name
+ "T201", # print found
+ "T203", # pprint found
+ "ERA001", # found commeneted out code
+ "E402", # Module level import not at top of cell
+ "E501", # Line too long
+]
+
+[tool.ruff.lint]
+# Allow unused variables when underscore-prefixed.
+dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
+
+select = [
+ "A",
+ # "ANN", # Handled by mypy
+ "ARG",
+ "B",
+ "BLE",
+ "COM",
+ "C4",
+ "D",
+ # "DTZ", # One day I should know how to utilize timezones and dates...
+ "E",
+ # "EXE", Meh
+ "ERA",
+ "F",
+ "FBT",
+ "I",
+ # "ISC", # Favours implicit string concatenation
+ "INP",
+ # "INT", # I don't understand this one
+ "N",
+ "NPY",
+ "PD",
+ "PLC",
+ "PLE",
+ "PLR",
+ "PLW",
+ "PIE",
+ "PT",
+ "PTH",
+ # "PYI", # Specific to .pyi files for type stubs
+ "Q",
+ "PGH004",
+ "RET",
+ "RUF",
+ "C90",
+ "S",
+ # "SLF", # Private member accessed (sure, it's python)
+ "SIM",
+ # "TRY", # Good in principle, would take a lot of work to statisfy
+ "T10",
+ "T20",
+ "TID",
+ "TCH",
+ "UP",
+ "N",
+ "W",
+ "YTT",
+]
+
+ignore = [
+ "D105", # Missing docstring in magic mthod
+ "D401", # First line of docstring should be in imperative mood
+ "N806", # Variable X in function should be lowercase
+ "E731", # Do not assign a lambda expression, use a def
+ "S101", # Use of assert detected.
+ "W292", # No newline at end of file
+ "PLC1901", # "" can be simplified to be falsey
+ "TC003", # Move stdlib import into TYPE_CHECKING
+ "COM812", # Trailing comma missing (handled by linter, ruff recommend disabling if using formatter)
+ "N803", # Argument should be lowercase (but we accept things like `X`)
+ "PLC0415", # Allow imports inside functions / non-top-level scope
+ "FBT001", # Allow Boolean-typed positional argument in function definition
+
+ # TODO(@eddibergman): These should be enabled
+ "D100", # Missing docstring in public module
+ "D103", # Missing docstring in public function
+ "D104", # Missing docstring in public package
+
+ # TODO(@eddiebergman): Maybe fix
+ "PLR2004", # Magic value used in comparison, consider replacing 2 with a constant variable
+ "D400", # First line must end with a period (@eddiebergman too many to fix so ignoring this for now)
+ "D203", # 1 blank line required before class docstring
+ "D205", # 1 blank line between summary and description
+
+ # TODO(@eddiebergman): Could be backwards breaking
+ "N802", # Public function name should be lower case (i.e. get_X())
+]
+
+
+
+[tool.ruff.lint.isort]
+known-first-party = ["openml"]
+no-lines-before = ["future"]
+required-imports = ["from __future__ import annotations"]
+combine-as-imports = true
+extra-standard-library = ["typing_extensions"]
+force-wrap-aliases = true
+
+[tool.ruff.lint.pydocstyle]
+convention = "numpy"
+
+[tool.mypy]
+python_version = "3.10"
+packages = ["openml", "tests"]
+
+show_error_codes = true
+
+warn_unused_configs = true # warn about unused [tool.mypy] lines
+
+follow_imports = "normal" # Type check top level api code we use from imports
+ignore_missing_imports = false # prefer explicit ignores
+
+disallow_untyped_defs = true # All functions must have types
+disallow_untyped_decorators = true # ... even decorators
+disallow_incomplete_defs = true # ...all types
+
+no_implicit_optional = true
+check_untyped_defs = true
+
+warn_return_any = true
+
+
+[[tool.mypy.overrides]]
+module = ["tests.*", "openml.extensions.sklearn.*"]
+
+# TODO(eddiebergman): This should be re-enabled after tests get refactored
+ignore_errors = true
+#disallow_untyped_defs = false # Sometimes we just want to ignore verbose types
+#disallow_untyped_decorators = false # Test decorators are not properly typed
+#disallow_incomplete_defs = false # Sometimes we just want to ignore verbose types
+#disable_error_code = ["var-annotated"]
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index 0a675a736..000000000
--- a/requirements.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-mock
-numpy>=1.6.2
-scipy>=0.13.3
-liac-arff>=2.1.1dev
-xmltodict
-nose
-requests
-scikit-learn
-nbformat
diff --git a/scripts/__init__.py b/scripts/__init__.py
new file mode 100644
index 000000000..000969b80
--- /dev/null
+++ b/scripts/__init__.py
@@ -0,0 +1 @@
+"""Package for scripts and utilities."""
diff --git a/scripts/gen_ref_pages.py b/scripts/gen_ref_pages.py
new file mode 100644
index 000000000..22a873a4a
--- /dev/null
+++ b/scripts/gen_ref_pages.py
@@ -0,0 +1,58 @@
+"""Generate the code reference pages.
+
+based on https://github.com/mkdocstrings/mkdocstrings/blob/33aa573efb17b13e7b9da77e29aeccb3fbddd8e8/docs/recipes.md
+but modified for lack of "src/" file structure.
+
+"""
+
+from __future__ import annotations
+
+from pathlib import Path
+
+import mkdocs_gen_files
+
+nav = mkdocs_gen_files.Nav()
+
+root = Path(__file__).parent.parent
+src = root / "openml"
+
+for path in sorted(src.rglob("*.py")):
+ module_path = path.relative_to(root).with_suffix("")
+ doc_path = path.relative_to(src).with_suffix(".md")
+ full_doc_path = Path("reference", doc_path)
+
+ parts = tuple(module_path.parts)
+
+ if parts[-1] == "__init__":
+ parts = parts[:-1]
+ doc_path = doc_path.with_name("index.md")
+ full_doc_path = full_doc_path.with_name("index.md")
+ elif parts[-1] == "__main__":
+ continue
+
+ nav[parts] = doc_path.as_posix()
+
+ with mkdocs_gen_files.open(full_doc_path, "w") as fd:
+ identifier = ".".join(parts)
+ print("::: " + identifier, file=fd)
+
+ mkdocs_gen_files.set_edit_path(full_doc_path, path.relative_to(root))
+
+ with mkdocs_gen_files.open("reference/SUMMARY.md", "w") as nav_file:
+ nav_file.writelines(nav.build_literate_nav())
+
+nav = mkdocs_gen_files.Nav()
+examples_dir = root / "examples"
+examples_doc_dir = root / "docs" / "examples"
+for path in sorted(examples_dir.rglob("*.py")):
+ if "_external_or_deprecated" in path.parts:
+ continue
+ dest_path = Path("examples") / path.relative_to(examples_dir)
+ with mkdocs_gen_files.open(dest_path, "w") as dest_file:
+ print(path.read_text(), file=dest_file)
+
+ new_relative_location = Path("../") / dest_path
+ nav[new_relative_location.parts[2:]] = new_relative_location.as_posix()
+
+ with mkdocs_gen_files.open("examples/SUMMARY.md", "w") as nav_file:
+ nav_file.writelines(nav.build_literate_nav())
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index fac02f0f9..000000000
--- a/setup.cfg
+++ /dev/null
@@ -1,17 +0,0 @@
-[metadata]
-description-file = README.md
-
-[nosetests]
-# nosetests skips test files with the executable bit by default
-# which can silently hide failing tests.
-exe = 1
-cover-html = 1
-cover-html-dir = coverage
-cover-package = openml
-
-detailed-errors = 1
-with-doctest = 1
-doctest-tests = 1
-doctest-extension = rst
-doctest-fixtures = _fixture
-#doctest-options = +ELLIPSIS,+NORMALIZE_WHITESPACE
diff --git a/setup.py b/setup.py
deleted file mode 100644
index b4e9113e3..000000000
--- a/setup.py
+++ /dev/null
@@ -1,69 +0,0 @@
-import os
-import setuptools
-import sys
-
-
-requirements_file = os.path.join(os.path.dirname(__file__), 'requirements.txt')
-requirements = []
-dependency_links = []
-with open(requirements_file) as fh:
- for line in fh:
- line = line.strip()
- if line:
- # Make sure the github URLs work here as well
- split = line.split('@')
- split = split[0]
- split = split.split('/')
- url = '/'.join(split[:-1])
- requirement = split[-1]
- requirements.append(requirement)
- # Add the rest of the URL to the dependency links to allow
- # setup.py test to work
- if 'git+https' in url:
- dependency_links.append(line.replace('git+', ''))
-
-
-try:
- import numpy
-except ImportError:
- print('numpy is required during installation')
- sys.exit(1)
-
-try:
- import scipy
-except ImportError:
- print('scipy is required during installation')
- sys.exit(1)
-
-
-setuptools.setup(name="openml",
- author="Matthias Feurer",
- author_email="feurerm@informatik.uni-freiburg.de",
- maintainer="Matthias Feurer",
- maintainer_email="feurerm@informatik.uni-freiburg.de",
- description="Python API for OpenML",
- license="GPLv3",
- url="http://openml.org/",
- version="0.3.0",
- packages=setuptools.find_packages(),
- package_data={'': ['*.txt', '*.md']},
- install_requires=requirements,
- test_suite="nose.collector",
- classifiers=['Intended Audience :: Science/Research',
- 'Intended Audience :: Developers',
- 'License :: GPLv3',
- 'Programming Language :: Python',
- 'Topic :: Software Development',
- 'Topic :: Scientific/Engineering',
- 'Operating System :: POSIX',
- 'Operating System :: Unix',
- 'Operating System :: MacOS',
- 'Programming Language :: Python :: 2',
- 'Programming Language :: Python :: 2.7',
- 'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.4',
- 'Programming Language :: Python :: 3.5',
- ],
- dependency_links=[
- "http://github.com/mfeurer/liac-arff/archive/master.zip"
- "#egg=liac-arff-2.1.1dev"])
\ No newline at end of file
diff --git a/tests/__init__.py b/tests/__init__.py
index e69de29bb..245c252db 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -0,0 +1,5 @@
+# License: BSD 3-Clause
+
+# Dummy to allow mock classes in the test files to have a version number for
+# their parent module
+__version__ = "0.1"
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 000000000..2a7a6dcc7
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,309 @@
+"""This file is recognized by pytest for defining specified behaviour
+
+'conftest.py' files are directory-scope files that are shared by all
+sub-directories from where this file is placed. pytest recognises
+'conftest.py' for any unit test executed from within this directory
+tree. This file is used to define fixtures, hooks, plugins, and other
+functionality that can be shared by the unit tests.
+
+This file has been created for the OpenML testing to primarily make use
+of the pytest hooks 'pytest_sessionstart' and 'pytest_sessionfinish',
+which are being used for managing the deletion of local and remote files
+created by the unit tests, run across more than one process.
+
+This design allows one to comment or remove the conftest.py file to
+disable file deletions, without editing any of the test case files.
+
+
+Possible Future: class TestBase from openml/testing.py can be included
+ under this file and there would not be any requirements to import
+ testing.py in each of the unit test modules.
+"""
+
+# License: BSD 3-Clause
+from __future__ import annotations
+
+import multiprocessing
+
+multiprocessing.set_start_method("spawn", force=True)
+
+from collections.abc import Iterator
+import logging
+import os
+import shutil
+from pathlib import Path
+import pytest
+import openml_sklearn
+
+import openml
+from openml.testing import TestBase
+
+import inspect
+
+# creating logger for unit test file deletion status
+logger = logging.getLogger("unit_tests")
+logger.setLevel(logging.DEBUG)
+
+file_list = []
+
+
+def worker_id() -> str:
+ """Returns the name of the worker process owning this function call.
+
+ :return: str
+ Possible outputs from the set of {'master', 'gw0', 'gw1', ..., 'gw(n-1)'}
+ where n is the number of workers being used by pytest-xdist
+ """
+ vars_ = list(os.environ.keys())
+ if "PYTEST_XDIST_WORKER" in vars_ or "PYTEST_XDIST_WORKER_COUNT" in vars_:
+ return os.environ["PYTEST_XDIST_WORKER"]
+ else:
+ return "master"
+
+
+def read_file_list() -> list[Path]:
+ """Returns a list of paths to all files that currently exist in 'openml/tests/files/'
+
+ :return: List[Path]
+ """
+ test_files_dir = Path(__file__).parent / "files"
+ return [f for f in test_files_dir.rglob("*") if f.is_file()]
+
+
+def compare_delete_files(old_list: list[Path], new_list: list[Path]) -> None:
+ """Deletes files that are there in the new_list but not in the old_list
+
+ :param old_list: List[Path]
+ :param new_list: List[Path]
+ :return: None
+ """
+ file_list = list(set(new_list) - set(old_list))
+ for file in file_list:
+ os.remove(file)
+ logger.info(f"Deleted from local: {file}")
+
+
+def delete_remote_files(tracker, flow_names) -> None:
+ """Function that deletes the entities passed as input, from the OpenML test server
+
+ The TestBase class in openml/testing.py has an attribute called publish_tracker.
+ This function expects the dictionary of the same structure.
+ It is a dictionary of lists, where the keys are entity types, while the values are
+ lists of integer IDs, except for key 'flow' where the value is a tuple (ID, flow name).
+
+ Iteratively, multiple POST requests are made to the OpenML test server using
+ openml.utils._delete_entity() to remove the entities uploaded by all the unit tests.
+
+ :param tracker: Dict
+ :return: None
+ """
+ openml.config.server = TestBase.test_server
+ openml.config.apikey = TestBase.user_key
+
+ # reordering to delete sub flows at the end of flows
+ # sub-flows have shorter names, hence, sorting by descending order of flow name length
+ if "flow" in tracker:
+ to_sort = list(zip(tracker["flow"], flow_names))
+ flow_deletion_order = [
+ entity_id for entity_id, _ in sorted(to_sort, key=lambda x: len(x[1]), reverse=True)
+ ]
+ tracker["flow"] = [flow_deletion_order[1] for flow_id, _ in flow_deletion_order]
+
+ # deleting all collected entities published to test server
+ # 'run's are deleted first to prevent dependency issue of entities on deletion
+ logger.info(f"Entity Types: {['run', 'data', 'flow', 'task', 'study']}")
+ for entity_type in ["run", "data", "flow", "task", "study"]:
+ logger.info(f"Deleting {entity_type}s...")
+ for _i, entity in enumerate(tracker[entity_type]):
+ try:
+ openml.utils._delete_entity(entity_type, entity)
+ logger.info(f"Deleted ({entity_type}, {entity})")
+ except Exception as e:
+ logger.warning(f"Cannot delete ({entity_type},{entity}): {e}")
+
+
+def pytest_sessionstart() -> None:
+ """Pytest hook that is executed before any unit test starts
+
+ This function will be called by each of the worker processes, along with the master process
+ when they are spawned. This happens even before the collection of unit tests.
+ If number of workers, n=4, there will be a total of 5 (1 master + 4 workers) calls of this
+ function, before execution of any unit test begins. The master pytest process has the name
+ 'master' while the worker processes are named as 'gw{i}' where i = 0, 1, ..., n-1.
+ The order of process spawning is: 'master' -> random ordering of the 'gw{i}' workers.
+
+ Since, master is always executed first, it is checked if the current process is 'master' and
+ store a list of strings of paths of all files in the directory (pre-unit test snapshot).
+
+ :return: None
+ """
+ # file_list is global to maintain the directory snapshot during tear down
+ global file_list
+ worker = worker_id()
+ if worker == "master":
+ file_list = read_file_list()
+
+
+def pytest_sessionfinish() -> None:
+ """Pytest hook that is executed after all unit tests of a worker ends
+
+ This function will be called by each of the worker processes, along with the master process
+ when they are done with the unit tests allocated to them.
+ If number of workers, n=4, there will be a total of 5 (1 master + 4 workers) calls of this
+ function, before execution of any unit test begins. The master pytest process has the name
+ 'master' while the worker processes are named as 'gw{i}' where i = 0, 1, ..., n-1.
+ The order of invocation is: random ordering of the 'gw{i}' workers -> 'master'.
+
+ Since, master is always executed last, it is checked if the current process is 'master' and,
+ * Compares file list with pre-unit test snapshot and deletes all local files generated
+ * Iterates over the list of entities uploaded to test server and deletes them remotely
+
+ :return: None
+ """
+ # allows access to the file_list read in the set up phase
+ global file_list
+ worker = worker_id()
+ logger.info(f"Finishing worker {worker}")
+
+ # Test file deletion
+ logger.info(f"Deleting files uploaded to test server for worker {worker}")
+ delete_remote_files(TestBase.publish_tracker, TestBase.flow_name_tracker)
+
+ if worker == "master":
+ # Local file deletion
+ new_file_list = read_file_list()
+ compare_delete_files(file_list, new_file_list)
+
+ # Delete any test dirs that remain
+ # In edge cases due to a mixture of pytest parametrization and oslo concurrency,
+ # some file lock are created after leaving the test. This removes these files!
+ test_files_dir = Path(__file__).parent.parent / "openml"
+ for f in test_files_dir.glob("tests.*"):
+ if f.is_dir():
+ shutil.rmtree(f)
+
+ logger.info("Local files deleted")
+
+ logger.info(f"{worker} is killed")
+
+
+def pytest_configure(config):
+ config.addinivalue_line("markers", "sklearn: marks tests that use scikit-learn")
+
+
+def pytest_addoption(parser):
+ parser.addoption(
+ "--long",
+ action="store_true",
+ default=False,
+ help="Run the long version of tests which support both short and long scenarios.",
+ )
+
+
+def _expected_static_cache_state(root_dir: Path) -> list[Path]:
+ _c_root_dir = root_dir / "org" / "openml" / "test"
+ res_paths = [root_dir, _c_root_dir]
+
+ for _d in ["datasets", "tasks", "runs", "setups"]:
+ res_paths.append(_c_root_dir / _d)
+
+ for _id in ["-1", "2"]:
+ tmp_p = _c_root_dir / "datasets" / _id
+ res_paths.extend(
+ [
+ tmp_p / "dataset.arff",
+ tmp_p / "features.xml",
+ tmp_p / "qualities.xml",
+ tmp_p / "description.xml",
+ ]
+ )
+
+ res_paths.append(_c_root_dir / "datasets" / "30" / "dataset_30.pq")
+ res_paths.append(_c_root_dir / "runs" / "1" / "description.xml")
+ res_paths.append(_c_root_dir / "setups" / "1" / "description.xml")
+
+ for _id in ["1", "3", "1882"]:
+ tmp_p = _c_root_dir / "tasks" / _id
+ res_paths.extend(
+ [
+ tmp_p / "datasplits.arff",
+ tmp_p / "task.xml",
+ ]
+ )
+
+ return res_paths
+
+
+def assert_static_test_cache_correct(root_dir: Path) -> None:
+ for p in _expected_static_cache_state(root_dir):
+ assert p.exists(), f"Expected path {p} exists"
+
+
+@pytest.fixture(scope="class")
+def long_version(request):
+ request.cls.long_version = request.config.getoption("--long")
+
+
+@pytest.fixture(scope="session")
+def test_files_directory() -> Path:
+ return Path(__file__).parent / "files"
+
+
+@pytest.fixture(scope="session")
+def test_api_key() -> str:
+ return TestBase.user_key
+
+
+@pytest.fixture(autouse=True, scope="function")
+def verify_cache_state(test_files_directory) -> Iterator[None]:
+ assert_static_test_cache_correct(test_files_directory)
+ yield
+ assert_static_test_cache_correct(test_files_directory)
+
+
+@pytest.fixture(autouse=True, scope="session")
+def as_robot() -> Iterator[None]:
+ policy = openml.config.retry_policy
+ n_retries = openml.config.connection_n_retries
+ openml.config.set_retry_policy("robot", n_retries=20)
+ yield
+ openml.config.set_retry_policy(policy, n_retries)
+
+
+@pytest.fixture(autouse=True)
+def with_server(request):
+ if "production_server" in request.keywords:
+ openml.config.server = "https://www.openml.org/api/v1/xml"
+ openml.config.apikey = None
+ yield
+ return
+ openml.config.server = f"{openml.config.TEST_SERVER_URL}/api/v1/xml"
+ openml.config.apikey = TestBase.user_key
+ yield
+
+
+@pytest.fixture(autouse=True)
+def with_test_cache(test_files_directory, request):
+ if not test_files_directory.exists():
+ raise ValueError(
+ f"Cannot find test cache dir, expected it to be {test_files_directory!s}!",
+ )
+ _root_cache_directory = openml.config._root_cache_directory
+ tmp_cache = test_files_directory / request.node.name
+ openml.config.set_root_cache_directory(tmp_cache)
+ yield
+ openml.config.set_root_cache_directory(_root_cache_directory)
+ if tmp_cache.exists():
+ shutil.rmtree(tmp_cache)
+
+
+@pytest.fixture
+def static_cache_dir():
+ return Path(__file__).parent / "files"
+
+@pytest.fixture
+def workdir(tmp_path):
+ original_cwd = Path.cwd()
+ os.chdir(tmp_path)
+ yield tmp_path
+ os.chdir(original_cwd)
diff --git a/tests/datasets/test_datasets.py b/tests/datasets/test_datasets.py
deleted file mode 100644
index 71fe69de5..000000000
--- a/tests/datasets/test_datasets.py
+++ /dev/null
@@ -1,238 +0,0 @@
-import unittest
-import os
-import sys
-
-if sys.version_info[0] >= 3:
- from unittest import mock
-else:
- import mock
-
-import scipy.sparse
-
-import openml
-from openml import OpenMLDataset
-from openml.exceptions import OpenMLCacheException, PyOpenMLError
-from openml.util import is_string
-from openml.testing import TestBase
-
-from openml.datasets.functions import (_get_cached_dataset,
- _get_cached_dataset_features,
- _get_cached_datasets,
- _get_dataset_description,
- _get_dataset_arff,
- _get_dataset_features,
- _get_dataset_qualities, get_dataset)
-
-
-class TestOpenMLDataset(TestBase):
-
- def test__list_cached_datasets(self):
- openml.config.set_cache_directory(self.static_cache_dir)
- cached_datasets = openml.datasets.functions._list_cached_datasets()
- self.assertIsInstance(cached_datasets, list)
- self.assertEqual(len(cached_datasets), 2)
- self.assertIsInstance(cached_datasets[0], int)
-
- @mock.patch('openml.datasets.functions._list_cached_datasets')
- def test__get_cached_datasets(self, _list_cached_datasets_mock):
- openml.config.set_cache_directory(self.static_cache_dir)
- _list_cached_datasets_mock.return_value = [-1, 2]
- datasets = _get_cached_datasets()
- self.assertIsInstance(datasets, dict)
- self.assertEqual(len(datasets), 2)
- self.assertIsInstance(list(datasets.values())[0], OpenMLDataset)
-
- def test__get_cached_dataset(self, ):
- openml.config.set_cache_directory(self.static_cache_dir)
- dataset = _get_cached_dataset(2)
- features = _get_cached_dataset_features(2)
- self.assertIsInstance(dataset, OpenMLDataset)
- self.assertTrue(len(dataset.features) > 0)
- self.assertTrue(len(dataset.features) == len(features))
-
- def test_get_chached_dataset_description(self):
- openml.config.set_cache_directory(self.static_cache_dir)
- description = openml.datasets.functions._get_cached_dataset_description(2)
- self.assertIsInstance(description, dict)
-
- def test_get_cached_dataset_description_not_cached(self):
- openml.config.set_cache_directory(self.static_cache_dir)
- self.assertRaisesRegexp(OpenMLCacheException, "Dataset description for "
- "dataset id 3 not cached",
- openml.datasets.functions._get_cached_dataset_description,
- 3)
-
- def test_get_cached_dataset_arff(self):
- openml.config.set_cache_directory(self.static_cache_dir)
- description = openml.datasets.functions._get_cached_dataset_arff(
- dataset_id=2)
- self.assertIsInstance(description, str)
-
- def test_get_cached_dataset_arff_not_cached(self):
- openml.config.set_cache_directory(self.static_cache_dir)
- self.assertRaisesRegexp(OpenMLCacheException, "ARFF file for "
- "dataset id 3 not cached",
- openml.datasets.functions._get_cached_dataset_arff,
- 3)
-
- def _check_dataset(self, dataset):
- self.assertEqual(type(dataset), dict)
- self.assertGreaterEqual(len(dataset), 2)
- self.assertIn('did', dataset)
- self.assertIsInstance(dataset['did'], int)
- self.assertIn('status', dataset)
- self.assertTrue(is_string(dataset['status']))
- self.assertIn(dataset['status'], ['in_preparation', 'active',
- 'deactivated'])
-
- def test_list_datasets(self):
- # We can only perform a smoke test here because we test on dynamic
- # data from the internet...
- datasets = openml.datasets.list_datasets()
- # 1087 as the number of datasets on openml.org
- self.assertGreaterEqual(len(datasets), 1087)
- for did in datasets:
- self._check_dataset(datasets[did])
-
- def test_list_datasets_by_tag(self):
- datasets = openml.datasets.list_datasets(tag='uci')
- self.assertGreaterEqual(len(datasets), 5)
- for did in datasets:
- self._check_dataset(datasets[did])
-
- def test_list_datasets_paginate(self):
- size = 10
- max = 100
- for i in range(0, max, size):
- datasets = openml.datasets.list_datasets(offset=i, size=size)
- self.assertGreaterEqual(size, len(datasets))
- for did in datasets:
- self._check_dataset(datasets[did])
-
- @unittest.skip('See https://github.com/openml/openml-python/issues/149')
- def test_check_datasets_active(self):
- active = openml.datasets.check_datasets_active([1, 17])
- self.assertTrue(active[1])
- self.assertFalse(active[17])
- self.assertRaisesRegexp(ValueError, 'Could not find dataset 79 in OpenML'
- ' dataset list.',
- openml.datasets.check_datasets_active, [79])
-
- def test_get_datasets(self):
- dids = [1, 2]
- datasets = openml.datasets.get_datasets(dids)
- self.assertEqual(len(datasets), 2)
- self.assertTrue(os.path.exists(os.path.join(
- openml.config.get_cache_directory(), "datasets", "1", "description.xml")))
- self.assertTrue(os.path.exists(os.path.join(
- openml.config.get_cache_directory(), "datasets", "2", "description.xml")))
- self.assertTrue(os.path.exists(os.path.join(
- openml.config.get_cache_directory(), "datasets", "1", "dataset.arff")))
- self.assertTrue(os.path.exists(os.path.join(
- openml.config.get_cache_directory(), "datasets", "2", "dataset.arff")))
- self.assertTrue(os.path.exists(os.path.join(
- openml.config.get_cache_directory(), "datasets", "1", "features.xml")))
- self.assertTrue(os.path.exists(os.path.join(
- openml.config.get_cache_directory(), "datasets", "2", "features.xml")))
- self.assertTrue(os.path.exists(os.path.join(
- openml.config.get_cache_directory(), "datasets", "1", "qualities.xml")))
- self.assertTrue(os.path.exists(os.path.join(
- openml.config.get_cache_directory(), "datasets", "2", "qualities.xml")))
-
- def test_get_dataset(self):
- dataset = openml.datasets.get_dataset(1)
- self.assertEqual(type(dataset), OpenMLDataset)
- self.assertEqual(dataset.name, 'anneal')
- self.assertTrue(os.path.exists(os.path.join(
- openml.config.get_cache_directory(), "datasets", "1", "description.xml")))
- self.assertTrue(os.path.exists(os.path.join(
- openml.config.get_cache_directory(), "datasets", "1", "dataset.arff")))
- self.assertTrue(os.path.exists(os.path.join(
- openml.config.get_cache_directory(), "datasets", "1", "features.xml")))
- self.assertTrue(os.path.exists(os.path.join(
- openml.config.get_cache_directory(), "datasets", "1", "qualities.xml")))
-
- def test_get_dataset_with_string(self):
- dataset = openml.datasets.get_dataset(373)
- self.assertRaises(PyOpenMLError, dataset._get_arff, 'arff')
- self.assertRaises(PyOpenMLError, dataset.get_data)
-
- def test_get_dataset_sparse(self):
- dataset = openml.datasets.get_dataset(1571)
- X = dataset.get_data()
- self.assertIsInstance(X, scipy.sparse.csr_matrix)
-
- def test_download_rowid(self):
- # Smoke test which checks that the dataset has the row-id set correctly
- did = 164
- dataset = openml.datasets.get_dataset(did)
- self.assertEqual(dataset.row_id_attribute, 'instance')
-
- def test__get_dataset_description(self):
- description = _get_dataset_description(self.workdir, 2)
- self.assertIsInstance(description, dict)
- description_xml_path = os.path.join(self.workdir,
- 'description.xml')
- self.assertTrue(os.path.exists(description_xml_path))
-
- def test__getarff_path_dataset_arff(self):
- openml.config.set_cache_directory(self.static_cache_dir)
- description = openml.datasets.functions._get_cached_dataset_description(2)
- arff_path = _get_dataset_arff(self.workdir, description)
- self.assertIsInstance(arff_path, str)
- self.assertTrue(os.path.exists(arff_path))
-
- def test__get_dataset_features(self):
- features = _get_dataset_features(self.workdir, 2)
- self.assertIsInstance(features, dict)
- features_xml_path = os.path.join(self.workdir, 'features.xml')
- self.assertTrue(os.path.exists(features_xml_path))
-
- def test__get_dataset_qualities(self):
- # Only a smoke check
- qualities = _get_dataset_qualities(self.workdir, 2)
- self.assertIsInstance(qualities, dict)
-
- def test_deletion_of_cache_dir(self):
- # Simple removal
- did_cache_dir = openml.datasets.functions.\
- _create_dataset_cache_directory(1)
- self.assertTrue(os.path.exists(did_cache_dir))
- openml.datasets.functions._remove_dataset_cache_dir(did_cache_dir)
- self.assertFalse(os.path.exists(did_cache_dir))
-
- # Use _get_dataset_arff to load the description, trigger an exception in the
- # test target and have a slightly higher coverage
- @mock.patch('openml.datasets.functions._get_dataset_arff')
- def test_deletion_of_cache_dir_faulty_download(self, patch):
- patch.side_effect = Exception('Boom!')
- self.assertRaisesRegexp(Exception, 'Boom!', openml.datasets.get_dataset,
- 1)
- datasets_cache_dir = os.path.join(self.workdir, 'datasets')
- self.assertEqual(len(os.listdir(datasets_cache_dir)), 0)
-
- def test_publish_dataset(self):
- dataset = openml.datasets.get_dataset(3)
- file_path = os.path.join(openml.config.get_cache_directory(),
- "datasets", "3", "dataset.arff")
- dataset = OpenMLDataset(
- name="anneal", version=1, description="test",
- format="ARFF", licence="public", default_target_attribute="class", data_file=file_path)
- dataset.publish()
- self.assertIsInstance(dataset.dataset_id, int)
-
- def test__retrieve_class_labels(self):
- openml.config.set_cache_directory(self.static_cache_dir)
- labels = openml.datasets.get_dataset(2).retrieve_class_labels()
- self.assertEqual(labels, ['1', '2', '3', '4', '5', 'U'])
- labels = openml.datasets.get_dataset(2).retrieve_class_labels(
- target_name='product-type')
- self.assertEqual(labels, ['C', 'H', 'G'])
-
- def test_upload_dataset_with_url(self):
- dataset = OpenMLDataset(
- name="UploadTestWithURL", version=1, description="test",
- format="ARFF",
- url="http://expdb.cs.kuleuven.be/expdb/data/uci/nominal/iris.arff")
- dataset.publish()
- self.assertIsInstance(dataset.dataset_id, int)
diff --git a/tests/entities/test_dataset.py b/tests/entities/test_dataset.py
deleted file mode 100644
index 65bd3e532..000000000
--- a/tests/entities/test_dataset.py
+++ /dev/null
@@ -1,215 +0,0 @@
-import inspect
-import unittest
-import os
-
-import numpy as np
-
-from openml import OpenMLDataset
-from openml.util import is_string
-
-
-class OpenMLDatasetTest(unittest.TestCase):
-
- def setUp(self):
- # Load dataset id 1
- __file__ = inspect.getfile(OpenMLDatasetTest)
- self.directory = os.path.dirname(__file__)
- self.arff_filename = os.path.join(self.directory, "..", "files",
- "datasets", "2", "dataset.arff")
- self.pickle_filename = os.path.join(self.directory, "..", "files",
- "datasets", "2", "dataset.pkl")
- self.dataset = OpenMLDataset(
- 1, "anneal", 2, "Lorem ipsum.", "arff", None, None, None,
- "2014-04-06 23:19:24", None, "Public",
- "http://openml.liacs.nl/files/download/2/dataset_2_anneal.ORIG.arff",
- "class", None, None, None, None, None, None, None, None, None,
- "939966a711925e333bf4aaadeaa71135", data_file=self.arff_filename)
-
- self.sparse_arff_filename = os.path.join(
- self.directory, "..", "files", "datasets", "-1", "dataset.arff")
- self.sparse_pickle_filename = os.path.join(
- self.directory, "..", "files", "datasets", "-1", "dataset.pkl")
- self.sparse_dataset = OpenMLDataset(
- -1, "dexter", -1, "Lorem ipsum.", "arff", None, None, None, None,
- None, "Public",
- "http://www.cs.ubc.ca/labs/beta/Projects/autoweka/datasets/dexter.zip",
- "class", None, None, None, None, None, None, None, None, None,
- None, data_file=self.sparse_arff_filename)
-
- def tearDown(self):
- for file_ in [self.pickle_filename, self.sparse_pickle_filename]:
- os.remove(file_)
-
- ##########################################################################
- # Pandas
-
- @unittest.skip("Does not work right now")
- def test_get_arff(self):
- rval = self.dataset.get_arff()
- self.assertIsInstance(rval, tuple)
- self.assertIsInstance(rval[0], np.ndarray)
- self.assertTrue(hasattr(rval[1], '__dict__'))
- self.assertEqual(rval[0].shape, (898, ))
-
- def test_get_data(self):
- # Basic usage
- rval = self.dataset.get_data()
- self.assertIsInstance(rval, np.ndarray)
- self.assertEqual(rval.dtype, np.float32)
- self.assertEqual((898, 39), rval.shape)
- rval, categorical = self.dataset.get_data(
- return_categorical_indicator=True)
- self.assertEqual(len(categorical), 39)
- self.assertTrue(all([isinstance(cat, bool) for cat in categorical]))
- rval, attribute_names = self.dataset.get_data(
- return_attribute_names=True)
- self.assertEqual(len(attribute_names), 39)
- self.assertTrue(all([is_string(att) for att in attribute_names]))
-
- def test_get_sparse_dataset(self):
- rval = self.sparse_dataset.get_data()
- self.assertIsInstance(rval, np.ndarray)
- self.assertEqual(rval.dtype, np.float32)
- self.assertEqual((2, 20001), rval.shape)
- rval, categorical = self.sparse_dataset.get_data(
- return_categorical_indicator=True)
- self.assertIsInstance(rval, np.ndarray)
- self.assertEqual(len(categorical), 20001)
- self.assertTrue(all([isinstance(cat, bool) for cat in categorical]))
- rval, attribute_names = self.sparse_dataset.get_data(
- return_attribute_names=True)
- self.assertIsInstance(rval, np.ndarray)
- self.assertEqual(len(attribute_names), 20001)
- self.assertTrue(all([is_string(att) for att in attribute_names]))
-
- def test_get_data_with_target(self):
- X, y = self.dataset.get_data(target="class")
- self.assertIsInstance(X, np.ndarray)
- self.assertEqual(X.dtype, np.float32)
- self.assertIn(y.dtype, [np.int32, np.int64])
- self.assertEqual(X.shape, (898, 38))
- X, y, attribute_names = self.dataset.get_data(
- target="class", return_attribute_names=True)
- self.assertEqual(len(attribute_names), 38)
- self.assertNotIn("class", attribute_names)
- self.assertEqual(y.shape, (898, ))
-
- def test_get_sparse_dataset_with_target(self):
- X, y = self.sparse_dataset.get_data(target="class")
- self.assertIsInstance(X, np.ndarray)
- self.assertEqual(X.dtype, np.float32)
- self.assertIsInstance(y, np.ndarray)
- self.assertIn(y.dtype, [np.int32, np.int64])
- self.assertEqual(X.shape, (2, 20000))
- X, y, attribute_names = self.sparse_dataset.get_data(
- target="class", return_attribute_names=True)
- self.assertIsInstance(X, np.ndarray)
- self.assertEqual(len(attribute_names), 20000)
- self.assertNotIn("class", attribute_names)
- self.assertEqual(y.shape, (2, ))
-
- def test_get_data_with_rowid(self):
- self.dataset.row_id_attribute = "condition"
- rval, categorical = self.dataset.get_data(
- include_row_id=True, return_categorical_indicator=True)
- self.assertEqual(rval.dtype, np.float32)
- self.assertEqual(rval.shape, (898, 39))
- self.assertEqual(len(categorical), 39)
- rval, categorical = self.dataset.get_data(
- include_row_id=False, return_categorical_indicator=True)
- self.assertEqual(rval.dtype, np.float32)
- self.assertEqual(rval.shape, (898, 38))
- self.assertEqual(len(categorical), 38)
-
- # TODO this is not yet supported!
- #rowid = ["condition", "formability"]
- #self.dataset.row_id_attribute = rowid
- #rval = self.dataset.get_pandas(include_row_id=False)
-
- def test_get_sparse_dataset_with_rowid(self):
- self.sparse_dataset.row_id_attribute = "a_0"
- rval, categorical = self.sparse_dataset.get_data(
- include_row_id=True, return_categorical_indicator=True)
- self.assertIsInstance(rval, np.ndarray)
- self.assertEqual(rval.dtype, np.float32)
- self.assertEqual(rval.shape, (2, 20001))
- self.assertEqual(len(categorical), 20001)
- rval, categorical = self.sparse_dataset.get_data(
- include_row_id=False, return_categorical_indicator=True)
- self.assertIsInstance(rval, np.ndarray)
- self.assertEqual(rval.dtype, np.float32)
- self.assertEqual(rval.shape, (2, 20000))
- self.assertEqual(len(categorical), 20000)
-
- # TODO this is not yet supported!
- # rowid = ["condition", "formability"]
- #self.dataset.row_id_attribute = rowid
- #rval = self.dataset.get_pandas(include_row_id=False)
-
- def test_get_data_with_ignore_attributes(self):
- self.dataset.ignore_attributes = "condition"
- rval = self.dataset.get_data(include_ignore_attributes=True)
- self.assertEqual(rval.dtype, np.float32)
- self.assertEqual(rval.shape, (898, 39))
- rval, categorical = self.dataset.get_data(
- include_ignore_attributes=True, return_categorical_indicator=True)
- self.assertEqual(len(categorical), 39)
- rval = self.dataset.get_data(include_ignore_attributes=False)
- self.assertEqual(rval.dtype, np.float32)
- self.assertEqual(rval.shape, (898, 38))
- rval, categorical = self.dataset.get_data(
- include_ignore_attributes=False, return_categorical_indicator=True)
- self.assertEqual(len(categorical), 38)
- # TODO test multiple ignore attributes!
-
- def test_get_sparse_dataset_with_ignore_attributes(self):
- self.sparse_dataset.ignore_attributes = "a_0"
- rval = self.sparse_dataset.get_data(include_ignore_attributes=True)
- self.assertIsInstance(rval, np.ndarray)
- self.assertEqual(rval.dtype, np.float32)
- self.assertEqual(rval.shape, (2, 20001))
- rval, categorical = self.sparse_dataset.get_data(
- include_ignore_attributes=True, return_categorical_indicator=True)
- self.assertIsInstance(rval, np.ndarray)
- self.assertEqual(len(categorical), 20001)
- rval = self.sparse_dataset.get_data(include_ignore_attributes=False)
- self.assertIsInstance(rval, np.ndarray)
- self.assertEqual(rval.dtype, np.float32)
- self.assertEqual(rval.shape, (2, 20000))
- rval, categorical = self.sparse_dataset.get_data(
- include_ignore_attributes=False, return_categorical_indicator=True)
- self.assertIsInstance(rval, np.ndarray)
- self.assertEqual(len(categorical), 20000)
- # TODO test multiple ignore attributes!
-
- def test_get_data_rowid_and_ignore_and_target(self):
- self.dataset.ignore_attributes = "condition"
- self.dataset.row_id_attribute = "hardness"
- X, y = self.dataset.get_data(target="class", include_row_id=False,
- include_ignore_attributes=False)
- self.assertEqual(X.dtype, np.float32)
- self.assertIn(y.dtype, [np.int32, np.int64])
- self.assertEqual(X.shape, (898, 36))
- X, y, categorical = self.dataset.get_data(
- target="class", return_categorical_indicator=True)
- self.assertEqual(len(categorical), 36)
- self.assertListEqual(categorical, [True] * 3 + [False] + [True] * 2 + [
- False] + [True] * 23 + [False] * 3 + [True] * 3)
- self.assertEqual(y.shape, (898, ))
-
- def test_get_sparse_dataset_rowid_and_ignore_and_target(self):
- self.sparse_dataset.ignore_attributes = "a_0"
- self.sparse_dataset.row_id_attribute = "a_1"
- X, y = self.sparse_dataset.get_data(
- target="class", include_row_id=False,
- include_ignore_attributes=False)
- self.assertIsInstance(X, np.ndarray)
- self.assertEqual(X.dtype, np.float32)
- self.assertIn(y.dtype, [np.int32, np.int64])
- self.assertEqual(X.shape, (2, 19998))
- X, y, categorical = self.sparse_dataset.get_data(
- target="class", return_categorical_indicator=True)
- self.assertIsInstance(X, np.ndarray)
- self.assertEqual(len(categorical), 19998)
- self.assertListEqual(categorical, [False] * 19998)
- self.assertEqual(y.shape, (2, ))
diff --git a/tests/entities/test_split.py b/tests/entities/test_split.py
deleted file mode 100644
index 015df7756..000000000
--- a/tests/entities/test_split.py
+++ /dev/null
@@ -1,70 +0,0 @@
-import inspect
-import os
-import unittest
-
-import numpy as np
-
-from openml import OpenMLSplit
-
-
-class OpenMLSplitTest(unittest.TestCase):
- def setUp(self):
- __file__ = inspect.getfile(OpenMLSplitTest)
- self.directory = os.path.dirname(__file__)
- # This is for dataset
- self.arff_filename = os.path.join(
- self.directory, "..", "files", "tasks", "1882", "datasplits.arff")
- self.pd_filename = self.arff_filename.replace(".arff", ".pkl")
-
- def tearDown(self):
- try:
- os.remove(self.pd_filename)
- except:
- pass
-
- def test_eq(self):
- split = OpenMLSplit._from_arff_file(self.arff_filename)
- self.assertEqual(split, split)
-
- split2 = OpenMLSplit._from_arff_file(self.arff_filename)
- split2.name = "a"
- self.assertNotEqual(split, split2)
-
- split2 = OpenMLSplit._from_arff_file(self.arff_filename)
- split2.description = "a"
- self.assertNotEqual(split, split2)
-
- split2 = OpenMLSplit._from_arff_file(self.arff_filename)
- split2.split[10] = dict()
- self.assertNotEqual(split, split2)
-
- split2 = OpenMLSplit._from_arff_file(self.arff_filename)
- split2.split[0][10] = dict()
- self.assertNotEqual(split, split2)
-
- def test_from_arff_file(self):
- split = OpenMLSplit._from_arff_file(self.arff_filename)
- self.assertIsInstance(split.split, dict)
- self.assertIsInstance(split.split[0], dict)
- self.assertIsInstance(split.split[0][0][0], np.ndarray)
- self.assertIsInstance(split.split[0][0].train, np.ndarray)
- self.assertIsInstance(split.split[0][0].train, np.ndarray)
- self.assertIsInstance(split.split[0][0][1], np.ndarray)
- self.assertIsInstance(split.split[0][0].test, np.ndarray)
- self.assertIsInstance(split.split[0][0].test, np.ndarray)
- for i in range(10):
- for j in range(10):
- self.assertGreaterEqual(split.split[i][j].train.shape[0], 808)
- self.assertGreaterEqual(split.split[i][j].test.shape[0], 89)
- self.assertEqual(split.split[i][j].train.shape[0] +
- split.split[i][j].test.shape[0], 898)
-
- def test_get_split(self):
- split = OpenMLSplit._from_arff_file(self.arff_filename)
- train_split, test_split = split.get(fold=5, repeat=2)
- self.assertEqual(train_split.shape[0], 808)
- self.assertEqual(test_split.shape[0], 90)
- self.assertRaisesRegexp(ValueError, "Repeat 10 not known",
- split.get, 10, 2)
- self.assertRaisesRegexp(ValueError, "Fold 10 not known",
- split.get, 2, 10)
diff --git a/tests/examples/test_OpenMLDemo.py b/tests/examples/test_OpenMLDemo.py
deleted file mode 100644
index 6383a7ce1..000000000
--- a/tests/examples/test_OpenMLDemo.py
+++ /dev/null
@@ -1,59 +0,0 @@
-import os
-import unittest
-import shutil
-import sys
-
-import nbformat
-from nbconvert.preprocessors import ExecutePreprocessor
-from nbconvert.preprocessors.execute import CellExecutionError
-
-
-class OpenMLDemoTest(unittest.TestCase):
- def setUp(self):
- python_version = sys.version_info[0]
- self.kernel_name = 'python%d' % python_version
- self.this_file_directory = os.path.dirname(__file__)
- self.notebook_output_directory = os.path.join(
- self.this_file_directory, '.out')
-
- try:
- shutil.rmtree(self.notebook_output_directory)
- except:
- pass
-
- try:
- os.makedirs(self.notebook_output_directory)
- except:
- pass
-
- def _test_notebook(self, notebook_name):
- notebook_name = 'OpenMLDemo.ipynb'
-
- notebook_filename = os.path.abspath(os.path.join(
- self.this_file_directory, '..', '..', 'examples', notebook_name))
- notebook_filename_out = os.path.join(
- self.notebook_output_directory, notebook_name)
-
- with open(notebook_filename) as f:
- nb = nbformat.read(f, as_version=4)
- nb.metadata.get('kernelspec', {})['name'] = self.kernel_name
- ep = ExecutePreprocessor(kernel_name=self.kernel_name)
-
- try:
- ep.preprocess(nb, {'metadata': {'path': self.this_file_directory}})
- except CellExecutionError as e:
- msg = 'Error executing the notebook "%s". ' % notebook_filename
- msg += 'See notebook "%s" for the traceback.\n\n' % notebook_filename_out
- msg += e.traceback
- self.fail(msg)
- finally:
- with open(notebook_filename_out, mode='wt') as f:
- nbformat.write(nb, f)
-
- @unittest.skip('SKIP for now until tests work again.')
- def test_OpenMLDemo(self):
- self._test_notebook('OpenMLDemo.ipynb')
-
- @unittest.skip('SKIP for now until tests work again.')
- def test_PyOpenML(self):
- self._test_notebook('PyOpenML.ipynb')
diff --git a/tests/files/localhost_8000 b/tests/files/localhost_8000
new file mode 120000
index 000000000..334c709ef
--- /dev/null
+++ b/tests/files/localhost_8000
@@ -0,0 +1 @@
+org/openml/test
\ No newline at end of file
diff --git a/tests/files/misc/features_with_whitespaces.xml b/tests/files/misc/features_with_whitespaces.xml
new file mode 100644
index 000000000..2b542d167
--- /dev/null
+++ b/tests/files/misc/features_with_whitespaces.xml
@@ -0,0 +1,22 @@
+
+
+ 0
+ V1
+ numeric
+ false
+ false
+ false
+ 0
+
+
+ 1
+ V42
+ nominal
+ - 50000.
+ 50000+.
+ false
+ false
+ false
+ 0
+
+
diff --git a/tests/files/misc/trace.arff b/tests/files/misc/trace.arff
new file mode 100644
index 000000000..8690f2ec6
--- /dev/null
+++ b/tests/files/misc/trace.arff
@@ -0,0 +1,519 @@
+@RELATION openml_task_11_predictions
+
+@ATTRIBUTE repeat NUMERIC
+@ATTRIBUTE fold NUMERIC
+@ATTRIBUTE iteration NUMERIC
+@ATTRIBUTE evaluation NUMERIC
+@ATTRIBUTE selected {true, false}
+@ATTRIBUTE parameter_classifier__C STRING
+@ATTRIBUTE parameter_classifier__coef0 STRING
+@ATTRIBUTE parameter_classifier__degree STRING
+@ATTRIBUTE parameter_classifier__gamma STRING
+@ATTRIBUTE parameter_classifier__shrinking STRING
+@ATTRIBUTE parameter_classifier__tol STRING
+@ATTRIBUTE parameter_imputation__strategy STRING
+
+@DATA
+0,0,0,0.460854092527,false,33.071372568665474,0.3203404220269961,0.8859287582405098,0.00045025152486913615,1,0.06598057156661737,'\"median\"'
+0,0,1,0.0604982206406,false,3815.4955808746367,-0.05227585926333217,4.167506391525767,0.00030404447355728643,0,0.04572741200480247,'\"most_frequent\"'
+0,0,2,1.0,true,0.12683336141796175,-0.8321891277755394,2.4581790396097802,5.062104629833274,0,9.776101771944316e-05,'\"most_frequent\"'
+0,0,3,0.460854092527,false,58.33480225628005,0.6887862821155794,0.5895621984985993,1.1197743687594635,0,0.14544346117907545,'\"median\"'
+0,0,4,0.460854092527,false,230.22094777454336,0.6391055065010833,0.49303767543903143,2.1132526784052312,1,0.00022433108964218734,'\"mean\"'
+0,0,5,0.9128113879,false,103.4484292233199,-0.4854493431211337,1.6399972262638856,0.058762072174774305,0,0.011319317732867729,'\"median\"'
+0,0,6,0.460854092527,false,1262.9544719090748,-0.8706043995064485,-0.6309385945794426,1.4576518307659585,1,0.003603391903048425,'\"mean\"'
+0,0,7,0.989323843416,false,2370.2380346347386,-0.9398419260894257,3.605867948929002,9.697351991518785,0,0.12768625145372015,'\"median\"'
+0,0,8,0.871886120996,false,10.708185496789293,0.8297646072600462,1.0265064345672414,0.0015171705919051544,0,0.00014143549626447738,'\"most_frequent\"'
+0,0,9,0.460854092527,false,546.5520333376504,1.0700243280950053,0.0007493844856911736,0.002420070901523877,0,0.054043886894401476,'\"median\"'
+0,0,10,0.0480427046263,false,23020.902189194603,-0.04761990870697819,2.0028508094048116,0.0001897599073475,0,3.733181656744836e-06,'\"mean\"'
+0,0,11,0.873665480427,false,0.005973097413175696,0.10804033613673322,1.1635363497627027,0.042184669785534624,0,0.0029952485934551915,'\"most_frequent\"'
+0,0,12,0.966192170819,false,0.7941529467310918,0.1412970556221817,4.394974788094363,5.474473504667687,0,0.0009656653736072529,'\"median\"'
+0,0,13,0.9128113879,false,7443.409697314099,1.4132648779744774,1.541352597564262,0.0042817833126264065,1,0.005700538880817045,'\"mean\"'
+0,0,14,0.873665480427,false,0.2436349050171504,0.814597131039096,3.073390314537452,8.952456384128966e-05,1,0.011468287217405636,'\"mean\"'
+0,0,15,0.962633451957,false,2.250509188460567,0.6628847849329401,4.3377747534170235,5.819451053940405,1,0.0839554167149034,'\"most_frequent\"'
+0,0,16,0.950177935943,false,4258.672620250725,0.43988578158957226,5.3279509706601775,12.067010331938448,0,0.047152940206782484,'\"mean\"'
+0,0,17,0.996441281139,false,0.4082057801997634,0.1074232477161669,3.1093161452406997,0.3429648864789778,1,0.007213479429046319,'\"most_frequent\"'
+0,0,18,0.864768683274,false,0.04114235281951013,0.7298903743395279,1.1154361999262175,0.02240854927440298,1,0.001610238782857859,'\"mean\"'
+0,0,19,0.873665480427,false,0.5527364497070328,0.42540728053197796,1.4353436976476306,0.00018478941725653256,1,0.012543886647778595,'\"most_frequent\"'
+0,0,20,0.870106761566,false,3.5217780669329493,0.07001801155000245,1.615989460383886,0.0005682066501002445,1,0.018274584919132163,'\"most_frequent\"'
+0,0,21,0.460854092527,false,33.864773796830676,0.520700102335918,0.9087259274299715,0.11334232846698317,1,0.0017461263941478948,'\"most_frequent\"'
+0,0,22,0.460854092527,false,6.8627945997390585,0.3526984415436743,0.79518004766004,0.008000320795849485,1,0.0037578673935801466,'\"mean\"'
+0,0,23,0.871886120996,false,1.224940151108357,-0.11224121109946009,5.526516325758739,7.140799229331201e-06,1,0.00020563331695746346,'\"mean\"'
+0,0,24,0.870106761566,false,0.09427222942310243,-0.808048327766683,1.9628728058230038,0.015636538275939107,1,8.001191362073544e-05,'\"most_frequent\"'
+0,0,25,0.871886120996,false,1.8404556479633412,-0.5704146363271635,3.7129133024367373,0.00026468617662167695,1,0.006635764247092615,'\"mean\"'
+0,0,26,0.873665480427,false,46.799580008095205,0.5190952793329281,1.0784855102345525,0.00011402199910448575,0,0.001496312450501793,'\"mean\"'
+0,0,27,0.953736654804,false,7184.172687991931,1.066421747081859,2.442717045695971,0.0015075458690755577,1,0.0005260366644281206,'\"median\"'
+0,0,28,0.893238434164,false,1.3666656723318231,-0.022826654121085033,4.433882121182842,0.02311242543259286,0,0.0010353947686532591,'\"median\"'
+0,0,29,0.873665480427,false,0.0020684882649306246,0.08492122406519559,1.8221393011271487,0.0010337291586753913,1,0.0021786401994399354,'\"mean\"'
+0,0,30,0.873665480427,false,5316.537478932054,0.06500195262922023,4.816115941666556,4.159470893074367e-06,1,0.00041131692509335246,'\"most_frequent\"'
+0,0,31,0.857651245552,false,0.0017619710299793055,-0.7217591935001806,2.661060241925342,0.11103789191656571,1,0.00024790749289802216,'\"median\"'
+0,0,32,0.9128113879,false,787.2311143331663,-0.7562793727839473,1.6192945607345584,0.006203982299904933,0,8.013845789170915e-05,'\"mean\"'
+0,0,33,0.873665480427,false,0.030090865814895226,0.4490511782184619,1.392968945378231,9.90489698222976e-05,1,0.002227760456458227,'\"most_frequent\"'
+0,0,34,0.460854092527,false,0.10831467137130846,0.5781886354478623,0.4281138580456286,0.016649678323049876,0,0.002450547990221719,'\"median\"'
+0,0,35,0.460854092527,false,20.366842082165377,-0.6606994059975078,0.8128273945556148,0.0012223733479301382,0,0.0007367496358913927,'\"mean\"'
+0,0,36,0.94128113879,false,25.12035809639323,0.6498264853509557,5.3830663343802385,3.026331235333894,0,0.014793758451378916,'\"most_frequent\"'
+0,0,37,0.460854092527,false,0.07790018628180186,-0.5617043496666525,0.7846568405239807,1.513850847264635,1,0.001614445393095381,'\"median\"'
+0,0,38,0.9128113879,false,1169.4826688628505,0.42652482933420977,1.04180858186593,0.07186733909787037,1,0.004685296365335189,'\"median\"'
+0,0,39,0.962633451957,false,0.3111503149879859,0.03840982873872345,2.8670203143989745,0.37946147815933706,0,3.6813367428061136e-05,'\"most_frequent\"'
+0,0,40,0.873665480427,false,2.0964521204498863,0.5156843237889213,5.042691522556766,0.00036374259901165426,0,8.109922336072e-06,'\"most_frequent\"'
+0,0,41,0.873665480427,false,10.610683764071359,0.5797346241190516,1.0703786315794108,2.3871467333214823e-05,1,8.087620342328147e-06,'\"most_frequent\"'
+0,0,42,0.9128113879,false,68.1989898040259,0.8830871985742957,1.6602384081026906,0.011687211967989164,1,0.012223830167725065,'\"median\"'
+0,0,43,0.989323843416,false,1.9372379505683421,-0.0005728063717822807,3.2804286112854486,0.5591534411888721,1,0.08288138847318628,'\"mean\"'
+0,0,44,0.9128113879,false,78.88645580950059,0.6642150208114938,1.2625287470895779,0.013468105949400719,1,0.00017834037912245715,'\"median\"'
+0,0,45,0.862989323843,false,2582.5949543411507,-0.13120464750481123,3.6811044017168633,0.00011805710075117711,1,0.0002601650437602319,'\"most_frequent\"'
+0,0,46,0.976868327402,false,15503.393717538293,0.17222894323234705,4.796070646429683,0.04682143447844051,0,0.0003775780645961211,'\"median\"'
+0,0,47,0.868327402135,false,0.007549122482913778,0.4399392546571857,1.9607976116811088,0.15962371198550449,1,9.815928027160892e-05,'\"most_frequent\"'
+0,0,48,0.870106761566,false,0.4368150105189183,0.11393065639120645,2.324540695399478,0.002753324941227206,0,2.4059479470398004e-05,'\"most_frequent\"'
+0,0,49,0.873665480427,false,0.1254513897684518,0.8286970801074661,3.661400010256001,0.0010936149156430214,0,8.50234143734015e-05,'\"most_frequent\"'
+0,1,0,0.953736654804,false,863.8941370601214,0.6447532297489346,5.816581664589442,2.4182439537657237,0,3.57052361739182e-05,'\"mean\"'
+0,1,1,0.879003558719,false,0.050720563285353445,-1.105166930492062,3.8023472461706094,0.00017623868145929758,0,0.0009863509492368517,'\"mean\"'
+0,1,2,0.957295373665,false,434.54886635650575,0.1367289943349579,5.769262159184547,5.633756921553203,1,3.293779052993788e-05,'\"most_frequent\"'
+0,1,3,0.992882562278,false,32.675137732169105,0.49420634063221724,3.4784162477377243,0.4497675543380719,0,0.0074159809757524805,'\"most_frequent\"'
+0,1,4,0.877224199288,false,34.41949548850553,0.2741412677686504,1.0274289029335997,0.0007931705387067778,0,0.06428907582229956,'\"median\"'
+0,1,5,0.916370106762,false,2.6836306666568914,0.30046410514003014,1.6607616731431443,0.3871558020438476,1,0.003112575654994272,'\"most_frequent\"'
+0,1,6,0.916370106762,false,351.5038507000028,0.2413586732256813,1.4200487511168274,0.2711897693359289,1,2.9079860935422102e-05,'\"most_frequent\"'
+0,1,7,0.871886120996,false,1.1605828235567535,-1.1540335087283105,1.6089295172872946,0.3269631057999799,0,5.33837606159268e-05,'\"most_frequent\"'
+0,1,8,0.875444839858,false,0.2801794916079623,0.6325827035021448,1.02532280088563,0.03491266352354559,1,0.32527852736618523,'\"mean\"'
+0,1,9,1.0,true,568.1048082291193,1.1576799858287983,2.952811638610787,0.5379804674110082,0,0.035970024976882835,'\"median\"'
+0,1,10,0.916370106762,false,7142.825077726176,0.4167960007695064,2.9476132756970794,0.0003171341158134385,1,0.018990429261504974,'\"median\"'
+0,1,11,0.460854092527,false,8033.5298531153685,-0.6312978791356147,0.2376556901939212,0.893221741106445,1,0.0032170504817701294,'\"mean\"'
+0,1,12,0.982206405694,false,11.15220272121919,1.2250141002182189,5.569926060017148,0.10141896087040897,1,0.006052704921068693,'\"most_frequent\"'
+0,1,13,0.460854092527,false,3.5464336124317457,0.085320536506025,0.1565530534470465,0.0462814107100094,0,0.011938920953418707,'\"most_frequent\"'
+0,1,14,0.877224199288,false,2.274639713896306,-0.42326263497824146,3.905490169640334,0.0004925293260751384,1,0.06248334975888174,'\"most_frequent\"'
+0,1,15,0.879003558719,false,4.263579185511151,0.545214392749332,4.947256901490285,1.4958562537725525e-05,1,0.00025563301438342903,'\"most_frequent\"'
+0,1,16,0.460854092527,false,0.2828918584755567,0.5191942550264348,0.9251668694657138,0.2222363374241719,1,1.6810846427955852e-05,'\"mean\"'
+0,1,17,0.460854092527,false,100.54385685637418,0.0977085365395323,0.5699547900468178,0.0004208063074768932,0,0.016464098011968926,'\"most_frequent\"'
+0,1,18,0.978647686833,false,7.638491625049555,0.8345459517954401,4.43034948385166,0.2776248951532424,0,0.00013828484189721006,'\"most_frequent\"'
+0,1,19,1.0,false,2.541949115037065,1.1617173694655054,2.556007893089475,5.195000608707235,0,0.00031664985748291026,'\"mean\"'
+0,1,20,0.829181494662,false,106.40233277745175,-0.8794792153757508,4.555676198727677,0.11755335634942539,1,0.015072626509841433,'\"most_frequent\"'
+0,1,21,0.875444839858,false,817.7494958114343,0.00772570205196671,1.3369203179284304,0.0004684354528626353,1,0.005201145881742862,'\"median\"'
+0,1,22,1.0,false,3.4382656032735217,0.9759705006305305,2.2037851243230273,0.6573665368149072,1,0.00012367985936014632,'\"median\"'
+0,1,23,0.991103202847,false,0.37693014438040034,0.49215110000626483,2.793839888706033,0.5286858149405526,0,0.04504540011684609,'\"median\"'
+0,1,24,0.960854092527,false,0.019120454016611613,0.07342362914284539,5.176952906486121,9.625262339641619,1,0.006864781211857609,'\"median\"'
+0,1,25,0.916370106762,false,9447.914376072922,-0.9634032233032104,1.4135630877409526,0.3795993900506162,1,0.03889788391660449,'\"median\"'
+0,1,26,0.460854092527,false,401533.78077535995,0.40838893928777437,0.533473596594473,0.01574868250697514,1,0.0019800779383653618,'\"most_frequent\"'
+0,1,27,0.943060498221,false,7240.343674477397,0.28606684774842206,2.181357142819479,0.0011765404395881654,0,3.152420510680123e-05,'\"median\"'
+0,1,28,0.879003558719,false,162.6675650031394,0.05506624060516352,1.2741625472224558,0.0008538732352301783,1,9.498622378836591e-06,'\"mean\"'
+0,1,29,0.460854092527,false,0.9276382716825005,0.7312104090499063,0.040644074448720424,0.3835094454899768,1,0.08078420902381034,'\"mean\"'
+0,1,30,0.868327402135,false,0.8518883039101244,0.5250138076748073,3.9009539133673243,0.0018006534869560008,0,0.0020818445781839263,'\"mean\"'
+0,1,31,0.877224199288,false,9.110195001087833,0.39581650627280496,2.7804139572627307,0.0004279886705489661,0,0.00047212059706202924,'\"most_frequent\"'
+0,1,32,0.460854092527,false,42129.5711168001,0.2897737852122466,-0.03686507520458293,0.5474443946560595,0,0.003675516908858429,'\"mean\"'
+0,1,33,0.875444839858,false,7.150276842172868,0.7577423758022889,1.3641495309906468,0.0047226458468449415,0,0.0014057768273858043,'\"median\"'
+0,1,34,0.460854092527,false,23818.49875845243,0.14529541299642545,0.5417557430536462,0.0011874700994864028,0,0.022753083824739635,'\"median\"'
+0,1,35,0.873665480427,false,13.226439000510782,0.6515379237373092,2.9124473050007773,0.002248968674128536,1,0.010630159026324972,'\"most_frequent\"'
+0,1,36,0.88256227758,false,117.75155763109984,1.2033541888134947,3.98374082567122,0.00063939588853538,0,1.4780180378498436e-05,'\"median\"'
+0,1,37,0.879003558719,false,6.347062506586624,0.41091445048009456,1.80734248504121,8.103749466644024e-06,1,0.0005517160467743681,'\"median\"'
+0,1,38,0.877224199288,false,5.059854061004953,-0.16438201671806701,5.43176434607806,0.00010780912861674254,1,2.8898850101289672e-06,'\"most_frequent\"'
+0,1,39,0.948398576512,false,3.858194086993066,-0.1889605916321092,5.16078207145214,0.594825095525301,1,0.0040385163039731815,'\"most_frequent\"'
+0,1,40,0.905693950178,false,177.413748127103,0.3551111571072702,4.620032816416409,0.002712561545358468,1,0.00015703462903945525,'\"median\"'
+0,1,41,0.903914590747,false,2934.268441224676,0.2916452929505362,5.176392431526145,0.0010921664463085041,1,4.3842235633006974e-05,'\"most_frequent\"'
+0,1,42,0.916370106762,false,3148.9803366070037,-0.942843818737892,1.2712578271878228,0.00899031461370604,1,0.0018710879411653583,'\"mean\"'
+0,1,43,0.873665480427,false,0.0022960550412201974,0.530508296086881,5.797883273588026,0.0006246648335201738,1,0.0012189717000259002,'\"most_frequent\"'
+0,1,44,0.843416370107,false,2.262308959825833,-0.09328189789133573,2.0018411890156598,0.005657440639048186,1,0.026332761713034024,'\"most_frequent\"'
+0,1,45,0.460854092527,false,15132.97325324137,-1.1821115479962438,0.8663881083558551,0.0010715412446165931,0,0.0015126370368210905,'\"median\"'
+0,1,46,0.916370106762,false,908.0920539873296,0.7898497620695019,1.1265443216679052,0.00830529659852941,0,0.0002719634853864867,'\"most_frequent\"'
+0,1,47,0.871886120996,false,2.516736957810146,1.0382060792675085,1.663647686328867,0.12963754233903824,1,0.024637576187371068,'\"mean\"'
+0,1,48,0.879003558719,false,0.18386593098047704,0.7210801385869583,2.2166510616419015,0.016931788355952024,1,9.587734926739557e-05,'\"mean\"'
+0,1,49,0.875444839858,false,0.0045047138891964825,0.5680258950515558,2.1789173607121657,0.00043563423419125234,1,0.03560797290235728,'\"most_frequent\"'
+0,2,0,0.914590747331,false,2917.482469876248,-0.40649696615110775,1.5327992158349089,0.0008180412893540321,0,0.00044667809852817053,'\"median\"'
+0,2,1,1.0,true,120.53262111341662,0.1742519696311548,2.852483134233391,53.8498482815957,0,9.72483273613791e-05,'\"most_frequent\"'
+0,2,2,0.893238434164,false,4864.493052308551,-0.4142932483896531,1.815214433593028,0.00010691626127791145,1,0.032009109850041156,'\"mean\"'
+0,2,3,0.996441281139,false,4308.2648092912605,0.273054205990646,3.156906547839568,0.6829754511557282,0,9.000409280250665e-05,'\"most_frequent\"'
+0,2,4,0.914590747331,false,112.0023219688152,-0.8563961667638059,1.5716149329974964,2.6850394726684925,0,0.00033136763524570586,'\"mean\"'
+0,2,5,0.893238434164,false,17.260666070279783,-0.268576464207674,2.019638276702361,0.024500199542209396,1,5.303292040865896e-05,'\"median\"'
+0,2,6,0.0551601423488,false,105.17616467392975,-0.2529621984605309,2.9268156230462608,0.0017605901013896465,1,0.0005740195878855032,'\"most_frequent\"'
+0,2,7,0.914590747331,false,39085.62427269756,0.39574567410435246,1.659474092821374,0.011499837775903268,1,0.0011926689309082197,'\"most_frequent\"'
+0,2,8,0.0533807829181,false,9.40155068840517,-1.0943614844425165,2.295536436946443,0.014591348333270608,1,5.941885216504551e-06,'\"median\"'
+0,2,9,0.873665480427,false,10.440884511682963,0.8808600846669495,2.3411548394465322,2.4889793124432783e-06,1,0.020549538409380102,'\"most_frequent\"'
+0,2,10,0.877224199288,false,432.6977566067502,0.4148163408148588,1.870757818771584,2.6835517523343027e-05,1,4.0641451327703454e-05,'\"median\"'
+0,2,11,0.905693950178,false,402.42328938790723,1.0325328807378777,2.0895131606338184,0.00024404465587080955,1,0.0014405447312712165,'\"median\"'
+0,2,12,0.871886120996,false,1.1396280487117214,-0.5952667003231793,1.2638669024057685,4.1177364747651755e-05,1,0.009460628882711434,'\"mean\"'
+0,2,13,0.944839857651,false,4.9955762901525045,0.8252456470582581,2.4623919086025614,0.04240003275307548,1,3.558968549225327e-06,'\"median\"'
+0,2,14,1.0,false,6.3733544447193315,0.5396270929997187,3.7755306227423366,0.14281234349171218,0,0.0023143498805704957,'\"most_frequent\"'
+0,2,15,0.994661921708,false,78.7390858353439,0.013308810246986214,3.246051561981843,2.4343011287637624,0,8.121903451894021e-06,'\"mean\"'
+0,2,16,0.862989323843,false,10.986324944290335,-0.5774710417559844,5.1059071275081,0.0017149318552961573,0,0.0020649897556418305,'\"median\"'
+0,2,17,0.871886120996,false,387.76620922703114,0.024691504932198433,2.926756483470834,2.106312431211724e-05,1,0.8003151251024102,'\"median\"'
+0,2,18,0.871886120996,false,0.896398092191917,0.7220633285958874,6.084682228889801,4.649691864069917e-05,1,0.0001715861888213987,'\"median\"'
+0,2,19,1.0,false,831.5246615823556,0.43431193219705777,2.7775329372876145,1.0706975184750156,1,0.0008771854759219146,'\"most_frequent\"'
+0,2,20,0.460854092527,false,10.139459280699896,0.6472845701094686,0.38749202607215716,0.0014720329953851492,1,4.6231893449620126e-05,'\"most_frequent\"'
+0,2,21,0.900355871886,false,2.161932545833139,0.3818494096004894,4.79644996357706,0.011221879080983078,0,0.0037048535862232307,'\"median\"'
+0,2,22,0.944839857651,false,166.90461162068374,0.40574270758093234,5.295892024487772,0.008288992441046469,0,0.028541720052456344,'\"median\"'
+0,2,23,0.460854092527,false,0.05670896301707122,0.5323306274206312,0.20081565033858118,9.81669474956577,1,0.00018639995469441948,'\"most_frequent\"'
+0,2,24,0.884341637011,false,5.639435247584509,-0.1539197980867354,1.3167477200970643,0.009673189363753646,0,3.0346547797259926e-05,'\"median\"'
+0,2,25,0.460854092527,false,1102.9235383940475,-0.9405555718798254,0.5775400444309327,0.0026910479578630513,0,8.945480658227282e-05,'\"most_frequent\"'
+0,2,26,0.868327402135,false,97.33347172688765,0.26576432303960484,2.990209629511563,9.726267382604767e-05,0,0.0004202115875025762,'\"median\"'
+0,2,27,0.967971530249,false,356.84742529751344,0.22580848282052285,4.1050568803186955,5.976223638053823,1,0.00012047326877060332,'\"median\"'
+0,2,28,0.88256227758,false,5.774643828849257,0.4823187700291236,5.004713434270252,0.0018381262241135019,0,0.021650521033043112,'\"most_frequent\"'
+0,2,29,0.0373665480427,false,2.4209426225254895,-1.1173201939894102,2.0799557511249245,0.0012307481464923081,0,0.004114946087748537,'\"median\"'
+0,2,30,0.460854092527,false,7055.589073074652,0.6753459520103522,0.543404354074519,0.0019707820564200816,0,0.003176299994110642,'\"median\"'
+0,2,31,0.880782918149,false,2465.9925784103134,0.3424039005864708,5.850945488921811,0.0001334208939007241,0,0.0005338732300334227,'\"mean\"'
+0,2,32,0.460854092527,false,1796.992006912984,0.25029288570446995,0.6168140745710593,1.837714365722256e-05,0,6.36185856451343e-05,'\"median\"'
+0,2,33,0.914590747331,false,1425.7691716746324,0.6012308220068647,1.4591945019814492,0.03696587883394436,0,0.0002666960979669482,'\"median\"'
+0,2,34,0.914590747331,false,2.1986599014100654,0.622366900273413,1.0149989972560707,0.6862469826992126,1,0.00017554173433155927,'\"median\"'
+0,2,35,0.9128113879,false,7106.4835699795585,0.3934931909687793,3.1591162587703527,0.00012377952996906085,0,0.17932111936813525,'\"most_frequent\"'
+0,2,36,0.855871886121,false,6.304082837120096,-0.7612161762262497,4.1454168130115265,0.09516046453780863,0,7.352217503764598e-05,'\"most_frequent\"'
+0,2,37,1.0,false,675.0340247341344,0.7478690522777716,3.797246841092111,0.05958620261888768,0,0.020169961592508372,'\"mean\"'
+0,2,38,0.871886120996,false,0.5578654803228994,0.9774799853587979,2.583797902158004,7.751349461301527e-05,1,0.00014702811000578533,'\"mean\"'
+0,2,39,0.460854092527,false,233.49621233430076,1.1950619368863742,0.5664339406282053,0.0006631386953215885,1,0.01054818403433575,'\"most_frequent\"'
+0,2,40,0.701067615658,false,5.2367730003467186,-0.7058995624398988,3.4587732695847624,0.020750140892721272,1,8.159638317596338e-06,'\"median\"'
+0,2,41,0.914590747331,false,2373.3048079141363,-0.2935583207026626,1.2890354720195167,0.02724762078683617,1,0.04324994358119785,'\"median\"'
+0,2,42,0.992882562278,false,44.03859441642581,-0.2568786844302291,3.7955136407613055,52.82293449991152,0,0.02989584755848762,'\"median\"'
+0,2,43,0.487544483986,false,47.28053958551503,-0.6491077004877063,2.4571521692515796,0.02296129915275161,1,0.001944648294208711,'\"most_frequent\"'
+0,2,44,1.0,false,0.11110288502251023,-1.2612793890225047,2.7574359089676883,5.963419120026271,0,0.0031963484451288373,'\"most_frequent\"'
+0,2,45,0.460854092527,false,24821.952304620427,0.7790786584251499,0.7804310563263012,2.7761875890402716e-05,0,0.0520677233020118,'\"most_frequent\"'
+0,2,46,0.871886120996,false,1057.6155629247066,0.06727727103798392,4.7800020416402615,0.0002808929339114073,1,0.008562221219806997,'\"mean\"'
+0,2,47,0.871886120996,false,0.002188902686159962,-0.45332723935146174,1.3461013911274153,0.00031378494529442434,1,0.0016568358678491525,'\"most_frequent\"'
+0,2,48,0.914590747331,false,2331.1046332797564,0.6290775346437019,1.4060991409583754,0.0035418979242734364,1,4.6956448051656326e-05,'\"median\"'
+0,2,49,0.975088967972,false,13453.852277280117,0.22965609549990473,3.4632016291955257,0.0025039686976331232,0,0.004147734471731875,'\"median\"'
+0,3,0,0.880782918149,false,36.470560648394375,-0.8966214779678029,5.752724492495632,3.08036041163799e-05,0,0.003436603275111555,'\"median\"'
+0,3,1,0.944839857651,false,6.803215739604396,0.45189683894888877,3.770796521761603,0.035613902234415316,1,0.0008026973615146241,'\"mean\"'
+0,3,2,0.919928825623,false,15500.58463864049,0.9381171954997347,1.993210268342693,0.0013882930859172326,1,0.008603273747073786,'\"most_frequent\"'
+0,3,3,0.0462633451957,false,49868.51325758738,-0.8146118845334804,4.5268513721780606,8.163172534510878e-05,1,3.654914065525398e-05,'\"most_frequent\"'
+0,3,4,0.983985765125,false,11606.702496743304,0.7373063684067116,5.3358784269996855,0.034809406561843614,0,4.823818592231601e-05,'\"mean\"'
+0,3,5,0.877224199288,false,134.68708873221223,0.6111069748506381,3.2041747717785842,0.0002909893585595965,1,0.0002619606964194342,'\"most_frequent\"'
+0,3,6,0.919928825623,false,218.30318690180786,-0.6152366544767235,1.7026125896598532,0.3482701685191634,1,2.707873681292823e-05,'\"most_frequent\"'
+0,3,7,0.957295373665,false,169.3957519328014,0.0661288334663952,5.873351656585527,0.7650272440568248,0,0.00010307943175173834,'\"mean\"'
+0,3,8,0.460854092527,false,24749.93597340302,0.25038480155332477,0.9633310153927003,0.0001195036138495485,1,0.07106220008147757,'\"mean\"'
+0,3,9,0.919928825623,false,24696.557250330097,0.6205112110978676,1.0278073690370961,0.002078161611577723,1,0.01697825447276031,'\"most_frequent\"'
+0,3,10,0.871886120996,false,93.98068735761689,-0.050763589122076416,1.8559056834332786,2.8535467626065227e-05,0,0.09772048197018324,'\"most_frequent\"'
+0,3,11,0.211743772242,false,3797.6317961779177,-0.5387390685456972,2.1596008383501575,0.013594327708094336,1,5.219302586871208e-05,'\"mean\"'
+0,3,12,0.919928825623,false,6.229772354976182,0.8518237957453741,1.6824844785430413,1.1051873662612577,0,0.05930156352408421,'\"most_frequent\"'
+0,3,13,0.879003558719,false,1.6866377676925652,0.5358340823902971,4.111734690971055,4.7245866231221144e-05,1,0.04795936071177684,'\"median\"'
+0,3,14,0.880782918149,false,0.41391063387232696,0.5377444651899708,2.1559557083298166,0.0016942744733519166,1,0.036914663143064366,'\"most_frequent\"'
+0,3,15,0.460854092527,false,5.512366884504672,-0.8803730353461454,0.854410960635368,0.0026695716434691104,0,0.00044869213030170567,'\"most_frequent\"'
+0,3,16,0.886120996441,false,0.08184488611823561,0.5191679645741639,2.609487217449034,0.039723169498926264,1,0.004766156493762348,'\"mean\"'
+0,3,17,0.886120996441,false,7.52891824096222,0.7812187187686961,2.239139663381396,0.0012232652318287331,0,0.04073610762222123,'\"median\"'
+0,3,18,0.879003558719,false,0.12056537679334463,0.21440132016042918,1.720447141916297,3.468660359122178e-05,1,0.009876408883300408,'\"mean\"'
+0,3,19,0.460854092527,false,6.66826029879166,0.632117572145696,-0.1183457226185094,0.0003173159874373839,1,0.051835804012669545,'\"mean\"'
+0,3,20,0.0533807829181,false,10.332459219846527,-0.11481298686939875,4.47782948340958,0.0005239167214906112,0,0.027059457473173083,'\"most_frequent\"'
+0,3,21,0.919928825623,false,17625.624777485744,-0.8945726107047862,1.6820547635710967,0.0009674240019309963,0,1.884094424255656e-05,'\"most_frequent\"'
+0,3,22,0.460854092527,false,110.89670852523328,1.0747748653803728,-0.07287791122179677,0.03445312276514631,0,0.005084347994262029,'\"most_frequent\"'
+0,3,23,0.460854092527,false,28.46243061302529,0.04887042301189956,0.3516972449050525,0.569873356357751,1,0.0006861648087918289,'\"mean\"'
+0,3,24,0.460854092527,false,34.7153567429343,0.9747657241671618,0.5237605682073911,0.06051215860382468,1,0.019826966709743894,'\"mean\"'
+0,3,25,0.879003558719,false,0.39850791860404,0.5943939454705046,1.265511917360886,2.551982533969714e-05,1,0.0006654273762318053,'\"most_frequent\"'
+0,3,26,0.919928825623,false,24.691554365946995,0.4273834954493855,1.2921748200674852,7.497599668016804,0,0.005498853516063422,'\"mean\"'
+0,3,27,0.919928825623,false,409.07472167546933,0.7538663252684674,1.3666896361658953,0.5627787015400205,1,8.976562856579272e-05,'\"most_frequent\"'
+0,3,28,0.460854092527,false,10670.53023580878,0.13341984593309417,0.9492316975880006,0.00024353623057229328,0,3.303390039393251e-05,'\"median\"'
+0,3,29,0.9128113879,false,67.54937957064368,0.5014039925496889,3.1429810017182063,0.003803286964528202,0,0.0021958631560820567,'\"mean\"'
+0,3,30,0.875444839858,false,2.7633347258151715,1.1495404970739433,5.213189165207819,0.00029504127517638527,1,0.0001587469849587,'\"median\"'
+0,3,31,0.967971530249,false,391.9678542493133,0.6379121667829053,2.8184046521906083,0.012428678580399775,1,0.0012931223794912603,'\"mean\"'
+0,3,32,0.460854092527,false,5.976761969667031,0.0669654224011946,0.8014832064513724,0.008561110762861555,0,5.5105880268936704e-05,'\"mean\"'
+0,3,33,0.919928825623,false,44.60975910846691,0.6509183241638763,1.514698861521955,0.8717002602538891,0,0.0006019907014730923,'\"mean\"'
+0,3,34,0.875444839858,false,0.32192847361076826,0.20798425883359054,4.488245904597454,0.002346491099536347,1,0.003925031083591858,'\"mean\"'
+0,3,35,0.97153024911,false,7.145576493960251,-0.3738175370194797,2.8058294697667874,0.5998635237896008,0,0.3482771355189032,'\"median\"'
+0,3,36,0.953736654804,false,3.8909173300723,0.29310733402618966,4.871878569168019,0.046421543645617507,1,4.214605110290681e-05,'\"median\"'
+0,3,37,0.919928825623,false,7814.864022609587,0.8230173359066428,1.5981749470635598,0.008865661099054933,1,0.003336161610716205,'\"most_frequent\"'
+0,3,38,0.905693950178,false,4.900322471991267,0.5267717655949231,4.252598247357612,0.005879367754428088,1,0.00780078948039277,'\"mean\"'
+0,3,39,0.879003558719,false,5.0228339837013,0.5317666323051958,4.06517438581724,6.80809087797603e-05,1,6.347007835291174e-05,'\"mean\"'
+0,3,40,0.919928825623,false,0.898695394700515,-0.11543205015990557,1.2885768380506042,0.6564830878417786,1,0.14295375882610525,'\"most_frequent\"'
+0,3,41,0.879003558719,false,325.2944426728011,0.24728137280994167,4.596494787748204,5.516304192748459e-06,0,3.780562880093987e-05,'\"mean\"'
+0,3,42,0.879003558719,false,0.02299127773279695,0.44416488045783176,1.763076810336679,0.00015167909833626168,0,0.000617261062840763,'\"most_frequent\"'
+0,3,43,1.0,true,15.935083387400466,-0.15265773669699684,2.025611701837872,3.09371358068095,0,0.001661729321061358,'\"median\"'
+0,3,44,0.893238434164,false,0.031207268822889538,0.5621949157162431,1.7817310301168474,11.434906312448602,1,0.001256239088507143,'\"most_frequent\"'
+0,3,45,1.0,false,25.11580458606462,0.9340397254952668,2.753517197588782,0.13853121636570825,0,0.008285894883302376,'\"median\"'
+0,3,46,0.943060498221,false,10053.475201191495,-0.5282967938006681,3.820086008369143,0.6157117080647511,0,0.005546889236498897,'\"mean\"'
+0,3,47,0.0462633451957,false,0.5628989435713693,-1.5064510150755743,2.7320340819146542,0.00038199471485966823,0,0.009896153781295124,'\"mean\"'
+0,3,48,0.0480427046263,false,26.52118862356242,-0.17732725804670268,2.2198847979095677,0.00045366231226925773,1,0.02092103471706673,'\"most_frequent\"'
+0,3,49,0.964412811388,false,51.24230081659039,0.18343245944076367,5.296067357739285,0.026563237944111213,1,0.0027335632304570654,'\"mean\"'
+0,4,0,0.460854092527,false,1.9975975781903055,0.6573956924820088,0.7820625823434166,0.00017153377927148482,1,0.0005046013918922219,'\"mean\"'
+0,4,1,0.918149466192,false,2729.3497094672243,0.7282622544291182,1.70431711177198,0.007717947813150589,0,0.0003079148977425983,'\"most_frequent\"'
+0,4,2,0.935943060498,false,0.028659720411386112,0.31824089262018085,4.4265625074006385,0.10193247124255471,0,0.0015935748716233377,'\"most_frequent\"'
+0,4,3,0.460854092527,false,32.848492428814716,-0.2723995797799568,0.6226748328204874,3.610641888444301e-05,1,0.007985105530085034,'\"mean\"'
+0,4,4,0.871886120996,false,42605.766598306276,-0.9967330705869719,6.024081645713997,0.25069594698674325,1,0.027075439930693015,'\"median\"'
+0,4,5,0.460854092527,false,44.2796169566102,-0.9623647847309389,0.1290293532636606,0.8639228324208229,1,0.00592070489983454,'\"median\"'
+0,4,6,0.460854092527,false,0.3937092735629269,-1.0663975956670815,0.7923737546796036,0.3172910794385085,0,0.002129728376754709,'\"most_frequent\"'
+0,4,7,0.877224199288,false,0.4752087924828423,-0.6377116278308219,1.185479064787914,0.10322086710008674,1,0.0010120078455969639,'\"median\"'
+0,4,8,0.989323843416,false,2295.615702048731,0.4675356419718959,3.1547691136827045,8.376380756884366,0,4.7340262491343674e-05,'\"mean\"'
+0,4,9,0.879003558719,false,25.491689075388596,0.2361891249719193,1.3382230813364386,0.0011356217171189995,1,0.0002891454657053547,'\"mean\"'
+0,4,10,0.848754448399,false,406.0341148792142,-0.6534548245811949,5.672259432335176,0.0004657934317964557,0,2.73300350455542e-06,'\"most_frequent\"'
+0,4,11,0.873665480427,false,16.892045479519794,-0.9056188070740147,3.830919021298213,6.828016432811479e-05,0,0.030118224345374676,'\"mean\"'
+0,4,12,0.880782918149,false,0.19713171088727002,-0.6218219840758359,3.4974135627130285,3.477535552474296e-05,1,0.042440157025532096,'\"mean\"'
+0,4,13,0.998220640569,false,187.08529497904075,0.29052456813479255,3.070340745880317,0.03535149141722234,1,0.01647771989027617,'\"mean\"'
+0,4,14,1.0,true,1761.825586779441,0.3840769602301012,2.0528012817519756,0.08653953661009232,1,0.048859523262837705,'\"median\"'
+0,4,15,0.0462633451957,false,3830.7578992010917,-0.9588540000364719,2.0009223703543153,0.0007269814225722508,0,0.015371013054371725,'\"mean\"'
+0,4,16,0.980427046263,false,1021.1838805674183,0.7430995130501808,6.411792227410006,0.011524574900837185,1,0.001001781014588982,'\"median\"'
+0,4,17,0.880782918149,false,0.4295975930770977,-0.9728377550435021,1.9629717873696344,0.00032207121804298845,0,0.0045776757083245195,'\"mean\"'
+0,4,18,0.989323843416,false,51011.41962153441,0.30142121985463033,3.5830078797109164,0.23330736068858993,1,3.356018999320689e-05,'\"most_frequent\"'
+0,4,19,0.880782918149,false,0.8453492495627098,0.6121860355831266,1.0988506760142978,0.00019663632791304796,1,0.0005897712003258938,'\"most_frequent\"'
+0,4,20,0.918149466192,false,40740.645833545415,0.03612792637749529,1.4318665614697053,1.3466031374066887,1,2.9156810513931278e-06,'\"most_frequent\"'
+0,4,21,0.460854092527,false,2.245672035111122,0.2593364134325874,0.9809364698145122,0.0018957078649393282,1,0.0016427826470802758,'\"mean\"'
+0,4,22,0.918149466192,false,11.213118150800076,1.0221944255897955,1.407683723169979,0.061635908090009905,1,4.325829819856612e-05,'\"mean\"'
+0,4,23,0.918149466192,false,0.8080181479505986,0.828899710785942,1.3179674007041164,3.99690340674426,1,0.0034424244337654107,'\"most_frequent\"'
+0,4,24,0.955516014235,false,0.272057409265882,0.48828616548006615,5.798133039024823,0.8057639044977581,1,0.02890927133738997,'\"most_frequent\"'
+0,4,25,0.884341637011,false,2.800721963743426,0.8697952512515913,5.052015683215163,0.0014184094492681817,1,0.004135037645445954,'\"median\"'
+0,4,26,0.0462633451957,false,14780.523091072057,-1.0542843719185573,2.8089814587046793,0.0007079469646783118,1,0.058883028486775485,'\"mean\"'
+0,4,27,0.91103202847,false,320.15814298991955,0.28013764752088033,1.2537107076791927,0.0016154264556285931,0,0.0012063007316745124,'\"mean\"'
+0,4,28,0.877224199288,false,131.80521867707287,0.6353242845136978,1.773353589333825,0.0001874231166162571,1,0.01948393774715573,'\"most_frequent\"'
+0,4,29,0.877224199288,false,1112.6257599991325,0.22511041419496775,1.5084066545637382,9.422382805089521e-05,0,0.0160614708452507,'\"median\"'
+0,4,30,0.976868327402,false,817.5892720456682,0.49309732350278646,4.971385937716054,0.13910881986903145,0,0.004918862559043423,'\"mean\"'
+0,4,31,0.460854092527,false,3111.256098606256,0.25254645115490154,0.49033232356054535,0.006053682541066926,1,6.379880397249585e-05,'\"mean\"'
+0,4,32,0.866548042705,false,4.499992110700251,-0.5899191211193116,3.64035003668287,0.06335790229409673,1,0.008793388594618473,'\"mean\"'
+0,4,33,0.932384341637,false,0.8232485022133634,-0.9473908014938456,2.0580544085493013,0.1770253439684234,1,3.672907441960708e-05,'\"median\"'
+0,4,34,0.998220640569,false,11053.619826841044,0.5764750387300459,3.908767596682923,0.03803309265730913,0,2.2249597699807e-05,'\"median\"'
+0,4,35,1.0,false,3.602670396555669,0.9870383278907675,2.112693828838392,0.32029217070280164,1,0.00012287444299266718,'\"most_frequent\"'
+0,4,36,0.460854092527,false,170.52933622381997,0.6723035805112072,0.3999112012239361,0.002782607426315867,0,5.9816918551503026e-05,'\"median\"'
+0,4,37,0.0462633451957,false,7619.03748999709,-1.4134076476031228,2.571059998983256,0.0030690671597159427,0,0.006946322931667636,'\"median\"'
+0,4,38,0.919928825623,false,0.030833551640403935,0.3456785300175159,3.536275654423502,0.11123724634268572,1,0.0007846939966635077,'\"median\"'
+0,4,39,0.0462633451957,false,17447.769617624035,-1.2746138383355632,2.3182005546081115,0.0020975064018168587,0,0.0007100635874142511,'\"mean\"'
+0,4,40,0.903914590747,false,16.69221968634561,0.301116385269775,5.580359591280189,0.005742149654821648,1,0.05551444466733553,'\"mean\"'
+0,4,41,0.918149466192,false,17.937850045757497,0.971663647783632,1.0091269216956527,5.051325122168923,0,1.7629136399721924e-05,'\"mean\"'
+0,4,42,0.967971530249,false,29.815669518505995,-0.06081246110870525,5.426640474166943,6.146281304890439,1,0.28410360546028857,'\"mean\"'
+0,4,43,0.460854092527,false,17.33745675486572,-0.7835826117066375,0.11629284556667963,0.00015847188602252993,0,4.34071323635258e-05,'\"mean\"'
+0,4,44,0.957295373665,false,17819.622563923942,-0.07916116199805788,5.283894325351497,0.713359171218223,1,0.0004211889630520002,'\"mean\"'
+0,4,45,0.460854092527,false,0.04176411372142008,0.6008603019285157,0.9010104000072614,0.0008644628275267583,0,1.5389826786461266e-05,'\"median\"'
+0,4,46,1.0,false,1269.2488579551925,0.7406913457080244,2.8995674586503974,0.02606720342407777,0,0.001712176436446115,'\"mean\"'
+0,4,47,0.918149466192,false,569.8731107206102,-0.05919606666374552,1.2180763969663695,0.01679392125801577,1,2.1429877680600846e-05,'\"median\"'
+0,4,48,0.918149466192,false,1058.9526774367198,0.7817120838437255,1.2641135140904072,0.005970030095003792,1,2.198769336587015e-05,'\"most_frequent\"'
+0,4,49,0.460854092527,false,2554.640832082914,0.25890364447202474,0.6420412648053168,0.0001913548010604811,1,0.0004904124859507641,'\"median\"'
+0,5,0,0.866785079929,false,13.66849615960516,0.06111742963068775,1.421949543436971,0.0011200295001338998,1,5.019929154351597e-05,'\"median\"'
+0,5,1,0.880994671403,false,21.069663111126196,1.0152637443063868,1.8368823015361335,0.0005436135346066656,1,0.0037355129011719464,'\"median\"'
+0,5,2,0.880994671403,false,111.41265444987043,0.06850844457538202,4.681294242680493,0.00329861223148026,0,0.005224645104149872,'\"most_frequent\"'
+0,5,3,0.914742451155,false,31114.500438695755,0.7823298794365788,1.8962154746417685,0.11705214344205381,1,0.0007678708864512136,'\"most_frequent\"'
+0,5,4,0.989342806394,false,2.1129127566087686,0.6427662936096907,3.6584757085101183,0.8725007962099892,0,0.0021440512597684434,'\"mean\"'
+0,5,5,0.8756660746,false,135.26359169148245,-0.2280225175971313,1.8424819205697291,0.0004426436613642648,1,0.0029216184330192615,'\"most_frequent\"'
+0,5,6,0.994671403197,false,1063.1354636761278,0.2707750446232701,3.1674744170809364,0.013098678433315096,0,0.00017775272526177706,'\"median\"'
+0,5,7,0.8756660746,false,1.0061111614431413,0.04398860015756936,1.679939705237607,0.2640113238148257,1,0.02194963106393073,'\"most_frequent\"'
+0,5,8,0.873889875666,false,14.282491153698386,0.6790917320549662,2.0001989838427567,0.00019518522123886564,0,0.04106884262317708,'\"most_frequent\"'
+0,5,9,0.460035523979,false,34276.39881215914,0.2737614135137135,0.7949344235513522,0.002636703332655627,0,0.001667313862956003,'\"median\"'
+0,5,10,0.989342806394,false,58.58725655888699,0.6614973468769791,3.6715020363402777,26.213024406896192,0,0.0013305177902142805,'\"mean\"'
+0,5,11,0.968028419183,false,532.1700439591681,0.644251559962876,4.232937596221234,0.7261151419555705,0,2.302729324144655e-05,'\"most_frequent\"'
+0,5,12,0.914742451155,false,14.997201998795203,-0.03148754091223698,1.237287158898332,0.039766215376899595,0,0.02023137398373552,'\"mean\"'
+0,5,13,0.959147424512,false,0.002448920410697238,0.7718260519747073,2.8759934383928973,3.3200895609150596,1,8.817885596348328e-06,'\"mean\"'
+0,5,14,0.044404973357,false,658.753589080941,-1.0495764005693433,2.722516440747492,0.00354549508724058,1,6.937440602215102e-06,'\"median\"'
+0,5,15,0.460035523979,false,159.26013587478658,0.19589043437799522,0.9194013108416599,0.0019677432412534158,1,0.000157287607312596,'\"most_frequent\"'
+0,5,16,0.877442273535,false,0.22581223451100604,0.4350947909848449,4.591651003158081,0.00010779452594222963,0,0.0008211341662083198,'\"median\"'
+0,5,17,0.98756660746,false,0.18557349411337457,-0.4483705433985157,3.0722335288389004,3.0816914711856898,0,0.02485120110475323,'\"median\"'
+0,5,18,1.0,true,3.598074077026892,0.6955198381641161,2.1474478308520553,3.1330990114869164,1,0.020940711790782378,'\"median\"'
+0,5,19,1.0,false,111.40264058597552,0.401901728464517,2.323681391501527,0.3638938626897485,0,2.8632436323828772e-05,'\"mean\"'
+0,5,20,0.902309058615,false,125.81995308975088,1.1950264854424146,2.0700124158927378,0.0006883420565716762,1,3.23220875520536e-05,'\"most_frequent\"'
+0,5,21,0.97513321492,false,16.08520318462572,-0.08004911970568335,4.269860972024203,10.885714443151436,0,0.0013740114094782632,'\"most_frequent\"'
+0,5,22,0.0479573712256,false,195.7360700655636,-0.9837277135633524,4.272700924690317,0.007862388185940988,0,0.0024950489644509815,'\"most_frequent\"'
+0,5,23,0.946714031972,false,2.8706662673393315,1.1063209411157655,4.337200350216567,0.01971651866432805,0,0.0030328497873715766,'\"mean\"'
+0,5,24,0.989342806394,false,50944.80190124149,0.6703945455704401,3.551427883244328,5.885642772894052,1,0.0020626235239115625,'\"most_frequent\"'
+0,5,25,0.845470692718,false,137937.0360627125,-0.36573945209382036,5.328603566197303,2.326449750894875e-05,1,3.146120437140172e-05,'\"median\"'
+0,5,26,0.877442273535,false,0.10977091475480114,0.031878441402818425,1.0539147796533883,0.0017584568644261368,1,8.782994042320584e-05,'\"median\"'
+0,5,27,0.895204262877,false,7.7852729604737245,0.9601059174341477,3.0246852888540716,0.003101808904844795,0,0.0022842339261458966,'\"most_frequent\"'
+0,5,28,0.877442273535,false,0.39917963695997094,0.639817101463683,1.4660998948256956,8.039013805748474e-05,0,0.001900939939936428,'\"most_frequent\"'
+0,5,29,0.914742451155,false,1378.1527416353017,0.9059596024736359,1.0348032184248936,0.0013031460322444604,0,5.31700986752432e-05,'\"most_frequent\"'
+0,5,30,0.920071047957,false,69.45747355460958,1.0201998173267939,3.529964059208952,0.003570725330982903,0,0.004329021912296458,'\"mean\"'
+0,5,31,0.985790408526,false,0.3800036958066856,-0.6716315550707297,2.58823607479567,0.7167225547040784,1,0.00010281539446740432,'\"mean\"'
+0,5,32,0.877442273535,false,0.324179573920309,0.9812976149126935,2.370813872427128,1.687001953898628e-05,1,2.029807807283747e-05,'\"most_frequent\"'
+0,5,33,0.460035523979,false,407878.7423904915,-0.0731708935072449,0.6349693650403847,1.03905115774658,0,3.488590678967584e-05,'\"mean\"'
+0,5,34,0.97513321492,false,74509.25824943294,0.23620122068313643,4.282250662670221,2.9480530293358687,1,0.00011085647366177926,'\"most_frequent\"'
+0,5,35,0.460035523979,false,0.08217794175214574,0.6012801497255864,0.8671233157214715,6.685532170316927e-06,0,0.006298250116667996,'\"mean\"'
+0,5,36,0.877442273535,false,0.40309466369109237,0.8807393383088514,5.934547301222656,2.999880775751355e-05,0,0.010940493861795108,'\"median\"'
+0,5,37,0.460035523979,false,354.98957832351874,0.48333634456985836,0.02644401872602231,3.893560224536179,0,0.05782080831901848,'\"median\"'
+0,5,38,0.0408525754885,false,3.0541665268300817,-0.31393673375110764,2.1972931373155573,2.821632631473712e-05,0,0.007522812759263012,'\"median\"'
+0,5,39,0.904085257549,false,2.70157335732934,1.0353613488230387,3.7148300644679093,0.009448251064378266,0,0.0053879699434309924,'\"most_frequent\"'
+0,5,40,0.904085257549,false,2.3141329228006366,0.31228119220585193,4.380970094050709,0.01593501705880869,1,0.003537801016427191,'\"median\"'
+0,5,41,0.914742451155,false,378.69943584733346,0.9281871383012354,1.7437244993938312,0.0043212757576537066,1,0.023537725222243466,'\"median\"'
+0,5,42,0.941385435169,false,1.4658427444821616,-0.978633773128549,3.9345912338670854,0.7991337306478419,1,0.0005785491536810366,'\"most_frequent\"'
+0,5,43,0.91296625222,false,2757.0615968072843,0.6711290189679047,2.786642853452718,0.0008717771384996698,0,0.00020180445589859793,'\"median\"'
+0,5,44,0.914742451155,false,88917.61956407459,0.9489927792050445,1.5368924048487398,0.9774702500108714,0,0.008372200690435181,'\"mean\"'
+0,5,45,0.460035523979,false,147.53342755286505,0.43238865424255185,0.6952055694829828,4.434733921447675,0,0.004773900409187143,'\"median\"'
+0,5,46,0.868561278863,false,122.86980844723632,0.3530865327356175,3.8772627644977717,3.0987318050833974e-05,1,5.304370194032722e-05,'\"most_frequent\"'
+0,5,47,0.460035523979,false,8.920191982679002,-0.18940273440221733,0.9605405307534115,0.005481627932243903,1,0.02158742147016021,'\"most_frequent\"'
+0,5,48,0.879218472469,false,0.4336218101433468,0.9716968036721519,1.1026233778305985,0.002695831567122861,1,0.0022978315645728544,'\"mean\"'
+0,5,49,0.877442273535,false,11.563614490869968,0.19474417521312457,4.086445457934597,3.8430133578188776e-05,1,8.094308835431811e-05,'\"mean\"'
+0,6,0,0.92539964476,false,43.85567794109849,0.7107635473419465,1.5149111564445426,0.3235599970561098,0,0.001024962739089953,'\"most_frequent\"'
+0,6,1,0.602131438721,false,1.3731603832653714,0.3251952798814186,1.421923188554574,0.0001701290304381303,1,6.442960632643003e-05,'\"most_frequent\"'
+0,6,2,1.0,true,1036.512837396929,0.41667739855572333,2.160046575482477,0.09828905584352092,0,0.015247429980626564,'\"most_frequent\"'
+0,6,3,0.602131438721,false,0.02210180536204348,-0.2846387502002478,3.0316008051957857,0.0002467074472683427,1,0.0027385711518895686,'\"median\"'
+0,6,4,1.0,false,104.54686627331418,0.8133327392571894,2.617463580793763,4.966087034282929,0,4.745494823237471e-05,'\"most_frequent\"'
+0,6,5,0.98756660746,false,222.30153555672115,-0.13016108232628948,3.8373514257227432,3.433073241944194,1,0.05282261186707727,'\"most_frequent\"'
+0,6,6,0.907637655417,false,20.389622538962893,0.42564310195058797,3.880353917591258,0.00608826713526226,0,4.0752442397506146e-05,'\"most_frequent\"'
+0,6,7,0.92539964476,false,175.34416394637321,0.1150220579498149,1.9842653274976272,0.6416260082418818,1,0.0003910233137893929,'\"median\"'
+0,6,8,0.923623445826,false,33.51051765590476,1.0121154762957114,2.1759218162390086,0.009808941728720455,1,5.56037576271293e-05,'\"median\"'
+0,6,9,0.460035523979,false,7.191663928062597,1.1542330069085514,0.6148859543988107,0.006133733820709831,1,7.63204349855321e-05,'\"most_frequent\"'
+0,6,10,0.044404973357,false,2724.036776423388,-0.8489784447014133,4.513251535217724,6.834271826600065e-05,1,3.0513658020842358e-05,'\"most_frequent\"'
+0,6,11,0.992895204263,false,283.12870798758297,0.7157084928001813,4.755535049496067,0.01987857833172399,0,1.077579081490017e-05,'\"mean\"'
+0,6,12,0.991119005329,false,23.830265433388174,0.34628056081014935,3.1976422154023934,4.221431614160195,0,1.8938439055394547e-05,'\"most_frequent\"'
+0,6,13,0.602131438721,false,0.6965621755785614,0.9573289738857684,2.6117058942415565,6.787569953708314e-05,1,0.00043385586588591996,'\"mean\"'
+0,6,14,0.460035523979,false,10567.25037719441,0.01828300865137339,0.9700523858343031,0.008346612165847868,1,0.02675935878373317,'\"mean\"'
+0,6,15,0.602131438721,false,32.29584798067366,1.0625919986442929,1.5822472511982613,7.968355993363271e-06,1,0.0011691575440731636,'\"most_frequent\"'
+0,6,16,0.600355239787,false,1.0467265410957336,-0.10646084666718847,3.122833388017648,0.0016587063899450587,1,0.003453503193001008,'\"mean\"'
+0,6,17,0.98756660746,false,0.2716299906438806,0.6790617875665578,3.718906025534923,1.5698942664344226,1,0.01617205895825715,'\"median\"'
+0,6,18,0.880994671403,false,33.081751275748,0.8703332500708476,1.7806525059417668,0.007785699519180217,1,0.005340745735737514,'\"mean\"'
+0,6,19,1.0,false,3.8089443699694367,0.40865035757430845,2.8915622932522966,3.6809151786329943,1,0.0004394865674954829,'\"most_frequent\"'
+0,6,20,0.460035523979,false,9644.221302107931,-0.46050041429107796,-0.05285627615480082,0.007385380895256728,1,0.04247791747885483,'\"mean\"'
+0,6,21,0.460035523979,false,40.92556147447078,0.9731512455754188,-0.1451851441210701,0.3487809730554698,0,0.034332355404991924,'\"mean\"'
+0,6,22,0.877442273535,false,0.43131511774698156,0.7195088939937216,2.9174551522260055,0.002360466046354005,1,6.272161024364554e-05,'\"most_frequent\"'
+0,6,23,0.602131438721,false,0.019934693812166147,-0.5119591645154185,5.259907722121105,0.0006440650044857645,0,9.4741968257937e-05,'\"median\"'
+0,6,24,0.889875666075,false,114673.6382719644,-0.49997729535675806,3.055710987475984,0.14236205042115072,0,0.0002813001118303393,'\"most_frequent\"'
+0,6,25,0.8756660746,false,61.70480492630334,0.4801355296342016,1.3286156671084677,0.0020169731338711126,1,0.06254518619334083,'\"most_frequent\"'
+0,6,26,0.92539964476,false,32.920479419186144,0.10580761079748102,1.9280025254201212,0.06765685585183619,0,1.1670950783784311e-05,'\"mean\"'
+0,6,27,0.511545293073,false,1286.8226792800226,-0.7762107314854696,4.4173466374086265,0.028003895596875907,0,0.012386244265932005,'\"mean\"'
+0,6,28,0.898756660746,false,89538.1356465184,0.7414899763013247,1.2560530909406957,8.06483125018966,1,0.018950754944369173,'\"mean\"'
+0,6,29,0.92539964476,false,4648.711753338892,0.551146742260735,1.8181810791929442,0.29055320531306333,1,0.0017361155799605211,'\"median\"'
+0,6,30,0.969804618117,false,12410.183751621695,0.1408739068130513,3.103357132340741,0.0029537402904998705,0,0.0032130038668258124,'\"median\"'
+0,6,31,0.92539964476,false,486.7494925978975,-0.20309495303712977,1.4904239439262505,0.10789010621634477,1,0.00639777813685907,'\"most_frequent\"'
+0,6,32,0.460035523979,false,10704.581819668994,-0.017054151010762575,0.8877483176535349,0.033341932083470804,0,0.14406913507999553,'\"mean\"'
+0,6,33,0.98756660746,false,53.990793699170794,0.441877583641834,2.920397935554504,0.04562245745088968,1,0.0008238642333599837,'\"most_frequent\"'
+0,6,34,0.886323268206,false,37.36404576585719,-0.5990857319137267,3.4873596252543306,1.641864633145686e-05,0,0.012168069036843563,'\"median\"'
+0,6,35,1.0,false,3543.7105941235995,0.13098756245748966,2.4842076737785024,0.08050601754895323,1,0.040942295337957166,'\"most_frequent\"'
+0,6,36,0.90053285968,false,1268.5395569011002,-0.19699587754024256,2.43013559667719,0.033363298390945074,1,0.008047597672240989,'\"median\"'
+0,6,37,0.92539964476,false,3.9733904572656793,-0.07332919122299769,1.3002169473616352,0.2673173226943961,1,0.003666109173462852,'\"median\"'
+0,6,38,0.460035523979,false,12.696687979869845,1.0624640694962295,-0.41330443811042183,1.4131107619011045,0,2.279903260022493e-05,'\"most_frequent\"'
+0,6,39,0.92539964476,false,1945.0647031735398,-0.1985051631448117,1.9168769320816448,0.0011319588944983653,1,3.034285937179739e-05,'\"most_frequent\"'
+0,6,40,1.0,false,3027.1865889165956,0.5283367809307203,2.9447717998620466,0.6442892517628049,1,2.0754475515632365e-05,'\"median\"'
+0,6,41,0.880994671403,false,0.15529541976359704,0.7498730485346193,3.393173321068033,0.004421811780173782,0,0.00022552624747747347,'\"most_frequent\"'
+0,6,42,0.882770870337,false,10.328903245732102,1.1256569796476679,2.6218606801313165,0.0015567651077213463,0,2.5656380505247336e-05,'\"most_frequent\"'
+0,6,43,0.92539964476,false,1552.1744921359677,0.08326268193489533,1.9635185973777203,0.004196100036571702,0,3.077291896830801e-05,'\"median\"'
+0,6,44,1.0,false,0.7207629852712976,0.1281855198789849,2.2962835201055505,2.2770152805079213,1,0.00020212223515053327,'\"most_frequent\"'
+0,6,45,0.932504440497,false,4152.087942053487,-0.8213548455045847,5.297310901792907,0.7800706483758937,0,0.0009218390113845069,'\"most_frequent\"'
+0,6,46,0.92539964476,false,487.4751312640391,0.9900975640219378,1.7504184926401776,0.37023670247063767,0,1.2365759832839483e-05,'\"most_frequent\"'
+0,6,47,0.92539964476,false,284.4732137306098,1.4137877766980242,1.0410268036018568,0.033821732137216565,0,0.006692724990727327,'\"most_frequent\"'
+0,6,48,0.461811722913,false,49919.276269990594,-0.9598081891956697,0.7579959281552323,0.007022546296279782,1,0.04304008803212698,'\"mean\"'
+0,6,49,0.460035523979,false,0.26321086475182887,-1.0133816191646141,0.7242401604234336,0.002051391174603537,1,0.004586630713926714,'\"most_frequent\"'
+0,7,0,1.0,true,24.973045269438934,0.912163370043651,2.6751120010727014,1.5108031422176578,1,0.0011403818735939244,'\"median\"'
+0,7,1,0.877442273535,false,148.99084108419916,0.4968095555538027,3.086353348276949,5.364225063119183e-05,1,0.0078012167978469375,'\"median\"'
+0,7,2,0.88809946714,false,63.268375670067684,0.26399255236114394,2.9071391969401326,0.001531616868869095,1,0.00013503106946083314,'\"most_frequent\"'
+0,7,3,0.0692717584369,false,69803.12637451757,-0.11844826387351082,2.5216912802530445,0.002105068814988689,0,0.010358045720600673,'\"most_frequent\"'
+0,7,4,0.97513321492,false,11.61410911404138,-0.1427884296410314,4.420760918975719,5.369899347015796,0,0.022983735268718115,'\"median\"'
+0,7,5,0.8756660746,false,7.2001550826296015,-0.4821699602589764,3.298001030741132,0.0008178928423940191,1,5.035115987902868e-05,'\"mean\"'
+0,7,6,0.872113676732,false,0.42430091817689564,-1.1374867190760811,1.80496432291102,0.002209316909564711,0,0.00015750127288847258,'\"most_frequent\"'
+0,7,7,0.596802841918,false,1.614764038561688,-0.043827056746579995,3.7933733407868857,2.949334578782188e-05,0,7.019074508594563e-06,'\"most_frequent\"'
+0,7,8,0.873889875666,false,23.99583561026496,-0.6697445208888476,1.352951361704945,0.0003640857492603726,1,0.03191281042356213,'\"most_frequent\"'
+0,7,9,0.8756660746,false,24.05872956569852,0.7187123181811206,2.0443957724129547,9.503020610325696e-05,1,0.0021163212092887886,'\"most_frequent\"'
+0,7,10,0.902309058615,false,242.82241946415894,-0.18104950077193963,2.1845507003775935,0.03129497463749704,0,0.0007220930390473377,'\"median\"'
+0,7,11,0.916518650089,false,4278.817431707071,0.3067946250632923,1.264684222486897,1.8532105859416965,1,0.00719566498857422,'\"median\"'
+0,7,12,1.0,false,11.358516854293207,0.8605649967952275,2.862476146058207,1.463504450675046,1,0.0004947109074603115,'\"median\"'
+0,7,13,0.461811722913,false,1120.218127969622,-0.19801271316832111,0.9852176399663557,7.833483892211213e-05,1,0.07281874043562094,'\"most_frequent\"'
+0,7,14,0.91296625222,false,3.379009970123852,0.2133086361045288,3.0783001866644573,0.02004475850647182,1,0.04338810975639759,'\"most_frequent\"'
+0,7,15,0.932504440497,false,0.16949008822013795,0.5607056833597837,3.7975191485455335,0.09218578517142338,1,0.015922932701577446,'\"mean\"'
+0,7,16,0.461811722913,false,1330.7254606852598,0.4695258993480246,0.9762855594076818,7.171637751942036e-05,0,2.0532071047335406e-05,'\"mean\"'
+0,7,17,1.0,false,0.8265647426340748,0.49925250924896547,2.517323558736013,1.8853051029103467,1,0.007855913784762721,'\"mean\"'
+0,7,18,0.976909413854,false,6207.988291011863,0.03045069539420575,4.436061950958776,0.1466698270590404,0,0.03777475917699017,'\"mean\"'
+0,7,19,0.873889875666,false,73.72618368139563,0.5953806570240546,1.7908275680268269,3.778388925235211e-05,0,3.344042945825218e-05,'\"median\"'
+0,7,20,0.971580817052,false,1714.239898252368,1.3787778841778335,5.090093708564485,0.18483674048854776,0,7.394180238402143e-06,'\"most_frequent\"'
+0,7,21,0.898756660746,false,20179.15958436573,0.4881690660511968,1.5568876146879869,9.133030117337281,1,0.0063591753065196065,'\"most_frequent\"'
+0,7,22,0.880994671403,false,83.13940489009681,-0.37362953934686594,1.7792945188241596,0.0008707527522933268,1,0.0107660817128699,'\"mean\"'
+0,7,23,0.992895204263,false,10346.58313314762,-0.03353463710979207,2.4085849350092867,0.09719085818060898,1,0.0018572321107959658,'\"most_frequent\"'
+0,7,24,0.8756660746,false,178.51830520844962,0.487701615017296,5.345775215855289,0.0004720774594113673,1,0.0016471228082465986,'\"most_frequent\"'
+0,7,25,0.596802841918,false,0.13268762889744481,1.0445206386855332,2.7399535036543545,6.635553302300538e-05,1,0.0012769676918945286,'\"most_frequent\"'
+0,7,26,0.461811722913,false,282108.294636431,0.6964671533564476,0.861365383647728,0.007607810548667796,1,4.715900952305604e-06,'\"most_frequent\"'
+0,7,27,0.905861456483,false,6.029723634638818,1.007710458905326,5.329200469931856,0.0037503410116436625,1,7.507402768320972e-05,'\"mean\"'
+0,7,28,0.902309058615,false,16.0035367100643,0.9857721162072361,4.130609914271185,0.0012643003601884241,0,0.0027927701612839354,'\"mean\"'
+0,7,29,0.8756660746,false,0.24116432357909678,0.8993077392289124,5.136736755578084,0.0008313548965856356,1,0.005873373781181742,'\"median\"'
+0,7,30,0.96269982238,false,2.655657608741368,-0.23411456896860425,3.3685762871219094,0.3967176951150274,0,0.06570789844147318,'\"median\"'
+0,7,31,0.8756660746,false,21.02315205584265,0.8673026659820452,1.3894274504879052,0.00197251464488766,1,0.005457528464100171,'\"most_frequent\"'
+0,7,32,0.923623445826,false,0.23812901896888694,0.7295601062673341,2.7376582262619853,0.17614934394668202,1,0.00029406646814082504,'\"median\"'
+0,7,33,0.461811722913,false,26.11291290332684,0.6374550911888465,0.5309890166936436,1.3566982206830016,0,0.005059305024294842,'\"most_frequent\"'
+0,7,34,0.461811722913,false,91.03026022001895,-0.18445920005870736,0.9826660160754138,0.0005056226808335933,0,0.0020289062651505595,'\"mean\"'
+0,7,35,0.461811722913,false,44.514088971838135,0.12168049941611345,0.49476891703488746,1.1085900836469442,0,0.04331905113121335,'\"most_frequent\"'
+0,7,36,0.916518650089,false,19668.548801588015,-0.27045758978628676,1.2906312250959464,0.21297140404886666,0,0.015019597680884808,'\"most_frequent\"'
+0,7,37,0.916518650089,false,53428.30646920361,0.7143858838906282,1.7251671622098748,0.0001348134010333923,1,0.0008081356584734022,'\"median\"'
+0,7,38,0.856127886323,false,925.7898789127964,-0.34478005043131343,3.131567095441692,0.001210041262019114,0,0.001083794776966413,'\"mean\"'
+0,7,39,0.460035523979,false,4623.4259002468525,0.07496826320048075,0.7232610657792478,0.8373964597405706,1,0.003776787736950945,'\"most_frequent\"'
+0,7,40,1.0,false,70.76359386902459,0.5929655711579311,2.18533544030584,0.3596793842242121,1,0.0011396982107894794,'\"mean\"'
+0,7,41,0.8756660746,false,0.160663162970681,-0.9484483651550402,1.1500803195713667,0.12159559182349321,0,0.0022157038199694763,'\"most_frequent\"'
+0,7,42,1.0,false,6977.617664283453,0.20184033202875357,2.0725641806570962,1.6903445044699081,1,0.05283493003367689,'\"mean\"'
+0,7,43,0.492007104796,false,1.4595486812737906,-0.24933170516406483,5.366432722165617,0.009362949827204946,1,0.012893440989576286,'\"median\"'
+0,7,44,0.998223801066,false,17.247347242039808,0.43082135230943425,3.0912832265793417,0.07274834325885306,1,2.3947003861865868e-05,'\"median\"'
+0,7,45,0.920071047957,false,284.6761790459249,0.31204401729638465,3.2999054358495883,0.0035182797117977696,1,0.08203301320897931,'\"most_frequent\"'
+0,7,46,0.916518650089,false,152.4376263364514,-1.3438468335680063,1.7802636722340386,0.5257939773862178,0,0.00013072880933413217,'\"mean\"'
+0,7,47,0.460035523979,false,0.009220405817822354,0.10465349430854087,0.46938246256906724,1.2234413911125323e-05,1,0.00032593817955360246,'\"most_frequent\"'
+0,7,48,0.461811722913,false,0.6986946895860315,-0.6001986156719983,0.3320702023291924,0.0008750381772928011,0,0.00989524200497762,'\"most_frequent\"'
+0,7,49,0.884547069272,false,156.10811895879047,0.9671493622946175,3.945590981509848,2.6688199562158217e-05,0,0.0009568257354625336,'\"mean\"'
+0,8,0,0.973357015986,false,4260.107398407962,0.669823290772445,4.731543070769061,2.082661920777334,1,0.0038742775172646597,'\"median\"'
+0,8,1,0.91296625222,false,2130.6534487948556,0.1002873554522179,1.3234154807195322,0.5341820038990216,1,0.00017768390556724683,'\"most_frequent\"'
+0,8,2,0.88809946714,false,0.4701161105281786,0.3666029898502039,3.604130210381162,0.012541376676521516,0,0.0006582931941079207,'\"median\"'
+0,8,3,0.991119005329,false,513.0357447356874,0.9431778580920311,4.416050445770097,0.011939064829407778,0,0.005331899094364483,'\"mean\"'
+0,8,4,0.872113676732,false,61.20701871773469,0.30008490836819823,1.335482930636752,5.78256882108219e-05,1,0.06069367870765139,'\"mean\"'
+0,8,5,0.602131438721,false,331.93872961430594,-0.27100193699343145,3.4122823275945766,4.565028471962421e-06,0,0.00022765140126365776,'\"mean\"'
+0,8,6,0.964476021314,false,2.878616308893704,0.2647424044300928,5.180794938182463,0.3169082975840112,1,0.0035130379073880016,'\"median\"'
+0,8,7,0.91296625222,false,209380.7301624571,0.8759402183738272,1.8228258979988685,0.00730163569136079,0,0.0001114322569384728,'\"median\"'
+0,8,8,0.598579040853,false,1.847936535476153,0.16013917571401173,2.3011986919825533,1.5568969120349444e-05,1,1.4455212064389794e-05,'\"most_frequent\"'
+0,8,9,0.600355239787,false,0.055499221295742265,-0.0008685197960959234,1.4431766905441723,4.7661808666837056e-05,1,0.011783744210735345,'\"mean\"'
+0,8,10,0.86323268206,false,1.5576926440636776,0.4964741612794113,1.5381669873149688,0.064244518092071,0,0.000286740888424632,'\"most_frequent\"'
+0,8,11,0.91296625222,false,2163.643980702201,-0.7882540267024858,1.157926180175481,29.08931855525445,1,2.8738379601009234e-05,'\"most_frequent\"'
+0,8,12,0.91296625222,false,18.570728287963195,1.0581144128828233,1.607359339496465,4.616948428315759,1,0.0015480085192343415,'\"mean\"'
+0,8,13,0.600355239787,false,3.542421657593335,-0.26312344315078295,3.542906495028303,0.00040408228067056264,1,3.559676814257828e-05,'\"mean\"'
+0,8,14,0.598579040853,false,0.039641876329170864,1.1499424386715034,2.606439342180959,0.0009375416486038883,1,0.07159409710907894,'\"median\"'
+0,8,15,0.460035523979,false,112.97828960470451,-0.8074341845569942,0.6054221665406904,0.02682603769902492,1,0.03438789710414498,'\"most_frequent\"'
+0,8,16,0.905861456483,false,1443.6798880029055,0.8432812646365335,5.7850746794672245,5.4300250850508854e-05,0,0.00043936441196731424,'\"most_frequent\"'
+0,8,17,0.600355239787,false,6.331334756657198,-0.5520270015939105,5.294228500764129,1.8895961847563268e-05,1,0.0009239023438210661,'\"mean\"'
+0,8,18,0.873889875666,false,1.6798732228353654,0.4166131686981018,2.9444848667412544,0.003098615214694962,1,9.454763424911034e-06,'\"median\"'
+0,8,19,0.461811722913,false,0.2574668146670974,0.5857001261919794,0.783228364414757,0.0008494182136627535,0,8.846118210623277e-05,'\"mean\"'
+0,8,20,0.872113676732,false,0.024436175212908147,0.1874735497135061,2.551359280471117,0.03143721609713994,0,0.09884612449846517,'\"median\"'
+0,8,21,0.902309058615,false,14.84137459798818,0.35389841970538227,6.032712835845537,0.00572290846677885,0,0.001040428540072819,'\"most_frequent\"'
+0,8,22,0.98756660746,false,5443.143130295858,0.8787044044171456,2.281986065577451,0.004869607419554642,0,0.0005598196741373084,'\"most_frequent\"'
+0,8,23,0.0550621669627,false,102745.04076415337,-0.3971020386649002,2.720031634188489,0.001961123181780432,0,6.68722333807081e-05,'\"mean\"'
+0,8,24,0.91296625222,false,137.36597924297578,0.050181774593935934,1.5513616901639868,6.163250911451913,1,0.019166365891514697,'\"mean\"'
+0,8,25,0.460035523979,false,14.785753679343271,0.49423351918990277,0.8379245150087985,0.000795797826329554,0,6.67007100714746e-05,'\"most_frequent\"'
+0,8,26,0.0852575488455,false,0.21381015527376937,-1.1456221222827403,4.400152937319537,0.017234893967226186,1,0.00030591981395197134,'\"mean\"'
+0,8,27,0.460035523979,false,2051.658393140636,0.13217128388691005,0.5889162451149552,0.00043876633229085086,0,0.00012403695550424024,'\"most_frequent\"'
+0,8,28,0.873889875666,false,122.56414154086626,0.15565029118365725,3.0125829669344895,0.000250345633046279,1,0.0002574911984834786,'\"mean\"'
+0,8,29,0.91296625222,false,488.8676399572419,0.6510867653230843,1.480674893288759,0.15175607792376722,0,0.00020668987903973458,'\"median\"'
+0,8,30,0.0515097690941,false,1151.9495081559419,-0.9779488325203672,2.9531068543581926,0.0026461774491897366,0,0.0012392618830047982,'\"most_frequent\"'
+0,8,31,0.968028419183,false,0.34561532760813496,0.32585807160254715,4.693671857725436,0.31270432918393726,0,0.008554515347791935,'\"most_frequent\"'
+0,8,32,0.889875666075,false,12.149766182307404,-0.27426367371669835,4.228344462753972,0.06872639658387246,1,0.009626125045543365,'\"most_frequent\"'
+0,8,33,0.91296625222,false,45.97453074700566,-1.1959885363711806,1.6391476152562734,0.11697040850815944,0,0.0047640101828458625,'\"mean\"'
+0,8,34,0.870337477798,false,1191.6842265984578,0.09090759105036829,2.1877650436485725,9.856987764588191e-05,0,0.014452603611268197,'\"mean\"'
+0,8,35,1.0,true,3.3254307702119044,0.8451059343728925,2.403811360052467,0.421084077241792,0,2.0460446055644092e-05,'\"mean\"'
+0,8,36,0.753108348135,false,0.020979410397417794,-0.169807497357769,5.32481375364438,0.02081608510531038,1,0.01019380458560152,'\"most_frequent\"'
+0,8,37,0.91296625222,false,6699.635649196931,0.6761214367949767,1.1312613521565096,0.3487670582386232,0,0.004933670324329108,'\"most_frequent\"'
+0,8,38,0.598579040853,false,0.3571723565010858,0.08179013179218061,3.7288477587689695,9.76582339047966e-06,1,0.0004244638962794993,'\"mean\"'
+0,8,39,0.598579040853,false,51.64675142550782,-0.033697707036654256,3.3011171597280575,0.00016206240733499178,1,1.8662531424413777e-05,'\"median\"'
+0,8,40,1.0,false,2.3623504789025738,-1.0240650754820018,2.4012780544342758,19.803363707993377,1,0.0011943576851378244,'\"mean\"'
+0,8,41,0.91296625222,false,1006.2280534505711,-1.0911144161110826,1.3545781048545236,0.04302739369346452,0,2.038146097504681e-05,'\"mean\"'
+0,8,42,0.873889875666,false,0.044390287360426946,0.12496716838666021,1.1792772723141747,0.020906475008859005,0,0.005074222636676571,'\"median\"'
+0,8,43,0.91296625222,false,885.7235324133286,0.8236138653678192,1.4244063092766241,0.09694632252231841,1,0.008164550865185798,'\"most_frequent\"'
+0,8,44,0.91296625222,false,22612.09653550347,0.13018733736399363,1.7539019738830948,0.0009993835038180723,1,0.2688677320259602,'\"median\"'
+0,8,45,0.460035523979,false,208.84128827931065,1.1679798024155197,0.9959312934313809,8.548963521936074e-06,0,0.007823174334825564,'\"most_frequent\"'
+0,8,46,0.460035523979,false,0.19654090216993195,-0.39522387873746245,-0.2646811988818156,0.6417406530064904,0,8.740928679645879e-05,'\"median\"'
+0,8,47,0.461811722913,false,0.17797136783804507,0.005957994405988043,0.7686887007080174,0.0068729556185472196,1,0.0018287269581913127,'\"median\"'
+0,8,48,0.461811722913,false,109.42343458727814,1.0389475595553872,0.6215637089533941,0.13579523549713166,1,4.5134655843008494e-05,'\"most_frequent\"'
+0,8,49,0.326820603908,false,0.17519603832101965,-0.8697881150601618,4.065580252565589,0.0006027598260945351,1,0.001870242173241928,'\"most_frequent\"'
+0,9,0,0.330373001776,false,0.7929551650751598,-0.11275756973062123,2.146872004842518,0.0001145542595710697,0,0.0004353819881257245,'\"most_frequent\"'
+0,9,1,0.600355239787,false,7.223229854781854,0.25241730937459333,5.184204551942713,4.364457775296339e-05,1,0.0013806702993796926,'\"most_frequent\"'
+0,9,2,0.8756660746,false,423.2939188035023,0.9426263960929339,1.053991648387858,0.00015543593237394275,0,2.237381861895125e-05,'\"median\"'
+0,9,3,0.916518650089,false,2417.839471019541,-0.3652175122150962,1.9394681154944138,0.2047231388651344,0,0.08048845919284181,'\"mean\"'
+0,9,4,0.460035523979,false,0.2354733939211322,0.08493662709290456,0.5908346578864514,0.024724594444342305,1,0.0010498214830268098,'\"median\"'
+0,9,5,0.600355239787,false,0.09865815477073735,0.9609844940727897,2.197017623244461,7.773166785090223e-06,0,0.03317831821046223,'\"mean\"'
+0,9,6,0.928952042629,false,6341.007450161471,0.06874042960012372,3.7699252085037855,0.0016322599768622088,0,0.009283042586807708,'\"mean\"'
+0,9,7,1.0,true,16.22015315625189,1.042594636085795,2.2934667262036017,1.1122221262787837,1,0.01978811656222838,'\"median\"'
+0,9,8,0.882770870337,false,519.9112924772703,-0.21901656234885897,5.549344497264323,0.00038659425761898537,1,0.0006928981352904642,'\"mean\"'
+0,9,9,0.460035523979,false,17.69745756328863,0.19778005187215716,0.44447134201958194,0.010850026185211161,0,0.02470421518670095,'\"mean\"'
+0,9,10,0.600355239787,false,0.796479784453037,0.529293318202733,1.5897197163971537,2.3424142728019482e-06,1,0.0009043373648399097,'\"mean\"'
+0,9,11,0.460035523979,false,75304.62381853443,1.0857606979860763,0.12281494845033136,0.03646726474010072,0,0.0034610067231405804,'\"most_frequent\"'
+0,9,12,0.460035523979,false,62.906949029696975,0.7075880305326933,0.7504210324151699,0.0015055077847438794,0,5.726670531136516e-05,'\"mean\"'
+0,9,13,0.966252220249,false,6.525132983062309,0.7585185440360596,2.7796470692489295,0.073379181421984,0,7.932851984297432e-06,'\"median\"'
+0,9,14,0.877442273535,false,23.57576014556137,0.8873721347776036,5.124468293261256,0.00031906366818353575,1,0.0638717689329287,'\"mean\"'
+0,9,15,0.991119005329,false,0.05814887752448838,0.9236770022551781,3.6972932713795137,0.9872034108375617,1,0.0012146450515890992,'\"most_frequent\"'
+0,9,16,0.971580817052,false,1.4473003834762712,-0.12106869392491076,4.32973456257078,0.4818194859994995,1,0.0006849626754324685,'\"most_frequent\"'
+0,9,17,0.461811722913,false,0.2917475000540697,1.0048383041318671,-0.36229000208945217,0.0013529543932839499,0,0.00035877018547138555,'\"most_frequent\"'
+0,9,18,0.879218472469,false,1.801356006644053,0.31135586877040056,2.019603350280192,0.0006758273800462388,0,0.0009704271192983297,'\"most_frequent\"'
+0,9,19,0.992895204263,false,116387.8823807923,1.1966525672985113,5.828680567604051,0.0006553379963211946,1,4.328726996658206e-06,'\"most_frequent\"'
+0,9,20,0.92539964476,false,0.15013626512081316,-0.7654517437447553,5.3863232537330745,0.7134805910974931,0,0.0024480231156916742,'\"most_frequent\"'
+0,9,21,0.460035523979,false,0.06034524605681806,0.03470233452367183,0.3446409871877787,2.0455043098526263e-05,1,3.510423077936586e-05,'\"median\"'
+0,9,22,0.460035523979,false,0.7111676468004967,0.8640367196178038,0.3988607356113181,0.0010244268364267954,0,0.0013239501882984204,'\"most_frequent\"'
+0,9,23,1.0,false,52557.73010955957,0.7503257336897403,2.586810524358058,0.010712837338858595,1,0.002385941937434256,'\"most_frequent\"'
+0,9,24,0.460035523979,false,2.6153752853444256,0.3966834036193233,0.5703822207372573,4.361726948399026e-05,0,0.00019771354929109354,'\"median\"'
+0,9,25,0.916518650089,false,1.0340696696187008,-0.5097867838581965,1.6792803184716243,1.4101235987865883,1,6.902784423873444e-05,'\"median\"'
+0,9,26,0.877442273535,false,574.4672924169071,0.6211471473889821,1.8230535766918925,4.485152306156482e-05,1,0.00018339005522939687,'\"mean\"'
+0,9,27,0.0603907637655,false,3599.8064929075153,-0.6588195246832768,2.4439891655572787,0.0009393931555570477,1,0.02771034517826329,'\"mean\"'
+0,9,28,0.600355239787,false,0.475165997551207,0.39265733064534103,3.4757802285940227,0.0001933045525063795,1,0.15736631656494707,'\"most_frequent\"'
+0,9,29,0.8756660746,false,0.028054691831741706,0.39762557986746416,1.5887827564773112,1.974381102794576,1,6.829809350280472e-05,'\"most_frequent\"'
+0,9,30,0.911190053286,false,70.16189200541936,0.48939548246169046,2.521849440642674,0.005114101511986077,0,0.006275245772073786,'\"median\"'
+0,9,31,0.918294849023,false,18421.46168473772,-0.8191090587864776,3.660678319894846,0.45229225404913387,1,0.0017752385755205608,'\"median\"'
+0,9,32,0.879218472469,false,3033.9415527715014,0.2694344479188932,3.5542928085873506,7.070160969706877e-06,0,0.0003575477792190339,'\"mean\"'
+0,9,33,0.916518650089,false,3306.0709332989018,0.6923846364387195,1.186243690973642,0.03398050522432396,1,0.01024395674328235,'\"most_frequent\"'
+0,9,34,0.461811722913,false,0.5110984472394127,0.09895238294772712,0.6700126787971288,0.3914744945127336,0,0.016546076599852348,'\"most_frequent\"'
+0,9,35,0.847246891652,false,5448.862769115369,-0.18181892648271808,3.7518633084655852,0.0004126338815608938,1,0.06551303167509931,'\"most_frequent\"'
+0,9,36,0.916518650089,false,66075.98088513625,0.5619000473587284,1.8252402209955376,0.06422997682272308,0,7.270597755943129e-05,'\"median\"'
+0,9,37,0.916518650089,false,42.4501712942481,0.7751585497971679,1.0132259554234135,0.35151140300039063,0,0.03478370507028009,'\"most_frequent\"'
+0,9,38,0.8756660746,false,20.3472407197862,0.030747600779033334,3.426650222696325,0.0017379854039870624,1,7.681453326685315e-05,'\"most_frequent\"'
+0,9,39,0.985790408526,false,28.794788930998667,0.9588679299839628,5.118939346125491,0.04034900784114201,1,0.0029104500684442878,'\"mean\"'
+0,9,40,1.0,false,2.2407811221967995,0.23299363832541356,2.7324260274129966,23.15184942886965,0,0.08679200501298358,'\"most_frequent\"'
+0,9,41,0.916518650089,false,1205.4454063209896,0.8128041905276748,1.0990621172431698,0.0006118422742162353,1,0.004336845053414217,'\"most_frequent\"'
+0,9,42,0.0603907637655,false,278160.5042540532,-0.8442661447679369,4.881992869192908,0.00024353627346173943,1,0.004346384658230803,'\"most_frequent\"'
+0,9,43,0.861456483126,false,2.7492595730345215,0.8418896404217042,5.403701541839213,9.91716711515321e-05,1,0.013535439173809378,'\"mean\"'
+0,9,44,0.461811722913,false,1609.7811477318858,0.2424215119638515,0.8426965868011322,8.534770903073012e-05,0,0.004021182277150715,'\"mean\"'
+0,9,45,0.460035523979,false,80.62197585091494,0.5656145310461822,0.9260967340424434,3.314721849681165e-05,0,0.1691249173983557,'\"mean\"'
+0,9,46,0.916518650089,false,11.551322429162767,-0.7991458983607153,1.1201851807440504,25.490915996067198,1,0.0009713114513813737,'\"median\"'
+0,9,47,1.0,false,52803.95318629395,0.48840311594449337,2.781026953358017,0.24387504713836486,0,0.0010179658001682542,'\"median\"'
+0,9,48,0.916518650089,false,4.212729480184038,-0.4237018370034965,1.3846048401020126,1.2806750694258338,1,0.0013379343290737073,'\"most_frequent\"'
+0,9,49,1.0,false,8255.254357596426,0.15512460844121778,2.632883048107603,9.685313873205853,1,9.29186571775498e-06,'\"median\"'
+%
+%
+%
\ No newline at end of file
diff --git a/tests/files/mock_responses/datasets/data_delete_has_tasks.xml b/tests/files/mock_responses/datasets/data_delete_has_tasks.xml
new file mode 100644
index 000000000..fc866047c
--- /dev/null
+++ b/tests/files/mock_responses/datasets/data_delete_has_tasks.xml
@@ -0,0 +1,4 @@
+
+ 354
+ Dataset is in use by other content. Can not be deleted
+
diff --git a/tests/files/mock_responses/datasets/data_delete_not_exist.xml b/tests/files/mock_responses/datasets/data_delete_not_exist.xml
new file mode 100644
index 000000000..b3b212fbe
--- /dev/null
+++ b/tests/files/mock_responses/datasets/data_delete_not_exist.xml
@@ -0,0 +1,4 @@
+
+ 352
+ Dataset does not exist
+
diff --git a/tests/files/mock_responses/datasets/data_delete_not_owned.xml b/tests/files/mock_responses/datasets/data_delete_not_owned.xml
new file mode 100644
index 000000000..7d412d48e
--- /dev/null
+++ b/tests/files/mock_responses/datasets/data_delete_not_owned.xml
@@ -0,0 +1,4 @@
+
+ 353
+ Dataset is not owned by you
+
\ No newline at end of file
diff --git a/tests/files/mock_responses/datasets/data_delete_successful.xml b/tests/files/mock_responses/datasets/data_delete_successful.xml
new file mode 100644
index 000000000..9df47c1a2
--- /dev/null
+++ b/tests/files/mock_responses/datasets/data_delete_successful.xml
@@ -0,0 +1,3 @@
+
+ 40000
+
diff --git a/tests/files/mock_responses/datasets/data_description_61.xml b/tests/files/mock_responses/datasets/data_description_61.xml
new file mode 100644
index 000000000..fc25e5861
--- /dev/null
+++ b/tests/files/mock_responses/datasets/data_description_61.xml
@@ -0,0 +1,30 @@
+
+ 61
+ iris
+ 1
+ **Author**: R.A. Fisher
+**Source**: [UCI](https://archive.ics.uci.edu/ml/datasets/Iris) - 1936 - Donated by Michael Marshall
+**Please cite**:
+
+**Iris Plants Database**
+This is perhaps the best known database to be found in the pattern recognition literature. Fisher's paper is a classic in the field and is referenced frequently to this day. (See Duda & Hart, for example.) The data set contains 3 classes of 50 instances each, where each class refers to a type of iris plant. One class is linearly separable from the other 2; the latter are NOT linearly separable from each other.
+
+Predicted attribute: class of iris plant.
+This is an exceedingly simple domain.
+
+### Attribute Information:
+ 1. sepal length in cm
+ 2. sepal width in cm
+ 3. petal length in cm
+ 4. petal width in cm
+ 5. class:
+ -- Iris Setosa
+ -- Iris Versicolour
+ -- Iris Virginica
+ 4
+ ARFF
+ R.A. Fisher 1936 2014-04-06T23:23:39
+ English Public https://api.openml.org/data/v1/download/61/iris.arff
+ https://data.openml.org/datasets/0000/0061/dataset_61.pq 61 class 1 https://archive.ics.uci.edu/ml/citation_policy.html Botany Ecology Kaggle Machine Learning study_1 study_25 study_4 study_41 study_50 study_52 study_7 study_86 study_88 study_89 uci public https://archive.ics.uci.edu/ml/datasets/Iris http://digital.library.adelaide.edu.au/dspace/handle/2440/15227 https://data.openml.org/datasets/0000/0061/dataset_61.pq active
+ 2020-11-20 19:02:18 ad484452702105cbf3d30f8deaba39a9
+
diff --git a/tests/files/mock_responses/flows/flow_delete_has_runs.xml b/tests/files/mock_responses/flows/flow_delete_has_runs.xml
new file mode 100644
index 000000000..5c8530e75
--- /dev/null
+++ b/tests/files/mock_responses/flows/flow_delete_has_runs.xml
@@ -0,0 +1,5 @@
+
+ 324
+ flow is in use by other content (runs). Can not be deleted
+ {10716, 10707} ()
+
diff --git a/tests/files/mock_responses/flows/flow_delete_is_subflow.xml b/tests/files/mock_responses/flows/flow_delete_is_subflow.xml
new file mode 100644
index 000000000..ddc314ae4
--- /dev/null
+++ b/tests/files/mock_responses/flows/flow_delete_is_subflow.xml
@@ -0,0 +1,5 @@
+
+ 328
+ flow is in use by other content (it is a subflow). Can not be deleted
+ {37661}
+
diff --git a/tests/files/mock_responses/flows/flow_delete_not_exist.xml b/tests/files/mock_responses/flows/flow_delete_not_exist.xml
new file mode 100644
index 000000000..4df49149f
--- /dev/null
+++ b/tests/files/mock_responses/flows/flow_delete_not_exist.xml
@@ -0,0 +1,4 @@
+
+ 322
+ flow does not exist
+
diff --git a/tests/files/mock_responses/flows/flow_delete_not_owned.xml b/tests/files/mock_responses/flows/flow_delete_not_owned.xml
new file mode 100644
index 000000000..3aa9a9ef2
--- /dev/null
+++ b/tests/files/mock_responses/flows/flow_delete_not_owned.xml
@@ -0,0 +1,4 @@
+
+ 323
+ flow is not owned by you
+
diff --git a/tests/files/mock_responses/flows/flow_delete_successful.xml b/tests/files/mock_responses/flows/flow_delete_successful.xml
new file mode 100644
index 000000000..7638e942d
--- /dev/null
+++ b/tests/files/mock_responses/flows/flow_delete_successful.xml
@@ -0,0 +1,3 @@
+
+ 33364
+
diff --git a/tests/files/mock_responses/runs/run_delete_not_exist.xml b/tests/files/mock_responses/runs/run_delete_not_exist.xml
new file mode 100644
index 000000000..855c223fa
--- /dev/null
+++ b/tests/files/mock_responses/runs/run_delete_not_exist.xml
@@ -0,0 +1,4 @@
+
+ 392
+ Run does not exist
+
diff --git a/tests/files/mock_responses/runs/run_delete_not_owned.xml b/tests/files/mock_responses/runs/run_delete_not_owned.xml
new file mode 100644
index 000000000..551252e22
--- /dev/null
+++ b/tests/files/mock_responses/runs/run_delete_not_owned.xml
@@ -0,0 +1,4 @@
+
+ 393
+ Run is not owned by you
+
diff --git a/tests/files/mock_responses/runs/run_delete_successful.xml b/tests/files/mock_responses/runs/run_delete_successful.xml
new file mode 100644
index 000000000..fe4233afa
--- /dev/null
+++ b/tests/files/mock_responses/runs/run_delete_successful.xml
@@ -0,0 +1,3 @@
+
+ 10591880
+
diff --git a/tests/files/mock_responses/tasks/task_delete_has_runs.xml b/tests/files/mock_responses/tasks/task_delete_has_runs.xml
new file mode 100644
index 000000000..87a92540d
--- /dev/null
+++ b/tests/files/mock_responses/tasks/task_delete_has_runs.xml
@@ -0,0 +1,4 @@
+
+ 454
+ Task is executed in some runs. Delete these first
+
diff --git a/tests/files/mock_responses/tasks/task_delete_not_exist.xml b/tests/files/mock_responses/tasks/task_delete_not_exist.xml
new file mode 100644
index 000000000..8a262af29
--- /dev/null
+++ b/tests/files/mock_responses/tasks/task_delete_not_exist.xml
@@ -0,0 +1,4 @@
+
+ 452
+ Task does not exist
+
diff --git a/tests/files/mock_responses/tasks/task_delete_not_owned.xml b/tests/files/mock_responses/tasks/task_delete_not_owned.xml
new file mode 100644
index 000000000..3d504772b
--- /dev/null
+++ b/tests/files/mock_responses/tasks/task_delete_not_owned.xml
@@ -0,0 +1,4 @@
+
+ 453
+ Task is not owned by you
+
diff --git a/tests/files/mock_responses/tasks/task_delete_successful.xml b/tests/files/mock_responses/tasks/task_delete_successful.xml
new file mode 100644
index 000000000..594b6e992
--- /dev/null
+++ b/tests/files/mock_responses/tasks/task_delete_successful.xml
@@ -0,0 +1,3 @@
+
+ 361323
+
diff --git a/tests/files/datasets/-1/dataset.arff b/tests/files/org/openml/test/datasets/-1/dataset.arff
similarity index 100%
rename from tests/files/datasets/-1/dataset.arff
rename to tests/files/org/openml/test/datasets/-1/dataset.arff
diff --git a/tests/files/datasets/-1/description.xml b/tests/files/org/openml/test/datasets/-1/description.xml
similarity index 100%
rename from tests/files/datasets/-1/description.xml
rename to tests/files/org/openml/test/datasets/-1/description.xml
diff --git a/tests/files/datasets/-1/features.xml b/tests/files/org/openml/test/datasets/-1/features.xml
similarity index 80%
rename from tests/files/datasets/-1/features.xml
rename to tests/files/org/openml/test/datasets/-1/features.xml
index e07e991eb..01adbf5a8 100644
--- a/tests/files/datasets/-1/features.xml
+++ b/tests/files/org/openml/test/datasets/-1/features.xml
@@ -6,6 +6,7 @@
false
false
false
+ 0
1
@@ -14,6 +15,7 @@
false
false
false
+ 0
2
@@ -22,6 +24,7 @@
false
false
false
+ 0
3
@@ -30,6 +33,7 @@
false
false
false
+ 0
4
@@ -38,6 +42,7 @@
false
false
false
+ 0
5
@@ -46,6 +51,7 @@
false
false
false
+ 0
6
@@ -54,6 +60,7 @@
false
false
false
+ 0
7
@@ -62,6 +69,7 @@
false
false
false
+ 0
8
@@ -70,6 +78,7 @@
false
false
false
+ 0
9
@@ -78,6 +87,7 @@
false
false
false
+ 0
10
@@ -86,6 +96,7 @@
false
false
false
+ 0
11
@@ -94,6 +105,7 @@
false
false
false
+ 0
12
@@ -102,6 +114,7 @@
false
false
false
+ 0
13
@@ -110,6 +123,7 @@
false
false
false
+ 0
14
@@ -118,6 +132,7 @@
false
false
false
+ 0
15
@@ -126,6 +141,7 @@
false
false
false
+ 0
16
@@ -134,6 +150,7 @@
false
false
false
+ 0
17
@@ -142,6 +159,7 @@
false
false
false
+ 0
18
@@ -150,6 +168,7 @@
false
false
false
+ 0
19
@@ -158,6 +177,7 @@
false
false
false
+ 0
20
@@ -166,6 +186,7 @@
false
false
false
+ 0
21
@@ -174,6 +195,7 @@
false
false
false
+ 0
22
@@ -182,6 +204,7 @@
false
false
false
+ 0
23
@@ -190,6 +213,7 @@
false
false
false
+ 0
24
@@ -198,6 +222,7 @@
false
false
false
+ 0
25
@@ -206,6 +231,7 @@
false
false
false
+ 0
26
@@ -214,6 +240,7 @@
false
false
false
+ 0
27
@@ -222,6 +249,7 @@
false
false
false
+ 0
28
@@ -230,6 +258,7 @@
false
false
false
+ 0
29
@@ -238,6 +267,7 @@
false
false
false
+ 0
30
@@ -246,6 +276,7 @@
false
false
false
+ 0
31
@@ -254,6 +285,7 @@
false
false
false
+ 0
32
@@ -262,6 +294,7 @@
false
false
false
+ 0
33
@@ -270,6 +303,7 @@
false
false
false
+ 0
34
@@ -278,6 +312,7 @@
false
false
false
+ 0
35
@@ -286,6 +321,7 @@
false
false
false
+ 0
36
@@ -294,6 +330,7 @@
false
false
false
+ 0
37
@@ -302,6 +339,7 @@
false
false
false
+ 0
38
@@ -310,6 +348,7 @@
false
false
false
+ 0
39
@@ -318,6 +357,7 @@
false
false
false
+ 0
40
@@ -326,6 +366,7 @@
false
false
false
+ 0
41
@@ -334,6 +375,7 @@
false
false
false
+ 0
42
@@ -342,6 +384,7 @@
false
false
false
+ 0
43
@@ -350,6 +393,7 @@
false
false
false
+ 0
44
@@ -358,6 +402,7 @@
false
false
false
+ 0
45
@@ -366,6 +411,7 @@
false
false
false
+ 0
46
@@ -374,6 +420,7 @@
false
false
false
+ 0
47
@@ -382,6 +429,7 @@
false
false
false
+ 0
48
@@ -390,6 +438,7 @@
false
false
false
+ 0
49
@@ -398,6 +447,7 @@
false
false
false
+ 0
50
@@ -406,6 +456,7 @@
false
false
false
+ 0
51
@@ -414,6 +465,7 @@
false
false
false
+ 0
52
@@ -422,6 +474,7 @@
false
false
false
+ 0
53
@@ -430,6 +483,7 @@
false
false
false
+ 0
54
@@ -438,6 +492,7 @@
false
false
false
+ 0
55
@@ -446,6 +501,7 @@
false
false
false
+ 0
56
@@ -454,6 +510,7 @@
false
false
false
+ 0
57
@@ -462,6 +519,7 @@
false
false
false
+ 0
58
@@ -470,6 +528,7 @@
false
false
false
+ 0
59
@@ -478,6 +537,7 @@
false
false
false
+ 0
60
@@ -486,6 +546,7 @@
false
false
false
+ 0
61
@@ -494,6 +555,7 @@
false
false
false
+ 0
62
@@ -502,6 +564,7 @@
false
false
false
+ 0
63
@@ -510,6 +573,7 @@
false
false
false
+ 0
64
@@ -518,6 +582,7 @@
false
false
false
+ 0
65
@@ -526,6 +591,7 @@
false
false
false
+ 0
66
@@ -534,6 +600,7 @@
false
false
false
+ 0
67
@@ -542,6 +609,7 @@
false
false
false
+ 0
68
@@ -550,6 +618,7 @@
false
false
false
+ 0
69
@@ -558,6 +627,7 @@
false
false
false
+ 0
70
@@ -566,6 +636,7 @@
false
false
false
+ 0
71
@@ -574,6 +645,7 @@
false
false
false
+ 0
72
@@ -582,6 +654,7 @@
false
false
false
+ 0
73
@@ -590,6 +663,7 @@
false
false
false
+ 0
74
@@ -598,6 +672,7 @@
false
false
false
+ 0
75
@@ -606,6 +681,7 @@
false
false
false
+ 0
76
@@ -614,6 +690,7 @@
false
false
false
+ 0
77
@@ -622,6 +699,7 @@
false
false
false
+ 0
78
@@ -630,6 +708,7 @@
false
false
false
+ 0
79
@@ -638,6 +717,7 @@
false
false
false
+ 0
80
@@ -646,6 +726,7 @@
false
false
false
+ 0
81
@@ -654,6 +735,7 @@
false
false
false
+ 0
82
@@ -662,6 +744,7 @@
false
false
false
+ 0
83
@@ -670,6 +753,7 @@
false
false
false
+ 0
84
@@ -678,6 +762,7 @@
false
false
false
+ 0
85
@@ -686,6 +771,7 @@
false
false
false
+ 0
86
@@ -694,6 +780,7 @@
false
false
false
+ 0
87
@@ -702,6 +789,7 @@
false
false
false
+ 0
88
@@ -710,6 +798,7 @@
false
false
false
+ 0
89
@@ -718,6 +807,7 @@
false
false
false
+ 0
90
@@ -726,6 +816,7 @@
false
false
false
+ 0
91
@@ -734,6 +825,7 @@
false
false
false
+ 0
92
@@ -742,6 +834,7 @@
false
false
false
+ 0
93
@@ -750,6 +843,7 @@
false
false
false
+ 0
94
@@ -758,6 +852,7 @@
false
false
false
+ 0
95
@@ -766,6 +861,7 @@
false
false
false
+ 0
96
@@ -774,6 +870,7 @@
false
false
false
+ 0
97
@@ -782,6 +879,7 @@
false
false
false
+ 0
98
@@ -790,6 +888,7 @@
false
false
false
+ 0
99
@@ -798,6 +897,7 @@
false
false
false
+ 0
100
@@ -806,6 +906,7 @@
false
false
false
+ 0
101
@@ -814,6 +915,7 @@
false
false
false
+ 0
102
@@ -822,6 +924,7 @@
false
false
false
+ 0
103
@@ -830,6 +933,7 @@
false
false
false
+ 0
104
@@ -838,6 +942,7 @@
false
false
false
+ 0
105
@@ -846,6 +951,7 @@
false
false
false
+ 0
106
@@ -854,6 +960,7 @@
false
false
false
+ 0
107
@@ -862,6 +969,7 @@
false
false
false
+ 0
108
@@ -870,6 +978,7 @@
false
false
false
+ 0
109
@@ -878,6 +987,7 @@
false
false
false
+ 0
110
@@ -886,6 +996,7 @@
false
false
false
+ 0
111
@@ -894,6 +1005,7 @@
false
false
false
+ 0
112
@@ -902,6 +1014,7 @@
false
false
false
+ 0
113
@@ -910,6 +1023,7 @@
false
false
false
+ 0
114
@@ -918,6 +1032,7 @@
false
false
false
+ 0
115
@@ -926,6 +1041,7 @@
false
false
false
+ 0
116
@@ -934,6 +1050,7 @@
false
false
false
+ 0
117
@@ -942,6 +1059,7 @@
false
false
false
+ 0
118
@@ -950,6 +1068,7 @@
false
false
false
+ 0
119
@@ -958,6 +1077,7 @@
false
false
false
+ 0
120
@@ -966,6 +1086,7 @@
false
false
false
+ 0
121
@@ -974,6 +1095,7 @@
false
false
false
+ 0
122
@@ -982,6 +1104,7 @@
false
false
false
+ 0
123
@@ -990,6 +1113,7 @@
false
false
false
+ 0
124
@@ -998,6 +1122,7 @@
false
false
false
+ 0
125
@@ -1006,6 +1131,7 @@
false
false
false
+ 0
126
@@ -1014,6 +1140,7 @@
false
false
false
+ 0
127
@@ -1022,6 +1149,7 @@
false
false
false
+ 0
128
@@ -1030,6 +1158,7 @@
false
false
false
+ 0
129
@@ -1038,6 +1167,7 @@
false
false
false
+ 0
130
@@ -1046,6 +1176,7 @@
false
false
false
+ 0
131
@@ -1054,6 +1185,7 @@
false
false
false
+ 0
132
@@ -1062,6 +1194,7 @@
false
false
false
+ 0
133
@@ -1070,6 +1203,7 @@
false
false
false
+ 0
134
@@ -1078,6 +1212,7 @@
false
false
false
+ 0
135
@@ -1086,6 +1221,7 @@
false
false
false
+ 0
136
@@ -1094,6 +1230,7 @@
false
false
false
+ 0
137
@@ -1102,6 +1239,7 @@
false
false
false
+ 0
138
@@ -1110,6 +1248,7 @@
false
false
false
+ 0
139
@@ -1118,6 +1257,7 @@
false
false
false
+ 0
140
@@ -1126,6 +1266,7 @@
false
false
false
+ 0
141
@@ -1134,6 +1275,7 @@
false
false
false
+ 0
142
@@ -1142,6 +1284,7 @@
false
false
false
+ 0
143
@@ -1150,6 +1293,7 @@
false
false
false
+ 0
144
@@ -1158,6 +1302,7 @@
false
false
false
+ 0
145
@@ -1166,6 +1311,7 @@
false
false
false
+ 0
146
@@ -1174,6 +1320,7 @@
false
false
false
+ 0
147
@@ -1182,6 +1329,7 @@
false
false
false
+ 0
148
@@ -1190,6 +1338,7 @@
false
false
false
+ 0
149
@@ -1198,6 +1347,7 @@
false
false
false
+ 0
150
@@ -1206,6 +1356,7 @@
false
false
false
+ 0
151
@@ -1214,6 +1365,7 @@
false
false
false
+ 0
152
@@ -1222,6 +1374,7 @@
false
false
false
+ 0
153
@@ -1230,6 +1383,7 @@
false
false
false
+ 0
154
@@ -1238,6 +1392,7 @@
false
false
false
+ 0
155
@@ -1246,6 +1401,7 @@
false
false
false
+ 0
156
@@ -1254,6 +1410,7 @@
false
false
false
+ 0
157
@@ -1262,6 +1419,7 @@
false
false
false
+ 0
158
@@ -1270,6 +1428,7 @@
false
false
false
+ 0
159
@@ -1278,6 +1437,7 @@
false
false
false
+ 0
160
@@ -1286,6 +1446,7 @@
false
false
false
+ 0
161
@@ -1294,6 +1455,7 @@
false
false
false
+ 0
162
@@ -1302,6 +1464,7 @@
false
false
false
+ 0
163
@@ -1310,6 +1473,7 @@
false
false
false
+ 0
164
@@ -1318,6 +1482,7 @@
false
false
false
+ 0
165
@@ -1326,6 +1491,7 @@
false
false
false
+ 0
166
@@ -1334,6 +1500,7 @@
false
false
false
+ 0
167
@@ -1342,6 +1509,7 @@
false
false
false
+ 0
168
@@ -1350,6 +1518,7 @@
false
false
false
+ 0
169
@@ -1358,6 +1527,7 @@
false
false
false
+ 0
170
@@ -1366,6 +1536,7 @@
false
false
false
+ 0
171
@@ -1374,6 +1545,7 @@
false
false
false
+ 0
172
@@ -1382,6 +1554,7 @@
false
false
false
+ 0
173
@@ -1390,6 +1563,7 @@
false
false
false
+ 0
174
@@ -1398,6 +1572,7 @@
false
false
false
+ 0
175
@@ -1406,6 +1581,7 @@
false
false
false
+ 0
176
@@ -1414,6 +1590,7 @@
false
false
false
+ 0
177
@@ -1422,6 +1599,7 @@
false
false
false
+ 0
178
@@ -1430,6 +1608,7 @@
false
false
false
+ 0
179
@@ -1438,6 +1617,7 @@
false
false
false
+ 0
180
@@ -1446,6 +1626,7 @@
false
false
false
+ 0
181
@@ -1454,6 +1635,7 @@
false
false
false
+ 0
182
@@ -1462,6 +1644,7 @@
false
false
false
+ 0
183
@@ -1470,6 +1653,7 @@
false
false
false
+ 0
184
@@ -1478,6 +1662,7 @@
false
false
false
+ 0
185
@@ -1486,6 +1671,7 @@
false
false
false
+ 0
186
@@ -1494,6 +1680,7 @@
false
false
false
+ 0
187
@@ -1502,6 +1689,7 @@
false
false
false
+ 0
188
@@ -1510,6 +1698,7 @@
false
false
false
+ 0
189
@@ -1518,6 +1707,7 @@
false
false
false
+ 0
190
@@ -1526,6 +1716,7 @@
false
false
false
+ 0
191
@@ -1534,6 +1725,7 @@
false
false
false
+ 0
192
@@ -1542,6 +1734,7 @@
false
false
false
+ 0
193
@@ -1550,6 +1743,7 @@
false
false
false
+ 0
194
@@ -1558,6 +1752,7 @@
false
false
false
+ 0
195
@@ -1566,6 +1761,7 @@
false
false
false
+ 0
196
@@ -1574,6 +1770,7 @@
false
false
false
+ 0
197
@@ -1582,6 +1779,7 @@
false
false
false
+ 0
198
@@ -1590,6 +1788,7 @@
false
false
false
+ 0
199
@@ -1598,6 +1797,7 @@
false
false
false
+ 0
200
@@ -1606,6 +1806,7 @@
false
false
false
+ 0
201
@@ -1614,6 +1815,7 @@
false
false
false
+ 0
202
@@ -1622,6 +1824,7 @@
false
false
false
+ 0
203
@@ -1630,6 +1833,7 @@
false
false
false
+ 0
204
@@ -1638,6 +1842,7 @@
false
false
false
+ 0
205
@@ -1646,6 +1851,7 @@
false
false
false
+ 0
206
@@ -1654,6 +1860,7 @@
false
false
false
+ 0
207
@@ -1662,6 +1869,7 @@
false
false
false
+ 0
208
@@ -1670,6 +1878,7 @@
false
false
false
+ 0
209
@@ -1678,6 +1887,7 @@
false
false
false
+ 0
210
@@ -1686,6 +1896,7 @@
false
false
false
+ 0
211
@@ -1694,6 +1905,7 @@
false
false
false
+ 0
212
@@ -1702,6 +1914,7 @@
false
false
false
+ 0
213
@@ -1710,6 +1923,7 @@
false
false
false
+ 0
214
@@ -1718,6 +1932,7 @@
false
false
false
+ 0
215
@@ -1726,6 +1941,7 @@
false
false
false
+ 0
216
@@ -1734,6 +1950,7 @@
false
false
false
+ 0
217
@@ -1742,6 +1959,7 @@
false
false
false
+ 0
218
@@ -1750,6 +1968,7 @@
false
false
false
+ 0
219
@@ -1758,6 +1977,7 @@
false
false
false
+ 0
220
@@ -1766,6 +1986,7 @@
false
false
false
+ 0
221
@@ -1774,6 +1995,7 @@
false
false
false
+ 0
222
@@ -1782,6 +2004,7 @@
false
false
false
+ 0
223
@@ -1790,6 +2013,7 @@
false
false
false
+ 0
224
@@ -1798,6 +2022,7 @@
false
false
false
+ 0
225
@@ -1806,6 +2031,7 @@
false
false
false
+ 0
226
@@ -1814,6 +2040,7 @@
false
false
false
+ 0
227
@@ -1822,6 +2049,7 @@
false
false
false
+ 0
228
@@ -1830,6 +2058,7 @@
false
false
false
+ 0
229
@@ -1838,6 +2067,7 @@
false
false
false
+ 0
230
@@ -1846,6 +2076,7 @@
false
false
false
+ 0
231
@@ -1854,6 +2085,7 @@
false
false
false
+ 0
232
@@ -1862,6 +2094,7 @@
false
false
false
+ 0
233
@@ -1870,6 +2103,7 @@
false
false
false
+ 0
234
@@ -1878,6 +2112,7 @@
false
false
false
+ 0
235
@@ -1886,6 +2121,7 @@
false
false
false
+ 0
236
@@ -1894,6 +2130,7 @@
false
false
false
+ 0
237
@@ -1902,6 +2139,7 @@
false
false
false
+ 0
238
@@ -1910,6 +2148,7 @@
false
false
false
+ 0
239
@@ -1918,6 +2157,7 @@
false
false
false
+ 0
240
@@ -1926,6 +2166,7 @@
false
false
false
+ 0
241
@@ -1934,6 +2175,7 @@
false
false
false
+ 0
242
@@ -1942,6 +2184,7 @@
false
false
false
+ 0
243
@@ -1950,6 +2193,7 @@
false
false
false
+ 0
244
@@ -1958,6 +2202,7 @@
false
false
false
+ 0
245
@@ -1966,6 +2211,7 @@
false
false
false
+ 0
246
@@ -1974,6 +2220,7 @@
false
false
false
+ 0
247
@@ -1982,6 +2229,7 @@
false
false
false
+ 0
248
@@ -1990,6 +2238,7 @@
false
false
false
+ 0
249
@@ -1998,6 +2247,7 @@
false
false
false
+ 0
250
@@ -2006,6 +2256,7 @@
false
false
false
+ 0
251
@@ -2014,6 +2265,7 @@
false
false
false
+ 0
252
@@ -2022,6 +2274,7 @@
false
false
false
+ 0
253
@@ -2030,6 +2283,7 @@
false
false
false
+ 0
254
@@ -2038,6 +2292,7 @@
false
false
false
+ 0
255
@@ -2046,6 +2301,7 @@
false
false
false
+ 0
256
@@ -2054,6 +2310,7 @@
false
false
false
+ 0
257
@@ -2062,6 +2319,7 @@
false
false
false
+ 0
258
@@ -2070,6 +2328,7 @@
false
false
false
+ 0
259
@@ -2078,6 +2337,7 @@
false
false
false
+ 0
260
@@ -2086,6 +2346,7 @@
false
false
false
+ 0
261
@@ -2094,6 +2355,7 @@
false
false
false
+ 0
262
@@ -2102,6 +2364,7 @@
false
false
false
+ 0
263
@@ -2110,6 +2373,7 @@
false
false
false
+ 0
264
@@ -2118,6 +2382,7 @@
false
false
false
+ 0
265
@@ -2126,6 +2391,7 @@
false
false
false
+ 0
266
@@ -2134,6 +2400,7 @@
false
false
false
+ 0
267
@@ -2142,6 +2409,7 @@
false
false
false
+ 0
268
@@ -2150,6 +2418,7 @@
false
false
false
+ 0
269
@@ -2158,6 +2427,7 @@
false
false
false
+ 0
270
@@ -2166,6 +2436,7 @@
false
false
false
+ 0
271
@@ -2174,6 +2445,7 @@
false
false
false
+ 0
272
@@ -2182,6 +2454,7 @@
false
false
false
+ 0
273
@@ -2190,6 +2463,7 @@
false
false
false
+ 0
274
@@ -2198,6 +2472,7 @@
false
false
false
+ 0
275
@@ -2206,6 +2481,7 @@
false
false
false
+ 0
276
@@ -2214,6 +2490,7 @@
false
false
false
+ 0
277
@@ -2222,6 +2499,7 @@
false
false
false
+ 0
278
@@ -2230,6 +2508,7 @@
false
false
false
+ 0
279
@@ -2238,6 +2517,7 @@
false
false
false
+ 0
280
@@ -2246,6 +2526,7 @@
false
false
false
+ 0
281
@@ -2254,6 +2535,7 @@
false
false
false
+ 0
282
@@ -2262,6 +2544,7 @@
false
false
false
+ 0
283
@@ -2270,6 +2553,7 @@
false
false
false
+ 0
284
@@ -2278,6 +2562,7 @@
false
false
false
+ 0
285
@@ -2286,6 +2571,7 @@
false
false
false
+ 0
286
@@ -2294,6 +2580,7 @@
false
false
false
+ 0
287
@@ -2302,6 +2589,7 @@
false
false
false
+ 0
288
@@ -2310,6 +2598,7 @@
false
false
false
+ 0
289
@@ -2318,6 +2607,7 @@
false
false
false
+ 0
290
@@ -2326,6 +2616,7 @@
false
false
false
+ 0
291
@@ -2334,6 +2625,7 @@
false
false
false
+ 0
292
@@ -2342,6 +2634,7 @@
false
false
false
+ 0
293
@@ -2350,6 +2643,7 @@
false
false
false
+ 0
294
@@ -2358,6 +2652,7 @@
false
false
false
+ 0
295
@@ -2366,6 +2661,7 @@
false
false
false
+ 0
296
@@ -2374,6 +2670,7 @@
false
false
false
+ 0
297
@@ -2382,6 +2679,7 @@
false
false
false
+ 0
298
@@ -2390,6 +2688,7 @@
false
false
false
+ 0
299
@@ -2398,6 +2697,7 @@
false
false
false
+ 0
300
@@ -2406,6 +2706,7 @@
false
false
false
+ 0
301
@@ -2414,6 +2715,7 @@
false
false
false
+ 0
302
@@ -2422,6 +2724,7 @@
false
false
false
+ 0
303
@@ -2430,6 +2733,7 @@
false
false
false
+ 0
304
@@ -2438,6 +2742,7 @@
false
false
false
+ 0
305
@@ -2446,6 +2751,7 @@
false
false
false
+ 0
306
@@ -2454,6 +2760,7 @@
false
false
false
+ 0
307
@@ -2462,6 +2769,7 @@
false
false
false
+ 0
308
@@ -2470,6 +2778,7 @@
false
false
false
+ 0
309
@@ -2478,6 +2787,7 @@
false
false
false
+ 0
310
@@ -2486,6 +2796,7 @@
false
false
false
+ 0
311
@@ -2494,6 +2805,7 @@
false
false
false
+ 0
312
@@ -2502,6 +2814,7 @@
false
false
false
+ 0
313
@@ -2510,6 +2823,7 @@
false
false
false
+ 0
314
@@ -2518,6 +2832,7 @@
false
false
false
+ 0
315
@@ -2526,6 +2841,7 @@
false
false
false
+ 0
316
@@ -2534,6 +2850,7 @@
false
false
false
+ 0
317
@@ -2542,6 +2859,7 @@
false
false
false
+ 0
318
@@ -2550,6 +2868,7 @@
false
false
false
+ 0
319
@@ -2558,6 +2877,7 @@
false
false
false
+ 0
320
@@ -2566,6 +2886,7 @@
false
false
false
+ 0
321
@@ -2574,6 +2895,7 @@
false
false
false
+ 0
322
@@ -2582,6 +2904,7 @@
false
false
false
+ 0
323
@@ -2590,6 +2913,7 @@
false
false
false
+ 0
324
@@ -2598,6 +2922,7 @@
false
false
false
+ 0
325
@@ -2606,6 +2931,7 @@
false
false
false
+ 0
326
@@ -2614,6 +2940,7 @@
false
false
false
+ 0
327
@@ -2622,6 +2949,7 @@
false
false
false
+ 0
328
@@ -2630,6 +2958,7 @@
false
false
false
+ 0
329
@@ -2638,6 +2967,7 @@
false
false
false
+ 0
330
@@ -2646,6 +2976,7 @@
false
false
false
+ 0
331
@@ -2654,6 +2985,7 @@
false
false
false
+ 0
332
@@ -2662,6 +2994,7 @@
false
false
false
+ 0
333
@@ -2670,6 +3003,7 @@
false
false
false
+ 0
334
@@ -2678,6 +3012,7 @@
false
false
false
+ 0
335
@@ -2686,6 +3021,7 @@
false
false
false
+ 0
336
@@ -2694,6 +3030,7 @@
false
false
false
+ 0
337
@@ -2702,6 +3039,7 @@
false
false
false
+ 0
338
@@ -2710,6 +3048,7 @@
false
false
false
+ 0
339
@@ -2718,6 +3057,7 @@
false
false
false
+ 0
340
@@ -2726,6 +3066,7 @@
false
false
false
+ 0
341
@@ -2734,6 +3075,7 @@
false
false
false
+ 0
342
@@ -2742,6 +3084,7 @@
false
false
false
+ 0
343
@@ -2750,6 +3093,7 @@
false
false
false
+ 0
344
@@ -2758,6 +3102,7 @@
false
false
false
+ 0
345
@@ -2766,6 +3111,7 @@
false
false
false
+ 0
346
@@ -2774,6 +3120,7 @@
false
false
false
+ 0
347
@@ -2782,6 +3129,7 @@
false
false
false
+ 0
348
@@ -2790,6 +3138,7 @@
false
false
false
+ 0
349
@@ -2798,6 +3147,7 @@
false
false
false
+ 0
350
@@ -2806,6 +3156,7 @@
false
false
false
+ 0
351
@@ -2814,6 +3165,7 @@
false
false
false
+ 0
352
@@ -2822,6 +3174,7 @@
false
false
false
+ 0
353
@@ -2830,6 +3183,7 @@
false
false
false
+ 0
354
@@ -2838,6 +3192,7 @@
false
false
false
+ 0
355
@@ -2846,6 +3201,7 @@
false
false
false
+ 0
356
@@ -2854,6 +3210,7 @@
false
false
false
+ 0
357
@@ -2862,6 +3219,7 @@
false
false
false
+ 0
358
@@ -2870,6 +3228,7 @@
false
false
false
+ 0
359
@@ -2878,6 +3237,7 @@
false
false
false
+ 0
360
@@ -2886,6 +3246,7 @@
false
false
false
+ 0
361
@@ -2894,6 +3255,7 @@
false
false
false
+ 0
362
@@ -2902,6 +3264,7 @@
false
false
false
+ 0
363
@@ -2910,6 +3273,7 @@
false
false
false
+ 0
364
@@ -2918,6 +3282,7 @@
false
false
false
+ 0
365
@@ -2926,6 +3291,7 @@
false
false
false
+ 0
366
@@ -2934,6 +3300,7 @@
false
false
false
+ 0
367
@@ -2942,6 +3309,7 @@
false
false
false
+ 0
368
@@ -2950,6 +3318,7 @@
false
false
false
+ 0
369
@@ -2958,6 +3327,7 @@
false
false
false
+ 0
370
@@ -2966,6 +3336,7 @@
false
false
false
+ 0
371
@@ -2974,6 +3345,7 @@
false
false
false
+ 0
372
@@ -2982,6 +3354,7 @@
false
false
false
+ 0
373
@@ -2990,6 +3363,7 @@
false
false
false
+ 0
374
@@ -2998,6 +3372,7 @@
false
false
false
+ 0
375
@@ -3006,6 +3381,7 @@
false
false
false
+ 0
376
@@ -3014,6 +3390,7 @@
false
false
false
+ 0
377
@@ -3022,6 +3399,7 @@
false
false
false
+ 0
378
@@ -3030,6 +3408,7 @@
false
false
false
+ 0
379
@@ -3038,6 +3417,7 @@
false
false
false
+ 0
380
@@ -3046,6 +3426,7 @@
false
false
false
+ 0
381
@@ -3054,6 +3435,7 @@
false
false
false
+ 0
382
@@ -3062,6 +3444,7 @@
false
false
false
+ 0
383
@@ -3070,6 +3453,7 @@
false
false
false
+ 0
384
@@ -3078,6 +3462,7 @@
false
false
false
+ 0
385
@@ -3086,6 +3471,7 @@
false
false
false
+ 0
386
@@ -3094,6 +3480,7 @@
false
false
false
+ 0
387
@@ -3102,6 +3489,7 @@
false
false
false
+ 0
388
@@ -3110,6 +3498,7 @@
false
false
false
+ 0
389
@@ -3118,6 +3507,7 @@
false
false
false
+ 0
390
@@ -3126,6 +3516,7 @@
false
false
false
+ 0
391
@@ -3134,6 +3525,7 @@
false
false
false
+ 0
392
@@ -3142,6 +3534,7 @@
false
false
false
+ 0
393
@@ -3150,6 +3543,7 @@
false
false
false
+ 0
394
@@ -3158,6 +3552,7 @@
false
false
false
+ 0
395
@@ -3166,6 +3561,7 @@
false
false
false
+ 0
396
@@ -3174,6 +3570,7 @@
false
false
false
+ 0
397
@@ -3182,6 +3579,7 @@
false
false
false
+ 0
398
@@ -3190,6 +3588,7 @@
false
false
false
+ 0
399
@@ -3198,6 +3597,7 @@
false
false
false
+ 0
400
@@ -3206,6 +3606,7 @@
false
false
false
+ 0
401
@@ -3214,6 +3615,7 @@
false
false
false
+ 0
402
@@ -3222,6 +3624,7 @@
false
false
false
+ 0
403
@@ -3230,6 +3633,7 @@
false
false
false
+ 0
404
@@ -3238,6 +3642,7 @@
false
false
false
+ 0
405
@@ -3246,6 +3651,7 @@
false
false
false
+ 0
406
@@ -3254,6 +3660,7 @@
false
false
false
+ 0
407
@@ -3262,6 +3669,7 @@
false
false
false
+ 0
408
@@ -3270,6 +3678,7 @@
false
false
false
+ 0
409
@@ -3278,6 +3687,7 @@
false
false
false
+ 0
410
@@ -3286,6 +3696,7 @@
false
false
false
+ 0
411
@@ -3294,6 +3705,7 @@
false
false
false
+ 0
412
@@ -3302,6 +3714,7 @@
false
false
false
+ 0
413
@@ -3310,6 +3723,7 @@
false
false
false
+ 0
414
@@ -3318,6 +3732,7 @@
false
false
false
+ 0
415
@@ -3326,6 +3741,7 @@
false
false
false
+ 0
416
@@ -3334,6 +3750,7 @@
false
false
false
+ 0
417
@@ -3342,6 +3759,7 @@
false
false
false
+ 0
418
@@ -3350,6 +3768,7 @@
false
false
false
+ 0
419
@@ -3358,6 +3777,7 @@
false
false
false
+ 0
420
@@ -3366,6 +3786,7 @@
false
false
false
+ 0
421
@@ -3374,6 +3795,7 @@
false
false
false
+ 0
422
@@ -3382,6 +3804,7 @@
false
false
false
+ 0
423
@@ -3390,6 +3813,7 @@
false
false
false
+ 0
424
@@ -3398,6 +3822,7 @@
false
false
false
+ 0
425
@@ -3406,6 +3831,7 @@
false
false
false
+ 0
426
@@ -3414,6 +3840,7 @@
false
false
false
+ 0
427
@@ -3422,6 +3849,7 @@
false
false
false
+ 0
428
@@ -3430,6 +3858,7 @@
false
false
false
+ 0
429
@@ -3438,6 +3867,7 @@
false
false
false
+ 0
430
@@ -3446,6 +3876,7 @@
false
false
false
+ 0
431
@@ -3454,6 +3885,7 @@
false
false
false
+ 0
432
@@ -3462,6 +3894,7 @@
false
false
false
+ 0
433
@@ -3470,6 +3903,7 @@
false
false
false
+ 0
434
@@ -3478,6 +3912,7 @@
false
false
false
+ 0
435
@@ -3486,6 +3921,7 @@
false
false
false
+ 0
436
@@ -3494,6 +3930,7 @@
false
false
false
+ 0
437
@@ -3502,6 +3939,7 @@
false
false
false
+ 0
438
@@ -3510,6 +3948,7 @@
false
false
false
+ 0
439
@@ -3518,6 +3957,7 @@
false
false
false
+ 0
440
@@ -3526,6 +3966,7 @@
false
false
false
+ 0
441
@@ -3534,6 +3975,7 @@
false
false
false
+ 0
442
@@ -3542,6 +3984,7 @@
false
false
false
+ 0
443
@@ -3550,6 +3993,7 @@
false
false
false
+ 0
444
@@ -3558,6 +4002,7 @@
false
false
false
+ 0
445
@@ -3566,6 +4011,7 @@
false
false
false
+ 0
446
@@ -3574,6 +4020,7 @@
false
false
false
+ 0
447
@@ -3582,6 +4029,7 @@
false
false
false
+ 0
448
@@ -3590,6 +4038,7 @@
false
false
false
+ 0
449
@@ -3598,6 +4047,7 @@
false
false
false
+ 0
450
@@ -3606,6 +4056,7 @@
false
false
false
+ 0
451
@@ -3614,6 +4065,7 @@
false
false
false
+ 0
452
@@ -3622,6 +4074,7 @@
false
false
false
+ 0
453
@@ -3630,6 +4083,7 @@
false
false
false
+ 0
454
@@ -3638,6 +4092,7 @@
false
false
false
+ 0
455
@@ -3646,6 +4101,7 @@
false
false
false
+ 0
456
@@ -3654,6 +4110,7 @@
false
false
false
+ 0
457
@@ -3662,6 +4119,7 @@
false
false
false
+ 0
458
@@ -3670,6 +4128,7 @@
false
false
false
+ 0
459
@@ -3678,6 +4137,7 @@
false
false
false
+ 0
460
@@ -3686,6 +4146,7 @@
false
false
false
+ 0
461
@@ -3694,6 +4155,7 @@
false
false
false
+ 0
462
@@ -3702,6 +4164,7 @@
false
false
false
+ 0
463
@@ -3710,6 +4173,7 @@
false
false
false
+ 0
464
@@ -3718,6 +4182,7 @@
false
false
false
+ 0
465
@@ -3726,6 +4191,7 @@
false
false
false
+ 0
466
@@ -3734,6 +4200,7 @@
false
false
false
+ 0
467
@@ -3742,6 +4209,7 @@
false
false
false
+ 0
468
@@ -3750,6 +4218,7 @@
false
false
false
+ 0
469
@@ -3758,6 +4227,7 @@
false
false
false
+ 0
470
@@ -3766,6 +4236,7 @@
false
false
false
+ 0
471
@@ -3774,6 +4245,7 @@
false
false
false
+ 0
472
@@ -3782,6 +4254,7 @@
false
false
false
+ 0
473
@@ -3790,6 +4263,7 @@
false
false
false
+ 0
474
@@ -3798,6 +4272,7 @@
false
false
false
+ 0
475
@@ -3806,6 +4281,7 @@
false
false
false
+ 0
476
@@ -3814,6 +4290,7 @@
false
false
false
+ 0
477
@@ -3822,6 +4299,7 @@
false
false
false
+ 0
478
@@ -3830,6 +4308,7 @@
false
false
false
+ 0
479
@@ -3838,6 +4317,7 @@
false
false
false
+ 0
480
@@ -3846,6 +4326,7 @@
false
false
false
+ 0
481
@@ -3854,6 +4335,7 @@
false
false
false
+ 0
482
@@ -3862,6 +4344,7 @@
false
false
false
+ 0
483
@@ -3870,6 +4353,7 @@
false
false
false
+ 0
484
@@ -3878,6 +4362,7 @@
false
false
false
+ 0
485
@@ -3886,6 +4371,7 @@
false
false
false
+ 0
486
@@ -3894,6 +4380,7 @@
false
false
false
+ 0
487
@@ -3902,6 +4389,7 @@
false
false
false
+ 0
488
@@ -3910,6 +4398,7 @@
false
false
false
+ 0
489
@@ -3918,6 +4407,7 @@
false
false
false
+ 0
490
@@ -3926,6 +4416,7 @@
false
false
false
+ 0
491
@@ -3934,6 +4425,7 @@
false
false
false
+ 0
492
@@ -3942,6 +4434,7 @@
false
false
false
+ 0
493
@@ -3950,6 +4443,7 @@
false
false
false
+ 0
494
@@ -3958,6 +4452,7 @@
false
false
false
+ 0
495
@@ -3966,6 +4461,7 @@
false
false
false
+ 0
496
@@ -3974,6 +4470,7 @@
false
false
false
+ 0
497
@@ -3982,6 +4479,7 @@
false
false
false
+ 0
498
@@ -3990,6 +4488,7 @@
false
false
false
+ 0
499
@@ -3998,6 +4497,7 @@
false
false
false
+ 0
500
@@ -4006,6 +4506,7 @@
false
false
false
+ 0
501
@@ -4014,6 +4515,7 @@
false
false
false
+ 0
502
@@ -4022,6 +4524,7 @@
false
false
false
+ 0
503
@@ -4030,6 +4533,7 @@
false
false
false
+ 0
504
@@ -4038,6 +4542,7 @@
false
false
false
+ 0
505
@@ -4046,6 +4551,7 @@
false
false
false
+ 0
506
@@ -4054,6 +4560,7 @@
false
false
false
+ 0
507
@@ -4062,6 +4569,7 @@
false
false
false
+ 0
508
@@ -4070,6 +4578,7 @@
false
false
false
+ 0
509
@@ -4078,6 +4587,7 @@
false
false
false
+ 0
510
@@ -4086,6 +4596,7 @@
false
false
false
+ 0
511
@@ -4094,6 +4605,7 @@
false
false
false
+ 0
512
@@ -4102,6 +4614,7 @@
false
false
false
+ 0
513
@@ -4110,6 +4623,7 @@
false
false
false
+ 0
514
@@ -4118,6 +4632,7 @@
false
false
false
+ 0
515
@@ -4126,6 +4641,7 @@
false
false
false
+ 0
516
@@ -4134,6 +4650,7 @@
false
false
false
+ 0
517
@@ -4142,6 +4659,7 @@
false
false
false
+ 0
518
@@ -4150,6 +4668,7 @@
false
false
false
+ 0
519
@@ -4158,6 +4677,7 @@
false
false
false
+ 0
520
@@ -4166,6 +4686,7 @@
false
false
false
+ 0
521
@@ -4174,6 +4695,7 @@
false
false
false
+ 0
522
@@ -4182,6 +4704,7 @@
false
false
false
+ 0
523
@@ -4190,6 +4713,7 @@
false
false
false
+ 0
524
@@ -4198,6 +4722,7 @@
false
false
false
+ 0
525
@@ -4206,6 +4731,7 @@
false
false
false
+ 0
526
@@ -4214,6 +4740,7 @@
false
false
false
+ 0
527
@@ -4222,6 +4749,7 @@
false
false
false
+ 0
528
@@ -4230,6 +4758,7 @@
false
false
false
+ 0
529
@@ -4238,6 +4767,7 @@
false
false
false
+ 0
530
@@ -4246,6 +4776,7 @@
false
false
false
+ 0
531
@@ -4254,6 +4785,7 @@
false
false
false
+ 0
532
@@ -4262,6 +4794,7 @@
false
false
false
+ 0
533
@@ -4270,6 +4803,7 @@
false
false
false
+ 0
534
@@ -4278,6 +4812,7 @@
false
false
false
+ 0
535
@@ -4286,6 +4821,7 @@
false
false
false
+ 0
536
@@ -4294,6 +4830,7 @@
false
false
false
+ 0
537
@@ -4302,6 +4839,7 @@
false
false
false
+ 0
538
@@ -4310,6 +4848,7 @@
false
false
false
+ 0
539
@@ -4318,6 +4857,7 @@
false
false
false
+ 0
540
@@ -4326,6 +4866,7 @@
false
false
false
+ 0
541
@@ -4334,6 +4875,7 @@
false
false
false
+ 0
542
@@ -4342,6 +4884,7 @@
false
false
false
+ 0
543
@@ -4350,6 +4893,7 @@
false
false
false
+ 0
544
@@ -4358,6 +4902,7 @@
false
false
false
+ 0
545
@@ -4366,6 +4911,7 @@
false
false
false
+ 0
546
@@ -4374,6 +4920,7 @@
false
false
false
+ 0
547
@@ -4382,6 +4929,7 @@
false
false
false
+ 0
548
@@ -4390,6 +4938,7 @@
false
false
false
+ 0
549
@@ -4398,6 +4947,7 @@
false
false
false
+ 0
550
@@ -4406,6 +4956,7 @@
false
false
false
+ 0
551
@@ -4414,6 +4965,7 @@
false
false
false
+ 0
552
@@ -4422,6 +4974,7 @@
false
false
false
+ 0
553
@@ -4430,6 +4983,7 @@
false
false
false
+ 0
554
@@ -4438,6 +4992,7 @@
false
false
false
+ 0
555
@@ -4446,6 +5001,7 @@
false
false
false
+ 0
556
@@ -4454,6 +5010,7 @@
false
false
false
+ 0
557
@@ -4462,6 +5019,7 @@
false
false
false
+ 0
558
@@ -4470,6 +5028,7 @@
false
false
false
+ 0
559
@@ -4478,6 +5037,7 @@
false
false
false
+ 0
560
@@ -4486,6 +5046,7 @@
false
false
false
+ 0
561
@@ -4494,6 +5055,7 @@
false
false
false
+ 0
562
@@ -4502,6 +5064,7 @@
false
false
false
+ 0
563
@@ -4510,6 +5073,7 @@
false
false
false
+ 0
564
@@ -4518,6 +5082,7 @@
false
false
false
+ 0
565
@@ -4526,6 +5091,7 @@
false
false
false
+ 0
566
@@ -4534,6 +5100,7 @@
false
false
false
+ 0
567
@@ -4542,6 +5109,7 @@
false
false
false
+ 0
568
@@ -4550,6 +5118,7 @@
false
false
false
+ 0
569
@@ -4558,6 +5127,7 @@
false
false
false
+ 0
570
@@ -4566,6 +5136,7 @@
false
false
false
+ 0
571
@@ -4574,6 +5145,7 @@
false
false
false
+ 0
572
@@ -4582,6 +5154,7 @@
false
false
false
+ 0
573
@@ -4590,6 +5163,7 @@
false
false
false
+ 0
574
@@ -4598,6 +5172,7 @@
false
false
false
+ 0
575
@@ -4606,6 +5181,7 @@
false
false
false
+ 0
576
@@ -4614,6 +5190,7 @@
false
false
false
+ 0
577
@@ -4622,6 +5199,7 @@
false
false
false
+ 0
578
@@ -4630,6 +5208,7 @@
false
false
false
+ 0
579
@@ -4638,6 +5217,7 @@
false
false
false
+ 0
580
@@ -4646,6 +5226,7 @@
false
false
false
+ 0
581
@@ -4654,6 +5235,7 @@
false
false
false
+ 0
582
@@ -4662,6 +5244,7 @@
false
false
false
+ 0
583
@@ -4670,6 +5253,7 @@
false
false
false
+ 0
584
@@ -4678,6 +5262,7 @@
false
false
false
+ 0
585
@@ -4686,6 +5271,7 @@
false
false
false
+ 0
586
@@ -4694,6 +5280,7 @@
false
false
false
+ 0
587
@@ -4702,6 +5289,7 @@
false
false
false
+ 0
588
@@ -4710,6 +5298,7 @@
false
false
false
+ 0
589
@@ -4718,6 +5307,7 @@
false
false
false
+ 0
590
@@ -4726,6 +5316,7 @@
false
false
false
+ 0
591
@@ -4734,6 +5325,7 @@
false
false
false
+ 0
592
@@ -4742,6 +5334,7 @@
false
false
false
+ 0
593
@@ -4750,6 +5343,7 @@
false
false
false
+ 0
594
@@ -4758,6 +5352,7 @@
false
false
false
+ 0
595
@@ -4766,6 +5361,7 @@
false
false
false
+ 0
596
@@ -4774,6 +5370,7 @@
false
false
false
+ 0
597
@@ -4782,6 +5379,7 @@
false
false
false
+ 0
598
@@ -4790,6 +5388,7 @@
false
false
false
+ 0
599
@@ -4798,6 +5397,7 @@
false
false
false
+ 0
600
@@ -4806,6 +5406,7 @@
false
false
false
+ 0
601
@@ -4814,6 +5415,7 @@
false
false
false
+ 0
602
@@ -4822,6 +5424,7 @@
false
false
false
+ 0
603
@@ -4830,6 +5433,7 @@
false
false
false
+ 0
604
@@ -4838,6 +5442,7 @@
false
false
false
+ 0
605
@@ -4846,6 +5451,7 @@
false
false
false
+ 0
606
@@ -4854,6 +5460,7 @@
false
false
false
+ 0
607
@@ -4862,6 +5469,7 @@
false
false
false
+ 0
608
@@ -4870,6 +5478,7 @@
false
false
false
+ 0
609
@@ -4878,6 +5487,7 @@
false
false
false
+ 0
610
@@ -4886,6 +5496,7 @@
false
false
false
+ 0
611
@@ -4894,6 +5505,7 @@
false
false
false
+ 0
612
@@ -4902,6 +5514,7 @@
false
false
false
+ 0
613
@@ -4910,6 +5523,7 @@
false
false
false
+ 0
614
@@ -4918,6 +5532,7 @@
false
false
false
+ 0
615
@@ -4926,6 +5541,7 @@
false
false
false
+ 0
616
@@ -4934,6 +5550,7 @@
false
false
false
+ 0
617
@@ -4942,6 +5559,7 @@
false
false
false
+ 0
618
@@ -4950,6 +5568,7 @@
false
false
false
+ 0
619
@@ -4958,6 +5577,7 @@
false
false
false
+ 0
620
@@ -4966,6 +5586,7 @@
false
false
false
+ 0
621
@@ -4974,6 +5595,7 @@
false
false
false
+ 0
622
@@ -4982,6 +5604,7 @@
false
false
false
+ 0
623
@@ -4990,6 +5613,7 @@
false
false
false
+ 0
624
@@ -4998,6 +5622,7 @@
false
false
false
+ 0
625
@@ -5006,6 +5631,7 @@
false
false
false
+ 0
626
@@ -5014,6 +5640,7 @@
false
false
false
+ 0
627
@@ -5022,6 +5649,7 @@
false
false
false
+ 0
628
@@ -5030,6 +5658,7 @@
false
false
false
+ 0
629
@@ -5038,6 +5667,7 @@
false
false
false
+ 0
630
@@ -5046,6 +5676,7 @@
false
false
false
+ 0
631
@@ -5054,6 +5685,7 @@
false
false
false
+ 0
632
@@ -5062,6 +5694,7 @@
false
false
false
+ 0
633
@@ -5070,6 +5703,7 @@
false
false
false
+ 0
634
@@ -5078,6 +5712,7 @@
false
false
false
+ 0
635
@@ -5086,6 +5721,7 @@
false
false
false
+ 0
636
@@ -5094,6 +5730,7 @@
false
false
false
+ 0
637
@@ -5102,6 +5739,7 @@
false
false
false
+ 0
638
@@ -5110,6 +5748,7 @@
false
false
false
+ 0
639
@@ -5118,6 +5757,7 @@
false
false
false
+ 0
640
@@ -5126,6 +5766,7 @@
false
false
false
+ 0
641
@@ -5134,6 +5775,7 @@
false
false
false
+ 0
642
@@ -5142,6 +5784,7 @@
false
false
false
+ 0
643
@@ -5150,6 +5793,7 @@
false
false
false
+ 0
644
@@ -5158,6 +5802,7 @@
false
false
false
+ 0
645
@@ -5166,6 +5811,7 @@
false
false
false
+ 0
646
@@ -5174,6 +5820,7 @@
false
false
false
+ 0
647
@@ -5182,6 +5829,7 @@
false
false
false
+ 0
648
@@ -5190,6 +5838,7 @@
false
false
false
+ 0
649
@@ -5198,6 +5847,7 @@
false
false
false
+ 0
650
@@ -5206,6 +5856,7 @@
false
false
false
+ 0
651
@@ -5214,6 +5865,7 @@
false
false
false
+ 0
652
@@ -5222,6 +5874,7 @@
false
false
false
+ 0
653
@@ -5230,6 +5883,7 @@
false
false
false
+ 0
654
@@ -5238,6 +5892,7 @@
false
false
false
+ 0
655
@@ -5246,6 +5901,7 @@
false
false
false
+ 0
656
@@ -5254,6 +5910,7 @@
false
false
false
+ 0
657
@@ -5262,6 +5919,7 @@
false
false
false
+ 0
658
@@ -5270,6 +5928,7 @@
false
false
false
+ 0
659
@@ -5278,6 +5937,7 @@
false
false
false
+ 0
660
@@ -5286,6 +5946,7 @@
false
false
false
+ 0
661
@@ -5294,6 +5955,7 @@
false
false
false
+ 0
662
@@ -5302,6 +5964,7 @@
false
false
false
+ 0
663
@@ -5310,6 +5973,7 @@
false
false
false
+ 0
664
@@ -5318,6 +5982,7 @@
false
false
false
+ 0
665
@@ -5326,6 +5991,7 @@
false
false
false
+ 0
666
@@ -5334,6 +6000,7 @@
false
false
false
+ 0
667
@@ -5342,6 +6009,7 @@
false
false
false
+ 0
668
@@ -5350,6 +6018,7 @@
false
false
false
+ 0
669
@@ -5358,6 +6027,7 @@
false
false
false
+ 0
670
@@ -5366,6 +6036,7 @@
false
false
false
+ 0
671
@@ -5374,6 +6045,7 @@
false
false
false
+ 0
672
@@ -5382,6 +6054,7 @@
false
false
false
+ 0
673
@@ -5390,6 +6063,7 @@
false
false
false
+ 0
674
@@ -5398,6 +6072,7 @@
false
false
false
+ 0
675
@@ -5406,6 +6081,7 @@
false
false
false
+ 0
676
@@ -5414,6 +6090,7 @@
false
false
false
+ 0
677
@@ -5422,6 +6099,7 @@
false
false
false
+ 0
678
@@ -5430,6 +6108,7 @@
false
false
false
+ 0
679
@@ -5438,6 +6117,7 @@
false
false
false
+ 0
680
@@ -5446,6 +6126,7 @@
false
false
false
+ 0
681
@@ -5454,6 +6135,7 @@
false
false
false
+ 0
682
@@ -5462,6 +6144,7 @@
false
false
false
+ 0
683
@@ -5470,6 +6153,7 @@
false
false
false
+ 0
684
@@ -5478,6 +6162,7 @@
false
false
false
+ 0
685
@@ -5486,6 +6171,7 @@
false
false
false
+ 0
686
@@ -5494,6 +6180,7 @@
false
false
false
+ 0
687
@@ -5502,6 +6189,7 @@
false
false
false
+ 0
688
@@ -5510,6 +6198,7 @@
false
false
false
+ 0
689
@@ -5518,6 +6207,7 @@
false
false
false
+ 0
690
@@ -5526,6 +6216,7 @@
false
false
false
+ 0
691
@@ -5534,6 +6225,7 @@
false
false
false
+ 0
692
@@ -5542,6 +6234,7 @@
false
false
false
+ 0
693
@@ -5550,6 +6243,7 @@
false
false
false
+ 0
694
@@ -5558,6 +6252,7 @@
false
false
false
+ 0
695
@@ -5566,6 +6261,7 @@
false
false
false
+ 0
696
@@ -5574,6 +6270,7 @@
false
false
false
+ 0
697
@@ -5582,6 +6279,7 @@
false
false
false
+ 0
698
@@ -5590,6 +6288,7 @@
false
false
false
+ 0
699
@@ -5598,6 +6297,7 @@
false
false
false
+ 0
700
@@ -5606,6 +6306,7 @@
false
false
false
+ 0
701
@@ -5614,6 +6315,7 @@
false
false
false
+ 0
702
@@ -5622,6 +6324,7 @@
false
false
false
+ 0
703
@@ -5630,6 +6333,7 @@
false
false
false
+ 0
704
@@ -5638,6 +6342,7 @@
false
false
false
+ 0
705
@@ -5646,6 +6351,7 @@
false
false
false
+ 0
706
@@ -5654,6 +6360,7 @@
false
false
false
+ 0
707
@@ -5662,6 +6369,7 @@
false
false
false
+ 0
708
@@ -5670,6 +6378,7 @@
false
false
false
+ 0
709
@@ -5678,6 +6387,7 @@
false
false
false
+ 0
710
@@ -5686,6 +6396,7 @@
false
false
false
+ 0
711
@@ -5694,6 +6405,7 @@
false
false
false
+ 0
712
@@ -5702,6 +6414,7 @@
false
false
false
+ 0
713
@@ -5710,6 +6423,7 @@
false
false
false
+ 0
714
@@ -5718,6 +6432,7 @@
false
false
false
+ 0
715
@@ -5726,6 +6441,7 @@
false
false
false
+ 0
716
@@ -5734,6 +6450,7 @@
false
false
false
+ 0
717
@@ -5742,6 +6459,7 @@
false
false
false
+ 0
718
@@ -5750,6 +6468,7 @@
false
false
false
+ 0
719
@@ -5758,6 +6477,7 @@
false
false
false
+ 0
720
@@ -5766,6 +6486,7 @@
false
false
false
+ 0
721
@@ -5774,6 +6495,7 @@
false
false
false
+ 0
722
@@ -5782,6 +6504,7 @@
false
false
false
+ 0
723
@@ -5790,6 +6513,7 @@
false
false
false
+ 0
724
@@ -5798,6 +6522,7 @@
false
false
false
+ 0
725
@@ -5806,6 +6531,7 @@
false
false
false
+ 0
726
@@ -5814,6 +6540,7 @@
false
false
false
+ 0
727
@@ -5822,6 +6549,7 @@
false
false
false
+ 0
728
@@ -5830,6 +6558,7 @@
false
false
false
+ 0
729
@@ -5838,6 +6567,7 @@
false
false
false
+ 0
730
@@ -5846,6 +6576,7 @@
false
false
false
+ 0
731
@@ -5854,6 +6585,7 @@
false
false
false
+ 0
732
@@ -5862,6 +6594,7 @@
false
false
false
+ 0
733
@@ -5870,6 +6603,7 @@
false
false
false
+ 0
734
@@ -5878,6 +6612,7 @@
false
false
false
+ 0
735
@@ -5886,6 +6621,7 @@
false
false
false
+ 0
736
@@ -5894,6 +6630,7 @@
false
false
false
+ 0
737
@@ -5902,6 +6639,7 @@
false
false
false
+ 0
738
@@ -5910,6 +6648,7 @@
false
false
false
+ 0
739
@@ -5918,6 +6657,7 @@
false
false
false
+ 0
740
@@ -5926,6 +6666,7 @@
false
false
false
+ 0
741
@@ -5934,6 +6675,7 @@
false
false
false
+ 0
742
@@ -5942,6 +6684,7 @@
false
false
false
+ 0
743
@@ -5950,6 +6693,7 @@
false
false
false
+ 0
744
@@ -5958,6 +6702,7 @@
false
false
false
+ 0
745
@@ -5966,6 +6711,7 @@
false
false
false
+ 0
746
@@ -5974,6 +6720,7 @@
false
false
false
+ 0
747
@@ -5982,6 +6729,7 @@
false
false
false
+ 0
748
@@ -5990,6 +6738,7 @@
false
false
false
+ 0
749
@@ -5998,6 +6747,7 @@
false
false
false
+ 0
750
@@ -6006,6 +6756,7 @@
false
false
false
+ 0
751
@@ -6014,6 +6765,7 @@
false
false
false
+ 0
752
@@ -6022,6 +6774,7 @@
false
false
false
+ 0
753
@@ -6030,6 +6783,7 @@
false
false
false
+ 0
754
@@ -6038,6 +6792,7 @@
false
false
false
+ 0
755
@@ -6046,6 +6801,7 @@
false
false
false
+ 0
756
@@ -6054,6 +6810,7 @@
false
false
false
+ 0
757
@@ -6062,6 +6819,7 @@
false
false
false
+ 0
758
@@ -6070,6 +6828,7 @@
false
false
false
+ 0
759
@@ -6078,6 +6837,7 @@
false
false
false
+ 0
760
@@ -6086,6 +6846,7 @@
false
false
false
+ 0
761
@@ -6094,6 +6855,7 @@
false
false
false
+ 0
762
@@ -6102,6 +6864,7 @@
false
false
false
+ 0
763
@@ -6110,6 +6873,7 @@
false
false
false
+ 0
764
@@ -6118,6 +6882,7 @@
false
false
false
+ 0
765
@@ -6126,6 +6891,7 @@
false
false
false
+ 0
766
@@ -6134,6 +6900,7 @@
false
false
false
+ 0
767
@@ -6142,6 +6909,7 @@
false
false
false
+ 0
768
@@ -6150,6 +6918,7 @@
false
false
false
+ 0
769
@@ -6158,6 +6927,7 @@
false
false
false
+ 0
770
@@ -6166,6 +6936,7 @@
false
false
false
+ 0
771
@@ -6174,6 +6945,7 @@
false
false
false
+ 0
772
@@ -6182,6 +6954,7 @@
false
false
false
+ 0
773
@@ -6190,6 +6963,7 @@
false
false
false
+ 0
774
@@ -6198,6 +6972,7 @@
false
false
false
+ 0
775
@@ -6206,6 +6981,7 @@
false
false
false
+ 0
776
@@ -6214,6 +6990,7 @@
false
false
false
+ 0
777
@@ -6222,6 +6999,7 @@
false
false
false
+ 0
778
@@ -6230,6 +7008,7 @@
false
false
false
+ 0
779
@@ -6238,6 +7017,7 @@
false
false
false
+ 0
780
@@ -6246,6 +7026,7 @@
false
false
false
+ 0
781
@@ -6254,6 +7035,7 @@
false
false
false
+ 0
782
@@ -6262,6 +7044,7 @@
false
false
false
+ 0
783
@@ -6270,6 +7053,7 @@
false
false
false
+ 0
784
@@ -6278,6 +7062,7 @@
false
false
false
+ 0
785
@@ -6286,6 +7071,7 @@
false
false
false
+ 0
786
@@ -6294,6 +7080,7 @@
false
false
false
+ 0
787
@@ -6302,6 +7089,7 @@
false
false
false
+ 0
788
@@ -6310,6 +7098,7 @@
false
false
false
+ 0
789
@@ -6318,6 +7107,7 @@
false
false
false
+ 0
790
@@ -6326,6 +7116,7 @@
false
false
false
+ 0
791
@@ -6334,6 +7125,7 @@
false
false
false
+ 0
792
@@ -6342,6 +7134,7 @@
false
false
false
+ 0
793
@@ -6350,6 +7143,7 @@
false
false
false
+ 0
794
@@ -6358,6 +7152,7 @@
false
false
false
+ 0
795
@@ -6366,6 +7161,7 @@
false
false
false
+ 0
796
@@ -6374,6 +7170,7 @@
false
false
false
+ 0
797
@@ -6382,6 +7179,7 @@
false
false
false
+ 0
798
@@ -6390,6 +7188,7 @@
false
false
false
+ 0
799
@@ -6398,6 +7197,7 @@
false
false
false
+ 0
800
@@ -6406,6 +7206,7 @@
false
false
false
+ 0
801
@@ -6414,6 +7215,7 @@
false
false
false
+ 0
802
@@ -6422,6 +7224,7 @@
false
false
false
+ 0
803
@@ -6430,6 +7233,7 @@
false
false
false
+ 0
804
@@ -6438,6 +7242,7 @@
false
false
false
+ 0
805
@@ -6446,6 +7251,7 @@
false
false
false
+ 0
806
@@ -6454,6 +7260,7 @@
false
false
false
+ 0
807
@@ -6462,6 +7269,7 @@
false
false
false
+ 0
808
@@ -6470,6 +7278,7 @@
false
false
false
+ 0
809
@@ -6478,6 +7287,7 @@
false
false
false
+ 0
810
@@ -6486,6 +7296,7 @@
false
false
false
+ 0
811
@@ -6494,6 +7305,7 @@
false
false
false
+ 0
812
@@ -6502,6 +7314,7 @@
false
false
false
+ 0
813
@@ -6510,6 +7323,7 @@
false
false
false
+ 0
814
@@ -6518,6 +7332,7 @@
false
false
false
+ 0
815
@@ -6526,6 +7341,7 @@
false
false
false
+ 0
816
@@ -6534,6 +7350,7 @@
false
false
false
+ 0
817
@@ -6542,6 +7359,7 @@
false
false
false
+ 0
818
@@ -6550,6 +7368,7 @@
false
false
false
+ 0
819
@@ -6558,6 +7377,7 @@
false
false
false
+ 0
820
@@ -6566,6 +7386,7 @@
false
false
false
+ 0
821
@@ -6574,6 +7395,7 @@
false
false
false
+ 0
822
@@ -6582,6 +7404,7 @@
false
false
false
+ 0
823
@@ -6590,6 +7413,7 @@
false
false
false
+ 0
824
@@ -6598,6 +7422,7 @@
false
false
false
+ 0
825
@@ -6606,6 +7431,7 @@
false
false
false
+ 0
826
@@ -6614,6 +7440,7 @@
false
false
false
+ 0
827
@@ -6622,6 +7449,7 @@
false
false
false
+ 0
828
@@ -6630,6 +7458,7 @@
false
false
false
+ 0
829
@@ -6638,6 +7467,7 @@
false
false
false
+ 0
830
@@ -6646,6 +7476,7 @@
false
false
false
+ 0
831
@@ -6654,6 +7485,7 @@
false
false
false
+ 0
832
@@ -6662,6 +7494,7 @@
false
false
false
+ 0
833
@@ -6670,6 +7503,7 @@
false
false
false
+ 0
834
@@ -6678,6 +7512,7 @@
false
false
false
+ 0
835
@@ -6686,6 +7521,7 @@
false
false
false
+ 0
836
@@ -6694,6 +7530,7 @@
false
false
false
+ 0
837
@@ -6702,6 +7539,7 @@
false
false
false
+ 0
838
@@ -6710,6 +7548,7 @@
false
false
false
+ 0
839
@@ -6718,6 +7557,7 @@
false
false
false
+ 0
840
@@ -6726,6 +7566,7 @@
false
false
false
+ 0
841
@@ -6734,6 +7575,7 @@
false
false
false
+ 0
842
@@ -6742,6 +7584,7 @@
false
false
false
+ 0
843
@@ -6750,6 +7593,7 @@
false
false
false
+ 0
844
@@ -6758,6 +7602,7 @@
false
false
false
+ 0
845
@@ -6766,6 +7611,7 @@
false
false
false
+ 0
846
@@ -6774,6 +7620,7 @@
false
false
false
+ 0
847
@@ -6782,6 +7629,7 @@
false
false
false
+ 0
848
@@ -6790,6 +7638,7 @@
false
false
false
+ 0
849
@@ -6798,6 +7647,7 @@
false
false
false
+ 0
850
@@ -6806,6 +7656,7 @@
false
false
false
+ 0
851
@@ -6814,6 +7665,7 @@
false
false
false
+ 0
852
@@ -6822,6 +7674,7 @@
false
false
false
+ 0
853
@@ -6830,6 +7683,7 @@
false
false
false
+ 0
854
@@ -6838,6 +7692,7 @@
false
false
false
+ 0
855
@@ -6846,6 +7701,7 @@
false
false
false
+ 0
856
@@ -6854,6 +7710,7 @@
false
false
false
+ 0
857
@@ -6862,6 +7719,7 @@
false
false
false
+ 0
858
@@ -6870,6 +7728,7 @@
false
false
false
+ 0
859
@@ -6878,6 +7737,7 @@
false
false
false
+ 0
860
@@ -6886,6 +7746,7 @@
false
false
false
+ 0
861
@@ -6894,6 +7755,7 @@
false
false
false
+ 0
862
@@ -6902,6 +7764,7 @@
false
false
false
+ 0
863
@@ -6910,6 +7773,7 @@
false
false
false
+ 0
864
@@ -6918,6 +7782,7 @@
false
false
false
+ 0
865
@@ -6926,6 +7791,7 @@
false
false
false
+ 0
866
@@ -6934,6 +7800,7 @@
false
false
false
+ 0
867
@@ -6942,6 +7809,7 @@
false
false
false
+ 0
868
@@ -6950,6 +7818,7 @@
false
false
false
+ 0
869
@@ -6958,6 +7827,7 @@
false
false
false
+ 0
870
@@ -6966,6 +7836,7 @@
false
false
false
+ 0
871
@@ -6974,6 +7845,7 @@
false
false
false
+ 0
872
@@ -6982,6 +7854,7 @@
false
false
false
+ 0
873
@@ -6990,6 +7863,7 @@
false
false
false
+ 0
874
@@ -6998,6 +7872,7 @@
false
false
false
+ 0
875
@@ -7006,6 +7881,7 @@
false
false
false
+ 0
876
@@ -7014,6 +7890,7 @@
false
false
false
+ 0
877
@@ -7022,6 +7899,7 @@
false
false
false
+ 0
878
@@ -7030,6 +7908,7 @@
false
false
false
+ 0
879
@@ -7038,6 +7917,7 @@
false
false
false
+ 0
880
@@ -7046,6 +7926,7 @@
false
false
false
+ 0
881
@@ -7054,6 +7935,7 @@
false
false
false
+ 0
882
@@ -7062,6 +7944,7 @@
false
false
false
+ 0
883
@@ -7070,6 +7953,7 @@
false
false
false
+ 0
884
@@ -7078,6 +7962,7 @@
false
false
false
+ 0
885
@@ -7086,6 +7971,7 @@
false
false
false
+ 0
886
@@ -7094,6 +7980,7 @@
false
false
false
+ 0
887
@@ -7102,6 +7989,7 @@
false
false
false
+ 0
888
@@ -7110,6 +7998,7 @@
false
false
false
+ 0
889
@@ -7118,6 +8007,7 @@
false
false
false
+ 0
890
@@ -7126,6 +8016,7 @@
false
false
false
+ 0
891
@@ -7134,6 +8025,7 @@
false
false
false
+ 0
892
@@ -7142,6 +8034,7 @@
false
false
false
+ 0
893
@@ -7150,6 +8043,7 @@
false
false
false
+ 0
894
@@ -7158,6 +8052,7 @@
false
false
false
+ 0
895
@@ -7166,6 +8061,7 @@
false
false
false
+ 0
896
@@ -7174,6 +8070,7 @@
false
false
false
+ 0
897
@@ -7182,6 +8079,7 @@
false
false
false
+ 0
898
@@ -7190,6 +8088,7 @@
false
false
false
+ 0
899
@@ -7198,6 +8097,7 @@
false
false
false
+ 0
900
@@ -7206,6 +8106,7 @@
false
false
false
+ 0
901
@@ -7214,6 +8115,7 @@
false
false
false
+ 0
902
@@ -7222,6 +8124,7 @@
false
false
false
+ 0
903
@@ -7230,6 +8133,7 @@
false
false
false
+ 0
904
@@ -7238,6 +8142,7 @@
false
false
false
+ 0
905
@@ -7246,6 +8151,7 @@
false
false
false
+ 0
906
@@ -7254,6 +8160,7 @@
false
false
false
+ 0
907
@@ -7262,6 +8169,7 @@
false
false
false
+ 0
908
@@ -7270,6 +8178,7 @@
false
false
false
+ 0
909
@@ -7278,6 +8187,7 @@
false
false
false
+ 0
910
@@ -7286,6 +8196,7 @@
false
false
false
+ 0
911
@@ -7294,6 +8205,7 @@
false
false
false
+ 0
912
@@ -7302,6 +8214,7 @@
false
false
false
+ 0
913
@@ -7310,6 +8223,7 @@
false
false
false
+ 0
914
@@ -7318,6 +8232,7 @@
false
false
false
+ 0
915
@@ -7326,6 +8241,7 @@
false
false
false
+ 0
916
@@ -7334,6 +8250,7 @@
false
false
false
+ 0
917
@@ -7342,6 +8259,7 @@
false
false
false
+ 0
918
@@ -7350,6 +8268,7 @@
false
false
false
+ 0
919
@@ -7358,6 +8277,7 @@
false
false
false
+ 0
920
@@ -7366,6 +8286,7 @@
false
false
false
+ 0
921
@@ -7374,6 +8295,7 @@
false
false
false
+ 0
922
@@ -7382,6 +8304,7 @@
false
false
false
+ 0
923
@@ -7390,6 +8313,7 @@
false
false
false
+ 0
924
@@ -7398,6 +8322,7 @@
false
false
false
+ 0
925
@@ -7406,6 +8331,7 @@
false
false
false
+ 0
926
@@ -7414,6 +8340,7 @@
false
false
false
+ 0
927
@@ -7422,6 +8349,7 @@
false
false
false
+ 0
928
@@ -7430,6 +8358,7 @@
false
false
false
+ 0
929
@@ -7438,6 +8367,7 @@
false
false
false
+ 0
930
@@ -7446,6 +8376,7 @@
false
false
false
+ 0
931
@@ -7454,6 +8385,7 @@
false
false
false
+ 0
932
@@ -7462,6 +8394,7 @@
false
false
false
+ 0
933
@@ -7470,6 +8403,7 @@
false
false
false
+ 0
934
@@ -7478,6 +8412,7 @@
false
false
false
+ 0
935
@@ -7486,6 +8421,7 @@
false
false
false
+ 0
936
@@ -7494,6 +8430,7 @@
false
false
false
+ 0
937
@@ -7502,6 +8439,7 @@
false
false
false
+ 0
938
@@ -7510,6 +8448,7 @@
false
false
false
+ 0
939
@@ -7518,6 +8457,7 @@
false
false
false
+ 0
940
@@ -7526,6 +8466,7 @@
false
false
false
+ 0
941
@@ -7534,6 +8475,7 @@
false
false
false
+ 0
942
@@ -7542,6 +8484,7 @@
false
false
false
+ 0
943
@@ -7550,6 +8493,7 @@
false
false
false
+ 0
944
@@ -7558,6 +8502,7 @@
false
false
false
+ 0
945
@@ -7566,6 +8511,7 @@
false
false
false
+ 0
946
@@ -7574,6 +8520,7 @@
false
false
false
+ 0
947
@@ -7582,6 +8529,7 @@
false
false
false
+ 0
948
@@ -7590,6 +8538,7 @@
false
false
false
+ 0
949
@@ -7598,6 +8547,7 @@
false
false
false
+ 0
950
@@ -7606,6 +8556,7 @@
false
false
false
+ 0
951
@@ -7614,6 +8565,7 @@
false
false
false
+ 0
952
@@ -7622,6 +8574,7 @@
false
false
false
+ 0
953
@@ -7630,6 +8583,7 @@
false
false
false
+ 0
954
@@ -7638,6 +8592,7 @@
false
false
false
+ 0
955
@@ -7646,6 +8601,7 @@
false
false
false
+ 0
956
@@ -7654,6 +8610,7 @@
false
false
false
+ 0
957
@@ -7662,6 +8619,7 @@
false
false
false
+ 0
958
@@ -7670,6 +8628,7 @@
false
false
false
+ 0
959
@@ -7678,6 +8637,7 @@
false
false
false
+ 0
960
@@ -7686,6 +8646,7 @@
false
false
false
+ 0
961
@@ -7694,6 +8655,7 @@
false
false
false
+ 0
962
@@ -7702,6 +8664,7 @@
false
false
false
+ 0
963
@@ -7710,6 +8673,7 @@
false
false
false
+ 0
964
@@ -7718,6 +8682,7 @@
false
false
false
+ 0
965
@@ -7726,6 +8691,7 @@
false
false
false
+ 0
966
@@ -7734,6 +8700,7 @@
false
false
false
+ 0
967
@@ -7742,6 +8709,7 @@
false
false
false
+ 0
968
@@ -7750,6 +8718,7 @@
false
false
false
+ 0
969
@@ -7758,6 +8727,7 @@
false
false
false
+ 0
970
@@ -7766,6 +8736,7 @@
false
false
false
+ 0
971
@@ -7774,6 +8745,7 @@
false
false
false
+ 0
972
@@ -7782,6 +8754,7 @@
false
false
false
+ 0
973
@@ -7790,6 +8763,7 @@
false
false
false
+ 0
974
@@ -7798,6 +8772,7 @@
false
false
false
+ 0
975
@@ -7806,6 +8781,7 @@
false
false
false
+ 0
976
@@ -7814,6 +8790,7 @@
false
false
false
+ 0
977
@@ -7822,6 +8799,7 @@
false
false
false
+ 0
978
@@ -7830,6 +8808,7 @@
false
false
false
+ 0
979
@@ -7838,6 +8817,7 @@
false
false
false
+ 0
980
@@ -7846,6 +8826,7 @@
false
false
false
+ 0
981
@@ -7854,6 +8835,7 @@
false
false
false
+ 0
982
@@ -7862,6 +8844,7 @@
false
false
false
+ 0
983
@@ -7870,6 +8853,7 @@
false
false
false
+ 0
984
@@ -7878,6 +8862,7 @@
false
false
false
+ 0
985
@@ -7886,6 +8871,7 @@
false
false
false
+ 0
986
@@ -7894,6 +8880,7 @@
false
false
false
+ 0
987
@@ -7902,6 +8889,7 @@
false
false
false
+ 0
988
@@ -7910,6 +8898,7 @@
false
false
false
+ 0
989
@@ -7918,6 +8907,7 @@
false
false
false
+ 0
990
@@ -7926,6 +8916,7 @@
false
false
false
+ 0
991
@@ -7934,6 +8925,7 @@
false
false
false
+ 0
992
@@ -7942,6 +8934,7 @@
false
false
false
+ 0
993
@@ -7950,6 +8943,7 @@
false
false
false
+ 0
994
@@ -7958,6 +8952,7 @@
false
false
false
+ 0
995
@@ -7966,6 +8961,7 @@
false
false
false
+ 0
996
@@ -7974,6 +8970,7 @@
false
false
false
+ 0
997
@@ -7982,6 +8979,7 @@
false
false
false
+ 0
998
@@ -7990,6 +8988,7 @@
false
false
false
+ 0
999
@@ -7998,6 +8997,7 @@
false
false
false
+ 0
1000
@@ -8006,6 +9006,7 @@
false
false
false
+ 0
1001
@@ -8014,6 +9015,7 @@
false
false
false
+ 0
1002
@@ -8022,6 +9024,7 @@
false
false
false
+ 0
1003
@@ -8030,6 +9033,7 @@
false
false
false
+ 0
1004
@@ -8038,6 +9042,7 @@
false
false
false
+ 0
1005
@@ -8046,6 +9051,7 @@
false
false
false
+ 0
1006
@@ -8054,6 +9060,7 @@
false
false
false
+ 0
1007
@@ -8062,6 +9069,7 @@
false
false
false
+ 0
1008
@@ -8070,6 +9078,7 @@
false
false
false
+ 0
1009
@@ -8078,6 +9087,7 @@
false
false
false
+ 0
1010
@@ -8086,6 +9096,7 @@
false
false
false
+ 0
1011
@@ -8094,6 +9105,7 @@
false
false
false
+ 0
1012
@@ -8102,6 +9114,7 @@
false
false
false
+ 0
1013
@@ -8110,6 +9123,7 @@
false
false
false
+ 0
1014
@@ -8118,6 +9132,7 @@
false
false
false
+ 0
1015
@@ -8126,6 +9141,7 @@
false
false
false
+ 0
1016
@@ -8134,6 +9150,7 @@
false
false
false
+ 0
1017
@@ -8142,6 +9159,7 @@
false
false
false
+ 0
1018
@@ -8150,6 +9168,7 @@
false
false
false
+ 0
1019
@@ -8158,6 +9177,7 @@
false
false
false
+ 0
1020
@@ -8166,6 +9186,7 @@
false
false
false
+ 0
1021
@@ -8174,6 +9195,7 @@
false
false
false
+ 0
1022
@@ -8182,6 +9204,7 @@
false
false
false
+ 0
1023
@@ -8190,6 +9213,7 @@
false
false
false
+ 0
1024
@@ -8198,6 +9222,7 @@
false
false
false
+ 0
1025
@@ -8206,6 +9231,7 @@
false
false
false
+ 0
1026
@@ -8214,6 +9240,7 @@
false
false
false
+ 0
1027
@@ -8222,6 +9249,7 @@
false
false
false
+ 0
1028
@@ -8230,6 +9258,7 @@
false
false
false
+ 0
1029
@@ -8238,6 +9267,7 @@
false
false
false
+ 0
1030
@@ -8246,6 +9276,7 @@
false
false
false
+ 0
1031
@@ -8254,6 +9285,7 @@
false
false
false
+ 0
1032
@@ -8262,6 +9294,7 @@
false
false
false
+ 0
1033
@@ -8270,6 +9303,7 @@
false
false
false
+ 0
1034
@@ -8278,6 +9312,7 @@
false
false
false
+ 0
1035
@@ -8286,6 +9321,7 @@
false
false
false
+ 0
1036
@@ -8294,6 +9330,7 @@
false
false
false
+ 0
1037
@@ -8302,6 +9339,7 @@
false
false
false
+ 0
1038
@@ -8310,6 +9348,7 @@
false
false
false
+ 0
1039
@@ -8318,6 +9357,7 @@
false
false
false
+ 0
1040
@@ -8326,6 +9366,7 @@
false
false
false
+ 0
1041
@@ -8334,6 +9375,7 @@
false
false
false
+ 0
1042
@@ -8342,6 +9384,7 @@
false
false
false
+ 0
1043
@@ -8350,6 +9393,7 @@
false
false
false
+ 0
1044
@@ -8358,6 +9402,7 @@
false
false
false
+ 0
1045
@@ -8366,6 +9411,7 @@
false
false
false
+ 0
1046
@@ -8374,6 +9420,7 @@
false
false
false
+ 0
1047
@@ -8382,6 +9429,7 @@
false
false
false
+ 0
1048
@@ -8390,6 +9438,7 @@
false
false
false
+ 0
1049
@@ -8398,6 +9447,7 @@
false
false
false
+ 0
1050
@@ -8406,6 +9456,7 @@
false
false
false
+ 0
1051
@@ -8414,6 +9465,7 @@
false
false
false
+ 0
1052
@@ -8422,6 +9474,7 @@
false
false
false
+ 0
1053
@@ -8430,6 +9483,7 @@
false
false
false
+ 0
1054
@@ -8438,6 +9492,7 @@
false
false
false
+ 0
1055
@@ -8446,6 +9501,7 @@
false
false
false
+ 0
1056
@@ -8454,6 +9510,7 @@
false
false
false
+ 0
1057
@@ -8462,6 +9519,7 @@
false
false
false
+ 0
1058
@@ -8470,6 +9528,7 @@
false
false
false
+ 0
1059
@@ -8478,6 +9537,7 @@
false
false
false
+ 0
1060
@@ -8486,6 +9546,7 @@
false
false
false
+ 0
1061
@@ -8494,6 +9555,7 @@
false
false
false
+ 0
1062
@@ -8502,6 +9564,7 @@
false
false
false
+ 0
1063
@@ -8510,6 +9573,7 @@
false
false
false
+ 0
1064
@@ -8518,6 +9582,7 @@
false
false
false
+ 0
1065
@@ -8526,6 +9591,7 @@
false
false
false
+ 0
1066
@@ -8534,6 +9600,7 @@
false
false
false
+ 0
1067
@@ -8542,6 +9609,7 @@
false
false
false
+ 0
1068
@@ -8550,6 +9618,7 @@
false
false
false
+ 0
1069
@@ -8558,6 +9627,7 @@
false
false
false
+ 0
1070
@@ -8566,6 +9636,7 @@
false
false
false
+ 0
1071
@@ -8574,6 +9645,7 @@
false
false
false
+ 0
1072
@@ -8582,6 +9654,7 @@
false
false
false
+ 0
1073
@@ -8590,6 +9663,7 @@
false
false
false
+ 0
1074
@@ -8598,6 +9672,7 @@
false
false
false
+ 0
1075
@@ -8606,6 +9681,7 @@
false
false
false
+ 0
1076
@@ -8614,6 +9690,7 @@
false
false
false
+ 0
1077
@@ -8622,6 +9699,7 @@
false
false
false
+ 0
1078
@@ -8630,6 +9708,7 @@
false
false
false
+ 0
1079
@@ -8638,6 +9717,7 @@
false
false
false
+ 0
1080
@@ -8646,6 +9726,7 @@
false
false
false
+ 0
1081
@@ -8654,6 +9735,7 @@
false
false
false
+ 0
1082
@@ -8662,6 +9744,7 @@
false
false
false
+ 0
1083
@@ -8670,6 +9753,7 @@
false
false
false
+ 0
1084
@@ -8678,6 +9762,7 @@
false
false
false
+ 0
1085
@@ -8686,6 +9771,7 @@
false
false
false
+ 0
1086
@@ -8694,6 +9780,7 @@
false
false
false
+ 0
1087
@@ -8702,6 +9789,7 @@
false
false
false
+ 0
1088
@@ -8710,6 +9798,7 @@
false
false
false
+ 0
1089
@@ -8718,6 +9807,7 @@
false
false
false
+ 0
1090
@@ -8726,6 +9816,7 @@
false
false
false
+ 0
1091
@@ -8734,6 +9825,7 @@
false
false
false
+ 0
1092
@@ -8742,6 +9834,7 @@
false
false
false
+ 0
1093
@@ -8750,6 +9843,7 @@
false
false
false
+ 0
1094
@@ -8758,6 +9852,7 @@
false
false
false
+ 0
1095
@@ -8766,6 +9861,7 @@
false
false
false
+ 0
1096
@@ -8774,6 +9870,7 @@
false
false
false
+ 0
1097
@@ -8782,6 +9879,7 @@
false
false
false
+ 0
1098
@@ -8790,6 +9888,7 @@
false
false
false
+ 0
1099
@@ -8798,6 +9897,7 @@
false
false
false
+ 0
1100
@@ -8806,6 +9906,7 @@
false
false
false
+ 0
1101
@@ -8814,6 +9915,7 @@
false
false
false
+ 0
1102
@@ -8822,6 +9924,7 @@
false
false
false
+ 0
1103
@@ -8830,6 +9933,7 @@
false
false
false
+ 0
1104
@@ -8838,6 +9942,7 @@
false
false
false
+ 0
1105
@@ -8846,6 +9951,7 @@
false
false
false
+ 0
1106
@@ -8854,6 +9960,7 @@
false
false
false
+ 0
1107
@@ -8862,6 +9969,7 @@
false
false
false
+ 0
1108
@@ -8870,6 +9978,7 @@
false
false
false
+ 0
1109
@@ -8878,6 +9987,7 @@
false
false
false
+ 0
1110
@@ -8886,6 +9996,7 @@
false
false
false
+ 0
1111
@@ -8894,6 +10005,7 @@
false
false
false
+ 0
1112
@@ -8902,6 +10014,7 @@
false
false
false
+ 0
1113
@@ -8910,6 +10023,7 @@
false
false
false
+ 0
1114
@@ -8918,6 +10032,7 @@
false
false
false
+ 0
1115
@@ -8926,6 +10041,7 @@
false
false
false
+ 0
1116
@@ -8934,6 +10050,7 @@
false
false
false
+ 0
1117
@@ -8942,6 +10059,7 @@
false
false
false
+ 0
1118
@@ -8950,6 +10068,7 @@
false
false
false
+ 0
1119
@@ -8958,6 +10077,7 @@
false
false
false
+ 0
1120
@@ -8966,6 +10086,7 @@
false
false
false
+ 0
1121
@@ -8974,6 +10095,7 @@
false
false
false
+ 0
1122
@@ -8982,6 +10104,7 @@
false
false
false
+ 0
1123
@@ -8990,6 +10113,7 @@
false
false
false
+ 0
1124
@@ -8998,6 +10122,7 @@
false
false
false
+ 0
1125
@@ -9006,6 +10131,7 @@
false
false
false
+ 0
1126
@@ -9014,6 +10140,7 @@
false
false
false
+ 0
1127
@@ -9022,6 +10149,7 @@
false
false
false
+ 0
1128
@@ -9030,6 +10158,7 @@
false
false
false
+ 0
1129
@@ -9038,6 +10167,7 @@
false
false
false
+ 0
1130
@@ -9046,6 +10176,7 @@
false
false
false
+ 0
1131
@@ -9054,6 +10185,7 @@
false
false
false
+ 0
1132
@@ -9062,6 +10194,7 @@
false
false
false
+ 0
1133
@@ -9070,6 +10203,7 @@
false
false
false
+ 0
1134
@@ -9078,6 +10212,7 @@
false
false
false
+ 0
1135
@@ -9086,6 +10221,7 @@
false
false
false
+ 0
1136
@@ -9094,6 +10230,7 @@
false
false
false
+ 0
1137
@@ -9102,6 +10239,7 @@
false
false
false
+ 0
1138
@@ -9110,6 +10248,7 @@
false
false
false
+ 0
1139
@@ -9118,6 +10257,7 @@
false
false
false
+ 0
1140
@@ -9126,6 +10266,7 @@
false
false
false
+ 0
1141
@@ -9134,6 +10275,7 @@
false
false
false
+ 0
1142
@@ -9142,6 +10284,7 @@
false
false
false
+ 0
1143
@@ -9150,6 +10293,7 @@
false
false
false
+ 0
1144
@@ -9158,6 +10302,7 @@
false
false
false
+ 0
1145
@@ -9166,6 +10311,7 @@
false
false
false
+ 0
1146
@@ -9174,6 +10320,7 @@
false
false
false
+ 0
1147
@@ -9182,6 +10329,7 @@
false
false
false
+ 0
1148
@@ -9190,6 +10338,7 @@
false
false
false
+ 0
1149
@@ -9198,6 +10347,7 @@
false
false
false
+ 0
1150
@@ -9206,6 +10356,7 @@
false
false
false
+ 0
1151
@@ -9214,6 +10365,7 @@
false
false
false
+ 0
1152
@@ -9222,6 +10374,7 @@
false
false
false
+ 0
1153
@@ -9230,6 +10383,7 @@
false
false
false
+ 0
1154
@@ -9238,6 +10392,7 @@
false
false
false
+ 0
1155
@@ -9246,6 +10401,7 @@
false
false
false
+ 0
1156
@@ -9254,6 +10410,7 @@
false
false
false
+ 0
1157
@@ -9262,6 +10419,7 @@
false
false
false
+ 0
1158
@@ -9270,6 +10428,7 @@
false
false
false
+ 0
1159
@@ -9278,6 +10437,7 @@
false
false
false
+ 0
1160
@@ -9286,6 +10446,7 @@
false
false
false
+ 0
1161
@@ -9294,6 +10455,7 @@
false
false
false
+ 0
1162
@@ -9302,6 +10464,7 @@
false
false
false
+ 0
1163
@@ -9310,6 +10473,7 @@
false
false
false
+ 0
1164
@@ -9318,6 +10482,7 @@
false
false
false
+ 0
1165
@@ -9326,6 +10491,7 @@
false
false
false
+ 0
1166
@@ -9334,6 +10500,7 @@
false
false
false
+ 0
1167
@@ -9342,6 +10509,7 @@
false
false
false
+ 0
1168
@@ -9350,6 +10518,7 @@
false
false
false
+ 0
1169
@@ -9358,6 +10527,7 @@
false
false
false
+ 0
1170
@@ -9366,6 +10536,7 @@
false
false
false
+ 0
1171
@@ -9374,6 +10545,7 @@
false
false
false
+ 0
1172
@@ -9382,6 +10554,7 @@
false
false
false
+ 0
1173
@@ -9390,6 +10563,7 @@
false
false
false
+ 0
1174
@@ -9398,6 +10572,7 @@
false
false
false
+ 0
1175
@@ -9406,6 +10581,7 @@
false
false
false
+ 0
1176
@@ -9414,6 +10590,7 @@
false
false
false
+ 0
1177
@@ -9422,6 +10599,7 @@
false
false
false
+ 0
1178
@@ -9430,6 +10608,7 @@
false
false
false
+ 0
1179
@@ -9438,6 +10617,7 @@
false
false
false
+ 0
1180
@@ -9446,6 +10626,7 @@
false
false
false
+ 0
1181
@@ -9454,6 +10635,7 @@
false
false
false
+ 0
1182
@@ -9462,6 +10644,7 @@
false
false
false
+ 0
1183
@@ -9470,6 +10653,7 @@
false
false
false
+ 0
1184
@@ -9478,6 +10662,7 @@
false
false
false
+ 0
1185
@@ -9486,6 +10671,7 @@
false
false
false
+ 0
1186
@@ -9494,6 +10680,7 @@
false
false
false
+ 0
1187
@@ -9502,6 +10689,7 @@
false
false
false
+ 0
1188
@@ -9510,6 +10698,7 @@
false
false
false
+ 0
1189
@@ -9518,6 +10707,7 @@
false
false
false
+ 0
1190
@@ -9526,6 +10716,7 @@
false
false
false
+ 0
1191
@@ -9534,6 +10725,7 @@
false
false
false
+ 0
1192
@@ -9542,6 +10734,7 @@
false
false
false
+ 0
1193
@@ -9550,6 +10743,7 @@
false
false
false
+ 0
1194
@@ -9558,6 +10752,7 @@
false
false
false
+ 0
1195
@@ -9566,6 +10761,7 @@
false
false
false
+ 0
1196
@@ -9574,6 +10770,7 @@
false
false
false
+ 0
1197
@@ -9582,6 +10779,7 @@
false
false
false
+ 0
1198
@@ -9590,6 +10788,7 @@
false
false
false
+ 0
1199
@@ -9598,6 +10797,7 @@
false
false
false
+ 0
1200
@@ -9606,6 +10806,7 @@
false
false
false
+ 0
1201
@@ -9614,6 +10815,7 @@
false
false
false
+ 0
1202
@@ -9622,6 +10824,7 @@
false
false
false
+ 0
1203
@@ -9630,6 +10833,7 @@
false
false
false
+ 0
1204
@@ -9638,6 +10842,7 @@
false
false
false
+ 0
1205
@@ -9646,6 +10851,7 @@
false
false
false
+ 0
1206
@@ -9654,6 +10860,7 @@
false
false
false
+ 0
1207
@@ -9662,6 +10869,7 @@
false
false
false
+ 0
1208
@@ -9670,6 +10878,7 @@
false
false
false
+ 0
1209
@@ -9678,6 +10887,7 @@
false
false
false
+ 0
1210
@@ -9686,6 +10896,7 @@
false
false
false
+ 0
1211
@@ -9694,6 +10905,7 @@
false
false
false
+ 0
1212
@@ -9702,6 +10914,7 @@
false
false
false
+ 0
1213
@@ -9710,6 +10923,7 @@
false
false
false
+ 0
1214
@@ -9718,6 +10932,7 @@
false
false
false
+ 0
1215
@@ -9726,6 +10941,7 @@
false
false
false
+ 0
1216
@@ -9734,6 +10950,7 @@
false
false
false
+ 0
1217
@@ -9742,6 +10959,7 @@
false
false
false
+ 0
1218
@@ -9750,6 +10968,7 @@
false
false
false
+ 0
1219
@@ -9758,6 +10977,7 @@
false
false
false
+ 0
1220
@@ -9766,6 +10986,7 @@
false
false
false
+ 0
1221
@@ -9774,6 +10995,7 @@
false
false
false
+ 0
1222
@@ -9782,6 +11004,7 @@
false
false
false
+ 0
1223
@@ -9790,6 +11013,7 @@
false
false
false
+ 0
1224
@@ -9798,6 +11022,7 @@
false
false
false
+ 0
1225
@@ -9806,6 +11031,7 @@
false
false
false
+ 0
1226
@@ -9814,6 +11040,7 @@
false
false
false
+ 0
1227
@@ -9822,6 +11049,7 @@
false
false
false
+ 0
1228
@@ -9830,6 +11058,7 @@
false
false
false
+ 0
1229
@@ -9838,6 +11067,7 @@
false
false
false
+ 0
1230
@@ -9846,6 +11076,7 @@
false
false
false
+ 0
1231
@@ -9854,6 +11085,7 @@
false
false
false
+ 0
1232
@@ -9862,6 +11094,7 @@
false
false
false
+ 0
1233
@@ -9870,6 +11103,7 @@
false
false
false
+ 0
1234
@@ -9878,6 +11112,7 @@
false
false
false
+ 0
1235
@@ -9886,6 +11121,7 @@
false
false
false
+ 0
1236
@@ -9894,6 +11130,7 @@
false
false
false
+ 0
1237
@@ -9902,6 +11139,7 @@
false
false
false
+ 0
1238
@@ -9910,6 +11148,7 @@
false
false
false
+ 0
1239
@@ -9918,6 +11157,7 @@
false
false
false
+ 0
1240
@@ -9926,6 +11166,7 @@
false
false
false
+ 0
1241
@@ -9934,6 +11175,7 @@
false
false
false
+ 0
1242
@@ -9942,6 +11184,7 @@
false
false
false
+ 0
1243
@@ -9950,6 +11193,7 @@
false
false
false
+ 0
1244
@@ -9958,6 +11202,7 @@
false
false
false
+ 0
1245
@@ -9966,6 +11211,7 @@
false
false
false
+ 0
1246
@@ -9974,6 +11220,7 @@
false
false
false
+ 0
1247
@@ -9982,6 +11229,7 @@
false
false
false
+ 0
1248
@@ -9990,6 +11238,7 @@
false
false
false
+ 0
1249
@@ -9998,6 +11247,7 @@
false
false
false
+ 0
1250
@@ -10006,6 +11256,7 @@
false
false
false
+ 0
1251
@@ -10014,6 +11265,7 @@
false
false
false
+ 0
1252
@@ -10022,6 +11274,7 @@
false
false
false
+ 0
1253
@@ -10030,6 +11283,7 @@
false
false
false
+ 0
1254
@@ -10038,6 +11292,7 @@
false
false
false
+ 0
1255
@@ -10046,6 +11301,7 @@
false
false
false
+ 0
1256
@@ -10054,6 +11310,7 @@
false
false
false
+ 0
1257
@@ -10062,6 +11319,7 @@
false
false
false
+ 0
1258
@@ -10070,6 +11328,7 @@
false
false
false
+ 0
1259
@@ -10078,6 +11337,7 @@
false
false
false
+ 0
1260
@@ -10086,6 +11346,7 @@
false
false
false
+ 0
1261
@@ -10094,6 +11355,7 @@
false
false
false
+ 0
1262
@@ -10102,6 +11364,7 @@
false
false
false
+ 0
1263
@@ -10110,6 +11373,7 @@
false
false
false
+ 0
1264
@@ -10118,6 +11382,7 @@
false
false
false
+ 0
1265
@@ -10126,6 +11391,7 @@
false
false
false
+ 0
1266
@@ -10134,6 +11400,7 @@
false
false
false
+ 0
1267
@@ -10142,6 +11409,7 @@
false
false
false
+ 0
1268
@@ -10150,6 +11418,7 @@
false
false
false
+ 0
1269
@@ -10158,6 +11427,7 @@
false
false
false
+ 0
1270
@@ -10166,6 +11436,7 @@
false
false
false
+ 0
1271
@@ -10174,6 +11445,7 @@
false
false
false
+ 0
1272
@@ -10182,6 +11454,7 @@
false
false
false
+ 0
1273
@@ -10190,6 +11463,7 @@
false
false
false
+ 0
1274
@@ -10198,6 +11472,7 @@
false
false
false
+ 0
1275
@@ -10206,6 +11481,7 @@
false
false
false
+ 0
1276
@@ -10214,6 +11490,7 @@
false
false
false
+ 0
1277
@@ -10222,6 +11499,7 @@
false
false
false
+ 0
1278
@@ -10230,6 +11508,7 @@
false
false
false
+ 0
1279
@@ -10238,6 +11517,7 @@
false
false
false
+ 0
1280
@@ -10246,6 +11526,7 @@
false
false
false
+ 0
1281
@@ -10254,6 +11535,7 @@
false
false
false
+ 0
1282
@@ -10262,6 +11544,7 @@
false
false
false
+ 0
1283
@@ -10270,6 +11553,7 @@
false
false
false
+ 0
1284
@@ -10278,6 +11562,7 @@
false
false
false
+ 0
1285
@@ -10286,6 +11571,7 @@
false
false
false
+ 0
1286
@@ -10294,6 +11580,7 @@
false
false
false
+ 0
1287
@@ -10302,6 +11589,7 @@
false
false
false
+ 0
1288
@@ -10310,6 +11598,7 @@
false
false
false
+ 0
1289
@@ -10318,6 +11607,7 @@
false
false
false
+ 0
1290
@@ -10326,6 +11616,7 @@
false
false
false
+ 0
1291
@@ -10334,6 +11625,7 @@
false
false
false
+ 0
1292
@@ -10342,6 +11634,7 @@
false
false
false
+ 0
1293
@@ -10350,6 +11643,7 @@
false
false
false
+ 0
1294
@@ -10358,6 +11652,7 @@
false
false
false
+ 0
1295
@@ -10366,6 +11661,7 @@
false
false
false
+ 0
1296
@@ -10374,6 +11670,7 @@
false
false
false
+ 0
1297
@@ -10382,6 +11679,7 @@
false
false
false
+ 0
1298
@@ -10390,6 +11688,7 @@
false
false
false
+ 0
1299
@@ -10398,6 +11697,7 @@
false
false
false
+ 0
1300
@@ -10406,6 +11706,7 @@
false
false
false
+ 0
1301
@@ -10414,6 +11715,7 @@
false
false
false
+ 0
1302
@@ -10422,6 +11724,7 @@
false
false
false
+ 0
1303
@@ -10430,6 +11733,7 @@
false
false
false
+ 0
1304
@@ -10438,6 +11742,7 @@
false
false
false
+ 0
1305
@@ -10446,6 +11751,7 @@
false
false
false
+ 0
1306
@@ -10454,6 +11760,7 @@
false
false
false
+ 0
1307
@@ -10462,6 +11769,7 @@
false
false
false
+ 0
1308
@@ -10470,6 +11778,7 @@
false
false
false
+ 0
1309
@@ -10478,6 +11787,7 @@
false
false
false
+ 0
1310
@@ -10486,6 +11796,7 @@
false
false
false
+ 0
1311
@@ -10494,6 +11805,7 @@
false
false
false
+ 0
1312
@@ -10502,6 +11814,7 @@
false
false
false
+ 0
1313
@@ -10510,6 +11823,7 @@
false
false
false
+ 0
1314
@@ -10518,6 +11832,7 @@
false
false
false
+ 0
1315
@@ -10526,6 +11841,7 @@
false
false
false
+ 0
1316
@@ -10534,6 +11850,7 @@
false
false
false
+ 0
1317
@@ -10542,6 +11859,7 @@
false
false
false
+ 0
1318
@@ -10550,6 +11868,7 @@
false
false
false
+ 0
1319
@@ -10558,6 +11877,7 @@
false
false
false
+ 0
1320
@@ -10566,6 +11886,7 @@
false
false
false
+ 0
1321
@@ -10574,6 +11895,7 @@
false
false
false
+ 0
1322
@@ -10582,6 +11904,7 @@
false
false
false
+ 0
1323
@@ -10590,6 +11913,7 @@
false
false
false
+ 0
1324
@@ -10598,6 +11922,7 @@
false
false
false
+ 0
1325
@@ -10606,6 +11931,7 @@
false
false
false
+ 0
1326
@@ -10614,6 +11940,7 @@
false
false
false
+ 0
1327
@@ -10622,6 +11949,7 @@
false
false
false
+ 0
1328
@@ -10630,6 +11958,7 @@
false
false
false
+ 0
1329
@@ -10638,6 +11967,7 @@
false
false
false
+ 0
1330
@@ -10646,6 +11976,7 @@
false
false
false
+ 0
1331
@@ -10654,6 +11985,7 @@
false
false
false
+ 0
1332
@@ -10662,6 +11994,7 @@
false
false
false
+ 0
1333
@@ -10670,6 +12003,7 @@
false
false
false
+ 0
1334
@@ -10678,6 +12012,7 @@
false
false
false
+ 0
1335
@@ -10686,6 +12021,7 @@
false
false
false
+ 0
1336
@@ -10694,6 +12030,7 @@
false
false
false
+ 0
1337
@@ -10702,6 +12039,7 @@
false
false
false
+ 0
1338
@@ -10710,6 +12048,7 @@
false
false
false
+ 0
1339
@@ -10718,6 +12057,7 @@
false
false
false
+ 0
1340
@@ -10726,6 +12066,7 @@
false
false
false
+ 0
1341
@@ -10734,6 +12075,7 @@
false
false
false
+ 0
1342
@@ -10742,6 +12084,7 @@
false
false
false
+ 0
1343
@@ -10750,6 +12093,7 @@
false
false
false
+ 0
1344
@@ -10758,6 +12102,7 @@
false
false
false
+ 0
1345
@@ -10766,6 +12111,7 @@
false
false
false
+ 0
1346
@@ -10774,6 +12120,7 @@
false
false
false
+ 0
1347
@@ -10782,6 +12129,7 @@
false
false
false
+ 0
1348
@@ -10790,6 +12138,7 @@
false
false
false
+ 0
1349
@@ -10798,6 +12147,7 @@
false
false
false
+ 0
1350
@@ -10806,6 +12156,7 @@
false
false
false
+ 0
1351
@@ -10814,6 +12165,7 @@
false
false
false
+ 0
1352
@@ -10822,6 +12174,7 @@
false
false
false
+ 0
1353
@@ -10830,6 +12183,7 @@
false
false
false
+ 0
1354
@@ -10838,6 +12192,7 @@
false
false
false
+ 0
1355
@@ -10846,6 +12201,7 @@
false
false
false
+ 0
1356
@@ -10854,6 +12210,7 @@
false
false
false
+ 0
1357
@@ -10862,6 +12219,7 @@
false
false
false
+ 0
1358
@@ -10870,6 +12228,7 @@
false
false
false
+ 0
1359
@@ -10878,6 +12237,7 @@
false
false
false
+ 0
1360
@@ -10886,6 +12246,7 @@
false
false
false
+ 0
1361
@@ -10894,6 +12255,7 @@
false
false
false
+ 0
1362
@@ -10902,6 +12264,7 @@
false
false
false
+ 0
1363
@@ -10910,6 +12273,7 @@
false
false
false
+ 0
1364
@@ -10918,6 +12282,7 @@
false
false
false
+ 0
1365
@@ -10926,6 +12291,7 @@
false
false
false
+ 0
1366
@@ -10934,6 +12300,7 @@
false
false
false
+ 0
1367
@@ -10942,6 +12309,7 @@
false
false
false
+ 0
1368
@@ -10950,6 +12318,7 @@
false
false
false
+ 0
1369
@@ -10958,6 +12327,7 @@
false
false
false
+ 0
1370
@@ -10966,6 +12336,7 @@
false
false
false
+ 0
1371
@@ -10974,6 +12345,7 @@
false
false
false
+ 0
1372
@@ -10982,6 +12354,7 @@
false
false
false
+ 0
1373
@@ -10990,6 +12363,7 @@
false
false
false
+ 0
1374
@@ -10998,6 +12372,7 @@
false
false
false
+ 0
1375
@@ -11006,6 +12381,7 @@
false
false
false
+ 0
1376
@@ -11014,6 +12390,7 @@
false
false
false
+ 0
1377
@@ -11022,6 +12399,7 @@
false
false
false
+ 0
1378
@@ -11030,6 +12408,7 @@
false
false
false
+ 0
1379
@@ -11038,6 +12417,7 @@
false
false
false
+ 0
1380
@@ -11046,6 +12426,7 @@
false
false
false
+ 0
1381
@@ -11054,6 +12435,7 @@
false
false
false
+ 0
1382
@@ -11062,6 +12444,7 @@
false
false
false
+ 0
1383
@@ -11070,6 +12453,7 @@
false
false
false
+ 0
1384
@@ -11078,6 +12462,7 @@
false
false
false
+ 0
1385
@@ -11086,6 +12471,7 @@
false
false
false
+ 0
1386
@@ -11094,6 +12480,7 @@
false
false
false
+ 0
1387
@@ -11102,6 +12489,7 @@
false
false
false
+ 0
1388
@@ -11110,6 +12498,7 @@
false
false
false
+ 0
1389
@@ -11118,6 +12507,7 @@
false
false
false
+ 0
1390
@@ -11126,6 +12516,7 @@
false
false
false
+ 0
1391
@@ -11134,6 +12525,7 @@
false
false
false
+ 0
1392
@@ -11142,6 +12534,7 @@
false
false
false
+ 0
1393
@@ -11150,6 +12543,7 @@
false
false
false
+ 0
1394
@@ -11158,6 +12552,7 @@
false
false
false
+ 0
1395
@@ -11166,6 +12561,7 @@
false
false
false
+ 0
1396
@@ -11174,6 +12570,7 @@
false
false
false
+ 0
1397
@@ -11182,6 +12579,7 @@
false
false
false
+ 0
1398
@@ -11190,6 +12588,7 @@
false
false
false
+ 0
1399
@@ -11198,6 +12597,7 @@
false
false
false
+ 0
1400
@@ -11206,6 +12606,7 @@
false
false
false
+ 0
1401
@@ -11214,6 +12615,7 @@
false
false
false
+ 0
1402
@@ -11222,6 +12624,7 @@
false
false
false
+ 0
1403
@@ -11230,6 +12633,7 @@
false
false
false
+ 0
1404
@@ -11238,6 +12642,7 @@
false
false
false
+ 0
1405
@@ -11246,6 +12651,7 @@
false
false
false
+ 0
1406
@@ -11254,6 +12660,7 @@
false
false
false
+ 0
1407
@@ -11262,6 +12669,7 @@
false
false
false
+ 0
1408
@@ -11270,6 +12678,7 @@
false
false
false
+ 0
1409
@@ -11278,6 +12687,7 @@
false
false
false
+ 0
1410
@@ -11286,6 +12696,7 @@
false
false
false
+ 0
1411
@@ -11294,6 +12705,7 @@
false
false
false
+ 0
1412
@@ -11302,6 +12714,7 @@
false
false
false
+ 0
1413
@@ -11310,6 +12723,7 @@
false
false
false
+ 0
1414
@@ -11318,6 +12732,7 @@
false
false
false
+ 0
1415
@@ -11326,6 +12741,7 @@
false
false
false
+ 0
1416
@@ -11334,6 +12750,7 @@
false
false
false
+ 0
1417
@@ -11342,6 +12759,7 @@
false
false
false
+ 0
1418
@@ -11350,6 +12768,7 @@
false
false
false
+ 0
1419
@@ -11358,6 +12777,7 @@
false
false
false
+ 0
1420
@@ -11366,6 +12786,7 @@
false
false
false
+ 0
1421
@@ -11374,6 +12795,7 @@
false
false
false
+ 0
1422
@@ -11382,6 +12804,7 @@
false
false
false
+ 0
1423
@@ -11390,6 +12813,7 @@
false
false
false
+ 0
1424
@@ -11398,6 +12822,7 @@
false
false
false
+ 0
1425
@@ -11406,6 +12831,7 @@
false
false
false
+ 0
1426
@@ -11414,6 +12840,7 @@
false
false
false
+ 0
1427
@@ -11422,6 +12849,7 @@
false
false
false
+ 0
1428
@@ -11430,6 +12858,7 @@
false
false
false
+ 0
1429
@@ -11438,6 +12867,7 @@
false
false
false
+ 0
1430
@@ -11446,6 +12876,7 @@
false
false
false
+ 0
1431
@@ -11454,6 +12885,7 @@
false
false
false
+ 0
1432
@@ -11462,6 +12894,7 @@
false
false
false
+ 0
1433
@@ -11470,6 +12903,7 @@
false
false
false
+ 0
1434
@@ -11478,6 +12912,7 @@
false
false
false
+ 0
1435
@@ -11486,6 +12921,7 @@
false
false
false
+ 0
1436
@@ -11494,6 +12930,7 @@
false
false
false
+ 0
1437
@@ -11502,6 +12939,7 @@
false
false
false
+ 0
1438
@@ -11510,6 +12948,7 @@
false
false
false
+ 0
1439
@@ -11518,6 +12957,7 @@
false
false
false
+ 0
1440
@@ -11526,6 +12966,7 @@
false
false
false
+ 0
1441
@@ -11534,6 +12975,7 @@
false
false
false
+ 0
1442
@@ -11542,6 +12984,7 @@
false
false
false
+ 0
1443
@@ -11550,6 +12993,7 @@
false
false
false
+ 0
1444
@@ -11558,6 +13002,7 @@
false
false
false
+ 0
1445
@@ -11566,6 +13011,7 @@
false
false
false
+ 0
1446
@@ -11574,6 +13020,7 @@
false
false
false
+ 0
1447
@@ -11582,6 +13029,7 @@
false
false
false
+ 0
1448
@@ -11590,6 +13038,7 @@
false
false
false
+ 0
1449
@@ -11598,6 +13047,7 @@
false
false
false
+ 0
1450
@@ -11606,6 +13056,7 @@
false
false
false
+ 0
1451
@@ -11614,6 +13065,7 @@
false
false
false
+ 0
1452
@@ -11622,6 +13074,7 @@
false
false
false
+ 0
1453
@@ -11630,6 +13083,7 @@
false
false
false
+ 0
1454
@@ -11638,6 +13092,7 @@
false
false
false
+ 0
1455
@@ -11646,6 +13101,7 @@
false
false
false
+ 0
1456
@@ -11654,6 +13110,7 @@
false
false
false
+ 0
1457
@@ -11662,6 +13119,7 @@
false
false
false
+ 0
1458
@@ -11670,6 +13128,7 @@
false
false
false
+ 0
1459
@@ -11678,6 +13137,7 @@
false
false
false
+ 0
1460
@@ -11686,6 +13146,7 @@
false
false
false
+ 0
1461
@@ -11694,6 +13155,7 @@
false
false
false
+ 0
1462
@@ -11702,6 +13164,7 @@
false
false
false
+ 0
1463
@@ -11710,6 +13173,7 @@
false
false
false
+ 0
1464
@@ -11718,6 +13182,7 @@
false
false
false
+ 0
1465
@@ -11726,6 +13191,7 @@
false
false
false
+ 0
1466
@@ -11734,6 +13200,7 @@
false
false
false
+ 0
1467
@@ -11742,6 +13209,7 @@
false
false
false
+ 0
1468
@@ -11750,6 +13218,7 @@
false
false
false
+ 0
1469
@@ -11758,6 +13227,7 @@
false
false
false
+ 0
1470
@@ -11766,6 +13236,7 @@
false
false
false
+ 0
1471
@@ -11774,6 +13245,7 @@
false
false
false
+ 0
1472
@@ -11782,6 +13254,7 @@
false
false
false
+ 0
1473
@@ -11790,6 +13263,7 @@
false
false
false
+ 0
1474
@@ -11798,6 +13272,7 @@
false
false
false
+ 0
1475
@@ -11806,6 +13281,7 @@
false
false
false
+ 0
1476
@@ -11814,6 +13290,7 @@
false
false
false
+ 0
1477
@@ -11822,6 +13299,7 @@
false
false
false
+ 0
1478
@@ -11830,6 +13308,7 @@
false
false
false
+ 0
1479
@@ -11838,6 +13317,7 @@
false
false
false
+ 0
1480
@@ -11846,6 +13326,7 @@
false
false
false
+ 0
1481
@@ -11854,6 +13335,7 @@
false
false
false
+ 0
1482
@@ -11862,6 +13344,7 @@
false
false
false
+ 0
1483
@@ -11870,6 +13353,7 @@
false
false
false
+ 0
1484
@@ -11878,6 +13362,7 @@
false
false
false
+ 0
1485
@@ -11886,6 +13371,7 @@
false
false
false
+ 0
1486
@@ -11894,6 +13380,7 @@
false
false
false
+ 0
1487
@@ -11902,6 +13389,7 @@
false
false
false
+ 0
1488
@@ -11910,6 +13398,7 @@
false
false
false
+ 0
1489
@@ -11918,6 +13407,7 @@
false
false
false
+ 0
1490
@@ -11926,6 +13416,7 @@
false
false
false
+ 0
1491
@@ -11934,6 +13425,7 @@
false
false
false
+ 0
1492
@@ -11942,6 +13434,7 @@
false
false
false
+ 0
1493
@@ -11950,6 +13443,7 @@
false
false
false
+ 0
1494
@@ -11958,6 +13452,7 @@
false
false
false
+ 0
1495
@@ -11966,6 +13461,7 @@
false
false
false
+ 0
1496
@@ -11974,6 +13470,7 @@
false
false
false
+ 0
1497
@@ -11982,6 +13479,7 @@
false
false
false
+ 0
1498
@@ -11990,6 +13488,7 @@
false
false
false
+ 0
1499
@@ -11998,6 +13497,7 @@
false
false
false
+ 0
1500
@@ -12006,6 +13506,7 @@
false
false
false
+ 0
1501
@@ -12014,6 +13515,7 @@
false
false
false
+ 0
1502
@@ -12022,6 +13524,7 @@
false
false
false
+ 0
1503
@@ -12030,6 +13533,7 @@
false
false
false
+ 0
1504
@@ -12038,6 +13542,7 @@
false
false
false
+ 0
1505
@@ -12046,6 +13551,7 @@
false
false
false
+ 0
1506
@@ -12054,6 +13560,7 @@
false
false
false
+ 0
1507
@@ -12062,6 +13569,7 @@
false
false
false
+ 0
1508
@@ -12070,6 +13578,7 @@
false
false
false
+ 0
1509
@@ -12078,6 +13587,7 @@
false
false
false
+ 0
1510
@@ -12086,6 +13596,7 @@
false
false
false
+ 0
1511
@@ -12094,6 +13605,7 @@
false
false
false
+ 0
1512
@@ -12102,6 +13614,7 @@
false
false
false
+ 0
1513
@@ -12110,6 +13623,7 @@
false
false
false
+ 0
1514
@@ -12118,6 +13632,7 @@
false
false
false
+ 0
1515
@@ -12126,6 +13641,7 @@
false
false
false
+ 0
1516
@@ -12134,6 +13650,7 @@
false
false
false
+ 0
1517
@@ -12142,6 +13659,7 @@
false
false
false
+ 0
1518
@@ -12150,6 +13668,7 @@
false
false
false
+ 0
1519
@@ -12158,6 +13677,7 @@
false
false
false
+ 0
1520
@@ -12166,6 +13686,7 @@
false
false
false
+ 0
1521
@@ -12174,6 +13695,7 @@
false
false
false
+ 0
1522
@@ -12182,6 +13704,7 @@
false
false
false
+ 0
1523
@@ -12190,6 +13713,7 @@
false
false
false
+ 0
1524
@@ -12198,6 +13722,7 @@
false
false
false
+ 0
1525
@@ -12206,6 +13731,7 @@
false
false
false
+ 0
1526
@@ -12214,6 +13740,7 @@
false
false
false
+ 0
1527
@@ -12222,6 +13749,7 @@
false
false
false
+ 0
1528
@@ -12230,6 +13758,7 @@
false
false
false
+ 0
1529
@@ -12238,6 +13767,7 @@
false
false
false
+ 0
1530
@@ -12246,6 +13776,7 @@
false
false
false
+ 0
1531
@@ -12254,6 +13785,7 @@
false
false
false
+ 0
1532
@@ -12262,6 +13794,7 @@
false
false
false
+ 0
1533
@@ -12270,6 +13803,7 @@
false
false
false
+ 0
1534
@@ -12278,6 +13812,7 @@
false
false
false
+ 0
1535
@@ -12286,6 +13821,7 @@
false
false
false
+ 0
1536
@@ -12294,6 +13830,7 @@
false
false
false
+ 0
1537
@@ -12302,6 +13839,7 @@
false
false
false
+ 0
1538
@@ -12310,6 +13848,7 @@
false
false
false
+ 0
1539
@@ -12318,6 +13857,7 @@
false
false
false
+ 0
1540
@@ -12326,6 +13866,7 @@
false
false
false
+ 0
1541
@@ -12334,6 +13875,7 @@
false
false
false
+ 0
1542
@@ -12342,6 +13884,7 @@
false
false
false
+ 0
1543
@@ -12350,6 +13893,7 @@
false
false
false
+ 0
1544
@@ -12358,6 +13902,7 @@
false
false
false
+ 0
1545
@@ -12366,6 +13911,7 @@
false
false
false
+ 0
1546
@@ -12374,6 +13920,7 @@
false
false
false
+ 0
1547
@@ -12382,6 +13929,7 @@
false
false
false
+ 0
1548
@@ -12390,6 +13938,7 @@
false
false
false
+ 0
1549
@@ -12398,6 +13947,7 @@
false
false
false
+ 0
1550
@@ -12406,6 +13956,7 @@
false
false
false
+ 0
1551
@@ -12414,6 +13965,7 @@
false
false
false
+ 0
1552
@@ -12422,6 +13974,7 @@
false
false
false
+ 0
1553
@@ -12430,6 +13983,7 @@
false
false
false
+ 0
1554
@@ -12438,6 +13992,7 @@
false
false
false
+ 0
1555
@@ -12446,6 +14001,7 @@
false
false
false
+ 0
1556
@@ -12454,6 +14010,7 @@
false
false
false
+ 0
1557
@@ -12462,6 +14019,7 @@
false
false
false
+ 0
1558
@@ -12470,6 +14028,7 @@
false
false
false
+ 0
1559
@@ -12478,6 +14037,7 @@
false
false
false
+ 0
1560
@@ -12486,6 +14046,7 @@
false
false
false
+ 0
1561
@@ -12494,6 +14055,7 @@
false
false
false
+ 0
1562
@@ -12502,6 +14064,7 @@
false
false
false
+ 0
1563
@@ -12510,6 +14073,7 @@
false
false
false
+ 0
1564
@@ -12518,6 +14082,7 @@
false
false
false
+ 0
1565
@@ -12526,6 +14091,7 @@
false
false
false
+ 0
1566
@@ -12534,6 +14100,7 @@
false
false
false
+ 0
1567
@@ -12542,6 +14109,7 @@
false
false
false
+ 0
1568
@@ -12550,6 +14118,7 @@
false
false
false
+ 0
1569
@@ -12558,6 +14127,7 @@
false
false
false
+ 0
1570
@@ -12566,6 +14136,7 @@
false
false
false
+ 0
1571
@@ -12574,6 +14145,7 @@
false
false
false
+ 0
1572
@@ -12582,6 +14154,7 @@
false
false
false
+ 0
1573
@@ -12590,6 +14163,7 @@
false
false
false
+ 0
1574
@@ -12598,6 +14172,7 @@
false
false
false
+ 0
1575
@@ -12606,6 +14181,7 @@
false
false
false
+ 0
1576
@@ -12614,6 +14190,7 @@
false
false
false
+ 0
1577
@@ -12622,6 +14199,7 @@
false
false
false
+ 0
1578
@@ -12630,6 +14208,7 @@
false
false
false
+ 0
1579
@@ -12638,6 +14217,7 @@
false
false
false
+ 0
1580
@@ -12646,6 +14226,7 @@
false
false
false
+ 0
1581
@@ -12654,6 +14235,7 @@
false
false
false
+ 0
1582
@@ -12662,6 +14244,7 @@
false
false
false
+ 0
1583
@@ -12670,6 +14253,7 @@
false
false
false
+ 0
1584
@@ -12678,6 +14262,7 @@
false
false
false
+ 0
1585
@@ -12686,6 +14271,7 @@
false
false
false
+ 0
1586
@@ -12694,6 +14280,7 @@
false
false
false
+ 0
1587
@@ -12702,6 +14289,7 @@
false
false
false
+ 0
1588
@@ -12710,6 +14298,7 @@
false
false
false
+ 0
1589
@@ -12718,6 +14307,7 @@
false
false
false
+ 0
1590
@@ -12726,6 +14316,7 @@
false
false
false
+ 0
1591
@@ -12734,6 +14325,7 @@
false
false
false
+ 0
1592
@@ -12742,6 +14334,7 @@
false
false
false
+ 0
1593
@@ -12750,6 +14343,7 @@
false
false
false
+ 0
1594
@@ -12758,6 +14352,7 @@
false
false
false
+ 0
1595
@@ -12766,6 +14361,7 @@
false
false
false
+ 0
1596
@@ -12774,6 +14370,7 @@
false
false
false
+ 0
1597
@@ -12782,6 +14379,7 @@
false
false
false
+ 0
1598
@@ -12790,6 +14388,7 @@
false
false
false
+ 0
1599
@@ -12798,6 +14397,7 @@
false
false
false
+ 0
1600
@@ -12806,6 +14406,7 @@
false
false
false
+ 0
1601
@@ -12814,6 +14415,7 @@
false
false
false
+ 0
1602
@@ -12822,6 +14424,7 @@
false
false
false
+ 0
1603
@@ -12830,6 +14433,7 @@
false
false
false
+ 0
1604
@@ -12838,6 +14442,7 @@
false
false
false
+ 0
1605
@@ -12846,6 +14451,7 @@
false
false
false
+ 0
1606
@@ -12854,6 +14460,7 @@
false
false
false
+ 0
1607
@@ -12862,6 +14469,7 @@
false
false
false
+ 0
1608
@@ -12870,6 +14478,7 @@
false
false
false
+ 0
1609
@@ -12878,6 +14487,7 @@
false
false
false
+ 0
1610
@@ -12886,6 +14496,7 @@
false
false
false
+ 0
1611
@@ -12894,6 +14505,7 @@
false
false
false
+ 0
1612
@@ -12902,6 +14514,7 @@
false
false
false
+ 0
1613
@@ -12910,6 +14523,7 @@
false
false
false
+ 0
1614
@@ -12918,6 +14532,7 @@
false
false
false
+ 0
1615
@@ -12926,6 +14541,7 @@
false
false
false
+ 0
1616
@@ -12934,6 +14550,7 @@
false
false
false
+ 0
1617
@@ -12942,6 +14559,7 @@
false
false
false
+ 0
1618
@@ -12950,6 +14568,7 @@
false
false
false
+ 0
1619
@@ -12958,6 +14577,7 @@
false
false
false
+ 0
1620
@@ -12966,6 +14586,7 @@
false
false
false
+ 0
1621
@@ -12974,6 +14595,7 @@
false
false
false
+ 0
1622
@@ -12982,6 +14604,7 @@
false
false
false
+ 0
1623
@@ -12990,6 +14613,7 @@
false
false
false
+ 0
1624
@@ -12998,6 +14622,7 @@
false
false
false
+ 0
1625
@@ -13006,6 +14631,7 @@
false
false
false
+ 0
1626
@@ -13014,6 +14640,7 @@
false
false
false
+ 0
1627
@@ -13022,6 +14649,7 @@
false
false
false
+ 0
1628
@@ -13030,6 +14658,7 @@
false
false
false
+ 0
1629
@@ -13038,6 +14667,7 @@
false
false
false
+ 0
1630
@@ -13046,6 +14676,7 @@
false
false
false
+ 0
1631
@@ -13054,6 +14685,7 @@
false
false
false
+ 0
1632
@@ -13062,6 +14694,7 @@
false
false
false
+ 0
1633
@@ -13070,6 +14703,7 @@
false
false
false
+ 0
1634
@@ -13078,6 +14712,7 @@
false
false
false
+ 0
1635
@@ -13086,6 +14721,7 @@
false
false
false
+ 0
1636
@@ -13094,6 +14730,7 @@
false
false
false
+ 0
1637
@@ -13102,6 +14739,7 @@
false
false
false
+ 0
1638
@@ -13110,6 +14748,7 @@
false
false
false
+ 0
1639
@@ -13118,6 +14757,7 @@
false
false
false
+ 0
1640
@@ -13126,6 +14766,7 @@
false
false
false
+ 0
1641
@@ -13134,6 +14775,7 @@
false
false
false
+ 0
1642
@@ -13142,6 +14784,7 @@
false
false
false
+ 0
1643
@@ -13150,6 +14793,7 @@
false
false
false
+ 0
1644
@@ -13158,6 +14802,7 @@
false
false
false
+ 0
1645
@@ -13166,6 +14811,7 @@
false
false
false
+ 0
1646
@@ -13174,6 +14820,7 @@
false
false
false
+ 0
1647
@@ -13182,6 +14829,7 @@
false
false
false
+ 0
1648
@@ -13190,6 +14838,7 @@
false
false
false
+ 0
1649
@@ -13198,6 +14847,7 @@
false
false
false
+ 0
1650
@@ -13206,6 +14856,7 @@
false
false
false
+ 0
1651
@@ -13214,6 +14865,7 @@
false
false
false
+ 0
1652
@@ -13222,6 +14874,7 @@
false
false
false
+ 0
1653
@@ -13230,6 +14883,7 @@
false
false
false
+ 0
1654
@@ -13238,6 +14892,7 @@
false
false
false
+ 0
1655
@@ -13246,6 +14901,7 @@
false
false
false
+ 0
1656
@@ -13254,6 +14910,7 @@
false
false
false
+ 0
1657
@@ -13262,6 +14919,7 @@
false
false
false
+ 0
1658
@@ -13270,6 +14928,7 @@
false
false
false
+ 0
1659
@@ -13278,6 +14937,7 @@
false
false
false
+ 0
1660
@@ -13286,6 +14946,7 @@
false
false
false
+ 0
1661
@@ -13294,6 +14955,7 @@
false
false
false
+ 0
1662
@@ -13302,6 +14964,7 @@
false
false
false
+ 0
1663
@@ -13310,6 +14973,7 @@
false
false
false
+ 0
1664
@@ -13318,6 +14982,7 @@
false
false
false
+ 0
1665
@@ -13326,6 +14991,7 @@
false
false
false
+ 0
1666
@@ -13334,6 +15000,7 @@
false
false
false
+ 0
1667
@@ -13342,6 +15009,7 @@
false
false
false
+ 0
1668
@@ -13350,6 +15018,7 @@
false
false
false
+ 0
1669
@@ -13358,6 +15027,7 @@
false
false
false
+ 0
1670
@@ -13366,6 +15036,7 @@
false
false
false
+ 0
1671
@@ -13374,6 +15045,7 @@
false
false
false
+ 0
1672
@@ -13382,6 +15054,7 @@
false
false
false
+ 0
1673
@@ -13390,6 +15063,7 @@
false
false
false
+ 0
1674
@@ -13398,6 +15072,7 @@
false
false
false
+ 0
1675
@@ -13406,6 +15081,7 @@
false
false
false
+ 0
1676
@@ -13414,6 +15090,7 @@
false
false
false
+ 0
1677
@@ -13422,6 +15099,7 @@
false
false
false
+ 0
1678
@@ -13430,6 +15108,7 @@
false
false
false
+ 0
1679
@@ -13438,6 +15117,7 @@
false
false
false
+ 0
1680
@@ -13446,6 +15126,7 @@
false
false
false
+ 0
1681
@@ -13454,6 +15135,7 @@
false
false
false
+ 0
1682
@@ -13462,6 +15144,7 @@
false
false
false
+ 0
1683
@@ -13470,6 +15153,7 @@
false
false
false
+ 0
1684
@@ -13478,6 +15162,7 @@
false
false
false
+ 0
1685
@@ -13486,6 +15171,7 @@
false
false
false
+ 0
1686
@@ -13494,6 +15180,7 @@
false
false
false
+ 0
1687
@@ -13502,6 +15189,7 @@
false
false
false
+ 0
1688
@@ -13510,6 +15198,7 @@
false
false
false
+ 0
1689
@@ -13518,6 +15207,7 @@
false
false
false
+ 0
1690
@@ -13526,6 +15216,7 @@
false
false
false
+ 0
1691
@@ -13534,6 +15225,7 @@
false
false
false
+ 0
1692
@@ -13542,6 +15234,7 @@
false
false
false
+ 0
1693
@@ -13550,6 +15243,7 @@
false
false
false
+ 0
1694
@@ -13558,6 +15252,7 @@
false
false
false
+ 0
1695
@@ -13566,6 +15261,7 @@
false
false
false
+ 0
1696
@@ -13574,6 +15270,7 @@
false
false
false
+ 0
1697
@@ -13582,6 +15279,7 @@
false
false
false
+ 0
1698
@@ -13590,6 +15288,7 @@
false
false
false
+ 0
1699
@@ -13598,6 +15297,7 @@
false
false
false
+ 0
1700
@@ -13606,6 +15306,7 @@
false
false
false
+ 0
1701
@@ -13614,6 +15315,7 @@
false
false
false
+ 0
1702
@@ -13622,6 +15324,7 @@
false
false
false
+ 0
1703
@@ -13630,6 +15333,7 @@
false
false
false
+ 0
1704
@@ -13638,6 +15342,7 @@
false
false
false
+ 0
1705
@@ -13646,6 +15351,7 @@
false
false
false
+ 0
1706
@@ -13654,6 +15360,7 @@
false
false
false
+ 0
1707
@@ -13662,6 +15369,7 @@
false
false
false
+ 0
1708
@@ -13670,6 +15378,7 @@
false
false
false
+ 0
1709
@@ -13678,6 +15387,7 @@
false
false
false
+ 0
1710
@@ -13686,6 +15396,7 @@
false
false
false
+ 0
1711
@@ -13694,6 +15405,7 @@
false
false
false
+ 0
1712
@@ -13702,6 +15414,7 @@
false
false
false
+ 0
1713
@@ -13710,6 +15423,7 @@
false
false
false
+ 0
1714
@@ -13718,6 +15432,7 @@
false
false
false
+ 0
1715
@@ -13726,6 +15441,7 @@
false
false
false
+ 0
1716
@@ -13734,6 +15450,7 @@
false
false
false
+ 0
1717
@@ -13742,6 +15459,7 @@
false
false
false
+ 0
1718
@@ -13750,6 +15468,7 @@
false
false
false
+ 0
1719
@@ -13758,6 +15477,7 @@
false
false
false
+ 0
1720
@@ -13766,6 +15486,7 @@
false
false
false
+ 0
1721
@@ -13774,6 +15495,7 @@
false
false
false
+ 0
1722
@@ -13782,6 +15504,7 @@
false
false
false
+ 0
1723
@@ -13790,6 +15513,7 @@
false
false
false
+ 0
1724
@@ -13798,6 +15522,7 @@
false
false
false
+ 0
1725
@@ -13806,6 +15531,7 @@
false
false
false
+ 0
1726
@@ -13814,6 +15540,7 @@
false
false
false
+ 0
1727
@@ -13822,6 +15549,7 @@
false
false
false
+ 0
1728
@@ -13830,6 +15558,7 @@
false
false
false
+ 0
1729
@@ -13838,6 +15567,7 @@
false
false
false
+ 0
1730
@@ -13846,6 +15576,7 @@
false
false
false
+ 0
1731
@@ -13854,6 +15585,7 @@
false
false
false
+ 0
1732
@@ -13862,6 +15594,7 @@
false
false
false
+ 0
1733
@@ -13870,6 +15603,7 @@
false
false
false
+ 0
1734
@@ -13878,6 +15612,7 @@
false
false
false
+ 0
1735
@@ -13886,6 +15621,7 @@
false
false
false
+ 0
1736
@@ -13894,6 +15630,7 @@
false
false
false
+ 0
1737
@@ -13902,6 +15639,7 @@
false
false
false
+ 0
1738
@@ -13910,6 +15648,7 @@
false
false
false
+ 0
1739
@@ -13918,6 +15657,7 @@
false
false
false
+ 0
1740
@@ -13926,6 +15666,7 @@
false
false
false
+ 0
1741
@@ -13934,6 +15675,7 @@
false
false
false
+ 0
1742
@@ -13942,6 +15684,7 @@
false
false
false
+ 0
1743
@@ -13950,6 +15693,7 @@
false
false
false
+ 0
1744
@@ -13958,6 +15702,7 @@
false
false
false
+ 0
1745
@@ -13966,6 +15711,7 @@
false
false
false
+ 0
1746
@@ -13974,6 +15720,7 @@
false
false
false
+ 0
1747
@@ -13982,6 +15729,7 @@
false
false
false
+ 0
1748
@@ -13990,6 +15738,7 @@
false
false
false
+ 0
1749
@@ -13998,6 +15747,7 @@
false
false
false
+ 0
1750
@@ -14006,6 +15756,7 @@
false
false
false
+ 0
1751
@@ -14014,6 +15765,7 @@
false
false
false
+ 0
1752
@@ -14022,6 +15774,7 @@
false
false
false
+ 0
1753
@@ -14030,6 +15783,7 @@
false
false
false
+ 0
1754
@@ -14038,6 +15792,7 @@
false
false
false
+ 0
1755
@@ -14046,6 +15801,7 @@
false
false
false
+ 0
1756
@@ -14054,6 +15810,7 @@
false
false
false
+ 0
1757
@@ -14062,6 +15819,7 @@
false
false
false
+ 0
1758
@@ -14070,6 +15828,7 @@
false
false
false
+ 0
1759
@@ -14078,6 +15837,7 @@
false
false
false
+ 0
1760
@@ -14086,6 +15846,7 @@
false
false
false
+ 0
1761
@@ -14094,6 +15855,7 @@
false
false
false
+ 0
1762
@@ -14102,6 +15864,7 @@
false
false
false
+ 0
1763
@@ -14110,6 +15873,7 @@
false
false
false
+ 0
1764
@@ -14118,6 +15882,7 @@
false
false
false
+ 0
1765
@@ -14126,6 +15891,7 @@
false
false
false
+ 0
1766
@@ -14134,6 +15900,7 @@
false
false
false
+ 0
1767
@@ -14142,6 +15909,7 @@
false
false
false
+ 0
1768
@@ -14150,6 +15918,7 @@
false
false
false
+ 0
1769
@@ -14158,6 +15927,7 @@
false
false
false
+ 0
1770
@@ -14166,6 +15936,7 @@
false
false
false
+ 0
1771
@@ -14174,6 +15945,7 @@
false
false
false
+ 0
1772
@@ -14182,6 +15954,7 @@
false
false
false
+ 0
1773
@@ -14190,6 +15963,7 @@
false
false
false
+ 0
1774
@@ -14198,6 +15972,7 @@
false
false
false
+ 0
1775
@@ -14206,6 +15981,7 @@
false
false
false
+ 0
1776
@@ -14214,6 +15990,7 @@
false
false
false
+ 0
1777
@@ -14222,6 +15999,7 @@
false
false
false
+ 0
1778
@@ -14230,6 +16008,7 @@
false
false
false
+ 0
1779
@@ -14238,6 +16017,7 @@
false
false
false
+ 0
1780
@@ -14246,6 +16026,7 @@
false
false
false
+ 0
1781
@@ -14254,6 +16035,7 @@
false
false
false
+ 0
1782
@@ -14262,6 +16044,7 @@
false
false
false
+ 0
1783
@@ -14270,6 +16053,7 @@
false
false
false
+ 0
1784
@@ -14278,6 +16062,7 @@
false
false
false
+ 0
1785
@@ -14286,6 +16071,7 @@
false
false
false
+ 0
1786
@@ -14294,6 +16080,7 @@
false
false
false
+ 0
1787
@@ -14302,6 +16089,7 @@
false
false
false
+ 0
1788
@@ -14310,6 +16098,7 @@
false
false
false
+ 0
1789
@@ -14318,6 +16107,7 @@
false
false
false
+ 0
1790
@@ -14326,6 +16116,7 @@
false
false
false
+ 0
1791
@@ -14334,6 +16125,7 @@
false
false
false
+ 0
1792
@@ -14342,6 +16134,7 @@
false
false
false
+ 0
1793
@@ -14350,6 +16143,7 @@
false
false
false
+ 0
1794
@@ -14358,6 +16152,7 @@
false
false
false
+ 0
1795
@@ -14366,6 +16161,7 @@
false
false
false
+ 0
1796
@@ -14374,6 +16170,7 @@
false
false
false
+ 0
1797
@@ -14382,6 +16179,7 @@
false
false
false
+ 0
1798
@@ -14390,6 +16188,7 @@
false
false
false
+ 0
1799
@@ -14398,6 +16197,7 @@
false
false
false
+ 0
1800
@@ -14406,6 +16206,7 @@
false
false
false
+ 0
1801
@@ -14414,6 +16215,7 @@
false
false
false
+ 0
1802
@@ -14422,6 +16224,7 @@
false
false
false
+ 0
1803
@@ -14430,6 +16233,7 @@
false
false
false
+ 0
1804
@@ -14438,6 +16242,7 @@
false
false
false
+ 0
1805
@@ -14446,6 +16251,7 @@
false
false
false
+ 0
1806
@@ -14454,6 +16260,7 @@
false
false
false
+ 0
1807
@@ -14462,6 +16269,7 @@
false
false
false
+ 0
1808
@@ -14470,6 +16278,7 @@
false
false
false
+ 0
1809
@@ -14478,6 +16287,7 @@
false
false
false
+ 0
1810
@@ -14486,6 +16296,7 @@
false
false
false
+ 0
1811
@@ -14494,6 +16305,7 @@
false
false
false
+ 0
1812
@@ -14502,6 +16314,7 @@
false
false
false
+ 0
1813
@@ -14510,6 +16323,7 @@
false
false
false
+ 0
1814
@@ -14518,6 +16332,7 @@
false
false
false
+ 0
1815
@@ -14526,6 +16341,7 @@
false
false
false
+ 0
1816
@@ -14534,6 +16350,7 @@
false
false
false
+ 0
1817
@@ -14542,6 +16359,7 @@
false
false
false
+ 0
1818
@@ -14550,6 +16368,7 @@
false
false
false
+ 0
1819
@@ -14558,6 +16377,7 @@
false
false
false
+ 0
1820
@@ -14566,6 +16386,7 @@
false
false
false
+ 0
1821
@@ -14574,6 +16395,7 @@
false
false
false
+ 0
1822
@@ -14582,6 +16404,7 @@
false
false
false
+ 0
1823
@@ -14590,6 +16413,7 @@
false
false
false
+ 0
1824
@@ -14598,6 +16422,7 @@
false
false
false
+ 0
1825
@@ -14606,6 +16431,7 @@
false
false
false
+ 0
1826
@@ -14614,6 +16440,7 @@
false
false
false
+ 0
1827
@@ -14622,6 +16449,7 @@
false
false
false
+ 0
1828
@@ -14630,6 +16458,7 @@
false
false
false
+ 0
1829
@@ -14638,6 +16467,7 @@
false
false
false
+ 0
1830
@@ -14646,6 +16476,7 @@
false
false
false
+ 0
1831
@@ -14654,6 +16485,7 @@
false
false
false
+ 0
1832
@@ -14662,6 +16494,7 @@
false
false
false
+ 0
1833
@@ -14670,6 +16503,7 @@
false
false
false
+ 0
1834
@@ -14678,6 +16512,7 @@
false
false
false
+ 0
1835
@@ -14686,6 +16521,7 @@
false
false
false
+ 0
1836
@@ -14694,6 +16530,7 @@
false
false
false
+ 0
1837
@@ -14702,6 +16539,7 @@
false
false
false
+ 0
1838
@@ -14710,6 +16548,7 @@
false
false
false
+ 0
1839
@@ -14718,6 +16557,7 @@
false
false
false
+ 0
1840
@@ -14726,6 +16566,7 @@
false
false
false
+ 0
1841
@@ -14734,6 +16575,7 @@
false
false
false
+ 0
1842
@@ -14742,6 +16584,7 @@
false
false
false
+ 0
1843
@@ -14750,6 +16593,7 @@
false
false
false
+ 0
1844
@@ -14758,6 +16602,7 @@
false
false
false
+ 0
1845
@@ -14766,6 +16611,7 @@
false
false
false
+ 0
1846
@@ -14774,6 +16620,7 @@
false
false
false
+ 0
1847
@@ -14782,6 +16629,7 @@
false
false
false
+ 0
1848
@@ -14790,6 +16638,7 @@
false
false
false
+ 0
1849
@@ -14798,6 +16647,7 @@
false
false
false
+ 0
1850
@@ -14806,6 +16656,7 @@
false
false
false
+ 0
1851
@@ -14814,6 +16665,7 @@
false
false
false
+ 0
1852
@@ -14822,6 +16674,7 @@
false
false
false
+ 0
1853
@@ -14830,6 +16683,7 @@
false
false
false
+ 0
1854
@@ -14838,6 +16692,7 @@
false
false
false
+ 0
1855
@@ -14846,6 +16701,7 @@
false
false
false
+ 0
1856
@@ -14854,6 +16710,7 @@
false
false
false
+ 0
1857
@@ -14862,6 +16719,7 @@
false
false
false
+ 0
1858
@@ -14870,6 +16728,7 @@
false
false
false
+ 0
1859
@@ -14878,6 +16737,7 @@
false
false
false
+ 0
1860
@@ -14886,6 +16746,7 @@
false
false
false
+ 0
1861
@@ -14894,6 +16755,7 @@
false
false
false
+ 0
1862
@@ -14902,6 +16764,7 @@
false
false
false
+ 0
1863
@@ -14910,6 +16773,7 @@
false
false
false
+ 0
1864
@@ -14918,6 +16782,7 @@
false
false
false
+ 0
1865
@@ -14926,6 +16791,7 @@
false
false
false
+ 0
1866
@@ -14934,6 +16800,7 @@
false
false
false
+ 0
1867
@@ -14942,6 +16809,7 @@
false
false
false
+ 0
1868
@@ -14950,6 +16818,7 @@
false
false
false
+ 0
1869
@@ -14958,6 +16827,7 @@
false
false
false
+ 0
1870
@@ -14966,6 +16836,7 @@
false
false
false
+ 0
1871
@@ -14974,6 +16845,7 @@
false
false
false
+ 0
1872
@@ -14982,6 +16854,7 @@
false
false
false
+ 0
1873
@@ -14990,6 +16863,7 @@
false
false
false
+ 0
1874
@@ -14998,6 +16872,7 @@
false
false
false
+ 0
1875
@@ -15006,6 +16881,7 @@
false
false
false
+ 0
1876
@@ -15014,6 +16890,7 @@
false
false
false
+ 0
1877
@@ -15022,6 +16899,7 @@
false
false
false
+ 0
1878
@@ -15030,6 +16908,7 @@
false
false
false
+ 0
1879
@@ -15038,6 +16917,7 @@
false
false
false
+ 0
1880
@@ -15046,6 +16926,7 @@
false
false
false
+ 0
1881
@@ -15054,6 +16935,7 @@
false
false
false
+ 0
1882
@@ -15062,6 +16944,7 @@
false
false
false
+ 0
1883
@@ -15070,6 +16953,7 @@
false
false
false
+ 0
1884
@@ -15078,6 +16962,7 @@
false
false
false
+ 0
1885
@@ -15086,6 +16971,7 @@
false
false
false
+ 0
1886
@@ -15094,6 +16980,7 @@
false
false
false
+ 0
1887
@@ -15102,6 +16989,7 @@
false
false
false
+ 0
1888
@@ -15110,6 +16998,7 @@
false
false
false
+ 0
1889
@@ -15118,6 +17007,7 @@
false
false
false
+ 0
1890
@@ -15126,6 +17016,7 @@
false
false
false
+ 0
1891
@@ -15134,6 +17025,7 @@
false
false
false
+ 0
1892
@@ -15142,6 +17034,7 @@
false
false
false
+ 0
1893
@@ -15150,6 +17043,7 @@
false
false
false
+ 0
1894
@@ -15158,6 +17052,7 @@
false
false
false
+ 0
1895
@@ -15166,6 +17061,7 @@
false
false
false
+ 0
1896
@@ -15174,6 +17070,7 @@
false
false
false
+ 0
1897
@@ -15182,6 +17079,7 @@
false
false
false
+ 0
1898
@@ -15190,6 +17088,7 @@
false
false
false
+ 0
1899
@@ -15198,6 +17097,7 @@
false
false
false
+ 0
1900
@@ -15206,6 +17106,7 @@
false
false
false
+ 0
1901
@@ -15214,6 +17115,7 @@
false
false
false
+ 0
1902
@@ -15222,6 +17124,7 @@
false
false
false
+ 0
1903
@@ -15230,6 +17133,7 @@
false
false
false
+ 0
1904
@@ -15238,6 +17142,7 @@
false
false
false
+ 0
1905
@@ -15246,6 +17151,7 @@
false
false
false
+ 0
1906
@@ -15254,6 +17160,7 @@
false
false
false
+ 0
1907
@@ -15262,6 +17169,7 @@
false
false
false
+ 0
1908
@@ -15270,6 +17178,7 @@
false
false
false
+ 0
1909
@@ -15278,6 +17187,7 @@
false
false
false
+ 0
1910
@@ -15286,6 +17196,7 @@
false
false
false
+ 0
1911
@@ -15294,6 +17205,7 @@
false
false
false
+ 0
1912
@@ -15302,6 +17214,7 @@
false
false
false
+ 0
1913
@@ -15310,6 +17223,7 @@
false
false
false
+ 0
1914
@@ -15318,6 +17232,7 @@
false
false
false
+ 0
1915
@@ -15326,6 +17241,7 @@
false
false
false
+ 0
1916
@@ -15334,6 +17250,7 @@
false
false
false
+ 0
1917
@@ -15342,6 +17259,7 @@
false
false
false
+ 0
1918
@@ -15350,6 +17268,7 @@
false
false
false
+ 0
1919
@@ -15358,6 +17277,7 @@
false
false
false
+ 0
1920
@@ -15366,6 +17286,7 @@
false
false
false
+ 0
1921
@@ -15374,6 +17295,7 @@
false
false
false
+ 0
1922
@@ -15382,6 +17304,7 @@
false
false
false
+ 0
1923
@@ -15390,6 +17313,7 @@
false
false
false
+ 0
1924
@@ -15398,6 +17322,7 @@
false
false
false
+ 0
1925
@@ -15406,6 +17331,7 @@
false
false
false
+ 0
1926
@@ -15414,6 +17340,7 @@
false
false
false
+ 0
1927
@@ -15422,6 +17349,7 @@
false
false
false
+ 0
1928
@@ -15430,6 +17358,7 @@
false
false
false
+ 0
1929
@@ -15438,6 +17367,7 @@
false
false
false
+ 0
1930
@@ -15446,6 +17376,7 @@
false
false
false
+ 0
1931
@@ -15454,6 +17385,7 @@
false
false
false
+ 0
1932
@@ -15462,6 +17394,7 @@
false
false
false
+ 0
1933
@@ -15470,6 +17403,7 @@
false
false
false
+ 0
1934
@@ -15478,6 +17412,7 @@
false
false
false
+ 0
1935
@@ -15486,6 +17421,7 @@
false
false
false
+ 0
1936
@@ -15494,6 +17430,7 @@
false
false
false
+ 0
1937
@@ -15502,6 +17439,7 @@
false
false
false
+ 0
1938
@@ -15510,6 +17448,7 @@
false
false
false
+ 0
1939
@@ -15518,6 +17457,7 @@
false
false
false
+ 0
1940
@@ -15526,6 +17466,7 @@
false
false
false
+ 0
1941
@@ -15534,6 +17475,7 @@
false
false
false
+ 0
1942
@@ -15542,6 +17484,7 @@
false
false
false
+ 0
1943
@@ -15550,6 +17493,7 @@
false
false
false
+ 0
1944
@@ -15558,6 +17502,7 @@
false
false
false
+ 0
1945
@@ -15566,6 +17511,7 @@
false
false
false
+ 0
1946
@@ -15574,6 +17520,7 @@
false
false
false
+ 0
1947
@@ -15582,6 +17529,7 @@
false
false
false
+ 0
1948
@@ -15590,6 +17538,7 @@
false
false
false
+ 0
1949
@@ -15598,6 +17547,7 @@
false
false
false
+ 0
1950
@@ -15606,6 +17556,7 @@
false
false
false
+ 0
1951
@@ -15614,6 +17565,7 @@
false
false
false
+ 0
1952
@@ -15622,6 +17574,7 @@
false
false
false
+ 0
1953
@@ -15630,6 +17583,7 @@
false
false
false
+ 0
1954
@@ -15638,6 +17592,7 @@
false
false
false
+ 0
1955
@@ -15646,6 +17601,7 @@
false
false
false
+ 0
1956
@@ -15654,6 +17610,7 @@
false
false
false
+ 0
1957
@@ -15662,6 +17619,7 @@
false
false
false
+ 0
1958
@@ -15670,6 +17628,7 @@
false
false
false
+ 0
1959
@@ -15678,6 +17637,7 @@
false
false
false
+ 0
1960
@@ -15686,6 +17646,7 @@
false
false
false
+ 0
1961
@@ -15694,6 +17655,7 @@
false
false
false
+ 0
1962
@@ -15702,6 +17664,7 @@
false
false
false
+ 0
1963
@@ -15710,6 +17673,7 @@
false
false
false
+ 0
1964
@@ -15718,6 +17682,7 @@
false
false
false
+ 0
1965
@@ -15726,6 +17691,7 @@
false
false
false
+ 0
1966
@@ -15734,6 +17700,7 @@
false
false
false
+ 0
1967
@@ -15742,6 +17709,7 @@
false
false
false
+ 0
1968
@@ -15750,6 +17718,7 @@
false
false
false
+ 0
1969
@@ -15758,6 +17727,7 @@
false
false
false
+ 0
1970
@@ -15766,6 +17736,7 @@
false
false
false
+ 0
1971
@@ -15774,6 +17745,7 @@
false
false
false
+ 0
1972
@@ -15782,6 +17754,7 @@
false
false
false
+ 0
1973
@@ -15790,6 +17763,7 @@
false
false
false
+ 0
1974
@@ -15798,6 +17772,7 @@
false
false
false
+ 0
1975
@@ -15806,6 +17781,7 @@
false
false
false
+ 0
1976
@@ -15814,6 +17790,7 @@
false
false
false
+ 0
1977
@@ -15822,6 +17799,7 @@
false
false
false
+ 0
1978
@@ -15830,6 +17808,7 @@
false
false
false
+ 0
1979
@@ -15838,6 +17817,7 @@
false
false
false
+ 0
1980
@@ -15846,6 +17826,7 @@
false
false
false
+ 0
1981
@@ -15854,6 +17835,7 @@
false
false
false
+ 0
1982
@@ -15862,6 +17844,7 @@
false
false
false
+ 0
1983
@@ -15870,6 +17853,7 @@
false
false
false
+ 0
1984
@@ -15878,6 +17862,7 @@
false
false
false
+ 0
1985
@@ -15886,6 +17871,7 @@
false
false
false
+ 0
1986
@@ -15894,6 +17880,7 @@
false
false
false
+ 0
1987
@@ -15902,6 +17889,7 @@
false
false
false
+ 0
1988
@@ -15910,6 +17898,7 @@
false
false
false
+ 0
1989
@@ -15918,6 +17907,7 @@
false
false
false
+ 0
1990
@@ -15926,6 +17916,7 @@
false
false
false
+ 0
1991
@@ -15934,6 +17925,7 @@
false
false
false
+ 0
1992
@@ -15942,6 +17934,7 @@
false
false
false
+ 0
1993
@@ -15950,6 +17943,7 @@
false
false
false
+ 0
1994
@@ -15958,6 +17952,7 @@
false
false
false
+ 0
1995
@@ -15966,6 +17961,7 @@
false
false
false
+ 0
1996
@@ -15974,6 +17970,7 @@
false
false
false
+ 0
1997
@@ -15982,6 +17979,7 @@
false
false
false
+ 0
1998
@@ -15990,6 +17988,7 @@
false
false
false
+ 0
1999
@@ -15998,6 +17997,7 @@
false
false
false
+ 0
2000
@@ -16006,6 +18006,7 @@
false
false
false
+ 0
2001
@@ -16014,6 +18015,7 @@
false
false
false
+ 0
2002
@@ -16022,6 +18024,7 @@
false
false
false
+ 0
2003
@@ -16030,6 +18033,7 @@
false
false
false
+ 0
2004
@@ -16038,6 +18042,7 @@
false
false
false
+ 0
2005
@@ -16046,6 +18051,7 @@
false
false
false
+ 0
2006
@@ -16054,6 +18060,7 @@
false
false
false
+ 0
2007
@@ -16062,6 +18069,7 @@
false
false
false
+ 0
2008
@@ -16070,6 +18078,7 @@
false
false
false
+ 0
2009
@@ -16078,6 +18087,7 @@
false
false
false
+ 0
2010
@@ -16086,6 +18096,7 @@
false
false
false
+ 0
2011
@@ -16094,6 +18105,7 @@
false
false
false
+ 0
2012
@@ -16102,6 +18114,7 @@
false
false
false
+ 0
2013
@@ -16110,6 +18123,7 @@
false
false
false
+ 0
2014
@@ -16118,6 +18132,7 @@
false
false
false
+ 0
2015
@@ -16126,6 +18141,7 @@
false
false
false
+ 0
2016
@@ -16134,6 +18150,7 @@
false
false
false
+ 0
2017
@@ -16142,6 +18159,7 @@
false
false
false
+ 0
2018
@@ -16150,6 +18168,7 @@
false
false
false
+ 0
2019
@@ -16158,6 +18177,7 @@
false
false
false
+ 0
2020
@@ -16166,6 +18186,7 @@
false
false
false
+ 0
2021
@@ -16174,6 +18195,7 @@
false
false
false
+ 0
2022
@@ -16182,6 +18204,7 @@
false
false
false
+ 0
2023
@@ -16190,6 +18213,7 @@
false
false
false
+ 0
2024
@@ -16198,6 +18222,7 @@
false
false
false
+ 0
2025
@@ -16206,6 +18231,7 @@
false
false
false
+ 0
2026
@@ -16214,6 +18240,7 @@
false
false
false
+ 0
2027
@@ -16222,6 +18249,7 @@
false
false
false
+ 0
2028
@@ -16230,6 +18258,7 @@
false
false
false
+ 0
2029
@@ -16238,6 +18267,7 @@
false
false
false
+ 0
2030
@@ -16246,6 +18276,7 @@
false
false
false
+ 0
2031
@@ -16254,6 +18285,7 @@
false
false
false
+ 0
2032
@@ -16262,6 +18294,7 @@
false
false
false
+ 0
2033
@@ -16270,6 +18303,7 @@
false
false
false
+ 0
2034
@@ -16278,6 +18312,7 @@
false
false
false
+ 0
2035
@@ -16286,6 +18321,7 @@
false
false
false
+ 0
2036
@@ -16294,6 +18330,7 @@
false
false
false
+ 0
2037
@@ -16302,6 +18339,7 @@
false
false
false
+ 0
2038
@@ -16310,6 +18348,7 @@
false
false
false
+ 0
2039
@@ -16318,6 +18357,7 @@
false
false
false
+ 0
2040
@@ -16326,6 +18366,7 @@
false
false
false
+ 0
2041
@@ -16334,6 +18375,7 @@
false
false
false
+ 0
2042
@@ -16342,6 +18384,7 @@
false
false
false
+ 0
2043
@@ -16350,6 +18393,7 @@
false
false
false
+ 0
2044
@@ -16358,6 +18402,7 @@
false
false
false
+ 0
2045
@@ -16366,6 +18411,7 @@
false
false
false
+ 0
2046
@@ -16374,6 +18420,7 @@
false
false
false
+ 0
2047
@@ -16382,6 +18429,7 @@
false
false
false
+ 0
2048
@@ -16390,6 +18438,7 @@
false
false
false
+ 0
2049
@@ -16398,6 +18447,7 @@
false
false
false
+ 0
2050
@@ -16406,6 +18456,7 @@
false
false
false
+ 0
2051
@@ -16414,6 +18465,7 @@
false
false
false
+ 0
2052
@@ -16422,6 +18474,7 @@
false
false
false
+ 0
2053
@@ -16430,6 +18483,7 @@
false
false
false
+ 0
2054
@@ -16438,6 +18492,7 @@
false
false
false
+ 0
2055
@@ -16446,6 +18501,7 @@
false
false
false
+ 0
2056
@@ -16454,6 +18510,7 @@
false
false
false
+ 0
2057
@@ -16462,6 +18519,7 @@
false
false
false
+ 0
2058
@@ -16470,6 +18528,7 @@
false
false
false
+ 0
2059
@@ -16478,6 +18537,7 @@
false
false
false
+ 0
2060
@@ -16486,6 +18546,7 @@
false
false
false
+ 0
2061
@@ -16494,6 +18555,7 @@
false
false
false
+ 0
2062
@@ -16502,6 +18564,7 @@
false
false
false
+ 0
2063
@@ -16510,6 +18573,7 @@
false
false
false
+ 0
2064
@@ -16518,6 +18582,7 @@
false
false
false
+ 0
2065
@@ -16526,6 +18591,7 @@
false
false
false
+ 0
2066
@@ -16534,6 +18600,7 @@
false
false
false
+ 0
2067
@@ -16542,6 +18609,7 @@
false
false
false
+ 0
2068
@@ -16550,6 +18618,7 @@
false
false
false
+ 0
2069
@@ -16558,6 +18627,7 @@
false
false
false
+ 0
2070
@@ -16566,6 +18636,7 @@
false
false
false
+ 0
2071
@@ -16574,6 +18645,7 @@
false
false
false
+ 0
2072
@@ -16582,6 +18654,7 @@
false
false
false
+ 0
2073
@@ -16590,6 +18663,7 @@
false
false
false
+ 0
2074
@@ -16598,6 +18672,7 @@
false
false
false
+ 0
2075
@@ -16606,6 +18681,7 @@
false
false
false
+ 0
2076
@@ -16614,6 +18690,7 @@
false
false
false
+ 0
2077
@@ -16622,6 +18699,7 @@
false
false
false
+ 0
2078
@@ -16630,6 +18708,7 @@
false
false
false
+ 0
2079
@@ -16638,6 +18717,7 @@
false
false
false
+ 0
2080
@@ -16646,6 +18726,7 @@
false
false
false
+ 0
2081
@@ -16654,6 +18735,7 @@
false
false
false
+ 0
2082
@@ -16662,6 +18744,7 @@
false
false
false
+ 0
2083
@@ -16670,6 +18753,7 @@
false
false
false
+ 0
2084
@@ -16678,6 +18762,7 @@
false
false
false
+ 0
2085
@@ -16686,6 +18771,7 @@
false
false
false
+ 0
2086
@@ -16694,6 +18780,7 @@
false
false
false
+ 0
2087
@@ -16702,6 +18789,7 @@
false
false
false
+ 0
2088
@@ -16710,6 +18798,7 @@
false
false
false
+ 0
2089
@@ -16718,6 +18807,7 @@
false
false
false
+ 0
2090
@@ -16726,6 +18816,7 @@
false
false
false
+ 0
2091
@@ -16734,6 +18825,7 @@
false
false
false
+ 0
2092
@@ -16742,6 +18834,7 @@
false
false
false
+ 0
2093
@@ -16750,6 +18843,7 @@
false
false
false
+ 0
2094
@@ -16758,6 +18852,7 @@
false
false
false
+ 0
2095
@@ -16766,6 +18861,7 @@
false
false
false
+ 0
2096
@@ -16774,6 +18870,7 @@
false
false
false
+ 0
2097
@@ -16782,6 +18879,7 @@
false
false
false
+ 0
2098
@@ -16790,6 +18888,7 @@
false
false
false
+ 0
2099
@@ -16798,6 +18897,7 @@
false
false
false
+ 0
2100
@@ -16806,6 +18906,7 @@
false
false
false
+ 0
2101
@@ -16814,6 +18915,7 @@
false
false
false
+ 0
2102
@@ -16822,6 +18924,7 @@
false
false
false
+ 0
2103
@@ -16830,6 +18933,7 @@
false
false
false
+ 0
2104
@@ -16838,6 +18942,7 @@
false
false
false
+ 0
2105
@@ -16846,6 +18951,7 @@
false
false
false
+ 0
2106
@@ -16854,6 +18960,7 @@
false
false
false
+ 0
2107
@@ -16862,6 +18969,7 @@
false
false
false
+ 0
2108
@@ -16870,6 +18978,7 @@
false
false
false
+ 0
2109
@@ -16878,6 +18987,7 @@
false
false
false
+ 0
2110
@@ -16886,6 +18996,7 @@
false
false
false
+ 0
2111
@@ -16894,6 +19005,7 @@
false
false
false
+ 0
2112
@@ -16902,6 +19014,7 @@
false
false
false
+ 0
2113
@@ -16910,6 +19023,7 @@
false
false
false
+ 0
2114
@@ -16918,6 +19032,7 @@
false
false
false
+ 0
2115
@@ -16926,6 +19041,7 @@
false
false
false
+ 0
2116
@@ -16934,6 +19050,7 @@
false
false
false
+ 0
2117
@@ -16942,6 +19059,7 @@
false
false
false
+ 0
2118
@@ -16950,6 +19068,7 @@
false
false
false
+ 0
2119
@@ -16958,6 +19077,7 @@
false
false
false
+ 0
2120
@@ -16966,6 +19086,7 @@
false
false
false
+ 0
2121
@@ -16974,6 +19095,7 @@
false
false
false
+ 0
2122
@@ -16982,6 +19104,7 @@
false
false
false
+ 0
2123
@@ -16990,6 +19113,7 @@
false
false
false
+ 0
2124
@@ -16998,6 +19122,7 @@
false
false
false
+ 0
2125
@@ -17006,6 +19131,7 @@
false
false
false
+ 0
2126
@@ -17014,6 +19140,7 @@
false
false
false
+ 0
2127
@@ -17022,6 +19149,7 @@
false
false
false
+ 0
2128
@@ -17030,6 +19158,7 @@
false
false
false
+ 0
2129
@@ -17038,6 +19167,7 @@
false
false
false
+ 0
2130
@@ -17046,6 +19176,7 @@
false
false
false
+ 0
2131
@@ -17054,6 +19185,7 @@
false
false
false
+ 0
2132
@@ -17062,6 +19194,7 @@
false
false
false
+ 0
2133
@@ -17070,6 +19203,7 @@
false
false
false
+ 0
2134
@@ -17078,6 +19212,7 @@
false
false
false
+ 0
2135
@@ -17086,6 +19221,7 @@
false
false
false
+ 0
2136
@@ -17094,6 +19230,7 @@
false
false
false
+ 0
2137
@@ -17102,6 +19239,7 @@
false
false
false
+ 0
2138
@@ -17110,6 +19248,7 @@
false
false
false
+ 0
2139
@@ -17118,6 +19257,7 @@
false
false
false
+ 0
2140
@@ -17126,6 +19266,7 @@
false
false
false
+ 0
2141
@@ -17134,6 +19275,7 @@
false
false
false
+ 0
2142
@@ -17142,6 +19284,7 @@
false
false
false
+ 0
2143
@@ -17150,6 +19293,7 @@
false
false
false
+ 0
2144
@@ -17158,6 +19302,7 @@
false
false
false
+ 0
2145
@@ -17166,6 +19311,7 @@
false
false
false
+ 0
2146
@@ -17174,6 +19320,7 @@
false
false
false
+ 0
2147
@@ -17182,6 +19329,7 @@
false
false
false
+ 0
2148
@@ -17190,6 +19338,7 @@
false
false
false
+ 0
2149
@@ -17198,6 +19347,7 @@
false
false
false
+ 0
2150
@@ -17206,6 +19356,7 @@
false
false
false
+ 0
2151
@@ -17214,6 +19365,7 @@
false
false
false
+ 0
2152
@@ -17222,6 +19374,7 @@
false
false
false
+ 0
2153
@@ -17230,6 +19383,7 @@
false
false
false
+ 0
2154
@@ -17238,6 +19392,7 @@
false
false
false
+ 0
2155
@@ -17246,6 +19401,7 @@
false
false
false
+ 0
2156
@@ -17254,6 +19410,7 @@
false
false
false
+ 0
2157
@@ -17262,6 +19419,7 @@
false
false
false
+ 0
2158
@@ -17270,6 +19428,7 @@
false
false
false
+ 0
2159
@@ -17278,6 +19437,7 @@
false
false
false
+ 0
2160
@@ -17286,6 +19446,7 @@
false
false
false
+ 0
2161
@@ -17294,6 +19455,7 @@
false
false
false
+ 0
2162
@@ -17302,6 +19464,7 @@
false
false
false
+ 0
2163
@@ -17310,6 +19473,7 @@
false
false
false
+ 0
2164
@@ -17318,6 +19482,7 @@
false
false
false
+ 0
2165
@@ -17326,6 +19491,7 @@
false
false
false
+ 0
2166
@@ -17334,6 +19500,7 @@
false
false
false
+ 0
2167
@@ -17342,6 +19509,7 @@
false
false
false
+ 0
2168
@@ -17350,6 +19518,7 @@
false
false
false
+ 0
2169
@@ -17358,6 +19527,7 @@
false
false
false
+ 0
2170
@@ -17366,6 +19536,7 @@
false
false
false
+ 0
2171
@@ -17374,6 +19545,7 @@
false
false
false
+ 0
2172
@@ -17382,6 +19554,7 @@
false
false
false
+ 0
2173
@@ -17390,6 +19563,7 @@
false
false
false
+ 0
2174
@@ -17398,6 +19572,7 @@
false
false
false
+ 0
2175
@@ -17406,6 +19581,7 @@
false
false
false
+ 0
2176
@@ -17414,6 +19590,7 @@
false
false
false
+ 0
2177
@@ -17422,6 +19599,7 @@
false
false
false
+ 0
2178
@@ -17430,6 +19608,7 @@
false
false
false
+ 0
2179
@@ -17438,6 +19617,7 @@
false
false
false
+ 0
2180
@@ -17446,6 +19626,7 @@
false
false
false
+ 0
2181
@@ -17454,6 +19635,7 @@
false
false
false
+ 0
2182
@@ -17462,6 +19644,7 @@
false
false
false
+ 0
2183
@@ -17470,6 +19653,7 @@
false
false
false
+ 0
2184
@@ -17478,6 +19662,7 @@
false
false
false
+ 0
2185
@@ -17486,6 +19671,7 @@
false
false
false
+ 0
2186
@@ -17494,6 +19680,7 @@
false
false
false
+ 0
2187
@@ -17502,6 +19689,7 @@
false
false
false
+ 0
2188
@@ -17510,6 +19698,7 @@
false
false
false
+ 0
2189
@@ -17518,6 +19707,7 @@
false
false
false
+ 0
2190
@@ -17526,6 +19716,7 @@
false
false
false
+ 0
2191
@@ -17534,6 +19725,7 @@
false
false
false
+ 0
2192
@@ -17542,6 +19734,7 @@
false
false
false
+ 0
2193
@@ -17550,6 +19743,7 @@
false
false
false
+ 0
2194
@@ -17558,6 +19752,7 @@
false
false
false
+ 0
2195
@@ -17566,6 +19761,7 @@
false
false
false
+ 0
2196
@@ -17574,6 +19770,7 @@
false
false
false
+ 0
2197
@@ -17582,6 +19779,7 @@
false
false
false
+ 0
2198
@@ -17590,6 +19788,7 @@
false
false
false
+ 0
2199
@@ -17598,6 +19797,7 @@
false
false
false
+ 0
2200
@@ -17606,6 +19806,7 @@
false
false
false
+ 0
2201
@@ -17614,6 +19815,7 @@
false
false
false
+ 0
2202
@@ -17622,6 +19824,7 @@
false
false
false
+ 0
2203
@@ -17630,6 +19833,7 @@
false
false
false
+ 0
2204
@@ -17638,6 +19842,7 @@
false
false
false
+ 0
2205
@@ -17646,6 +19851,7 @@
false
false
false
+ 0
2206
@@ -17654,6 +19860,7 @@
false
false
false
+ 0
2207
@@ -17662,6 +19869,7 @@
false
false
false
+ 0
2208
@@ -17670,6 +19878,7 @@
false
false
false
+ 0
2209
@@ -17678,6 +19887,7 @@
false
false
false
+ 0
2210
@@ -17686,6 +19896,7 @@
false
false
false
+ 0
2211
@@ -17694,6 +19905,7 @@
false
false
false
+ 0
2212
@@ -17702,6 +19914,7 @@
false
false
false
+ 0
2213
@@ -17710,6 +19923,7 @@
false
false
false
+ 0
2214
@@ -17718,6 +19932,7 @@
false
false
false
+ 0
2215
@@ -17726,6 +19941,7 @@
false
false
false
+ 0
2216
@@ -17734,6 +19950,7 @@
false
false
false
+ 0
2217
@@ -17742,6 +19959,7 @@
false
false
false
+ 0
2218
@@ -17750,6 +19968,7 @@
false
false
false
+ 0
2219
@@ -17758,6 +19977,7 @@
false
false
false
+ 0
2220
@@ -17766,6 +19986,7 @@
false
false
false
+ 0
2221
@@ -17774,6 +19995,7 @@
false
false
false
+ 0
2222
@@ -17782,6 +20004,7 @@
false
false
false
+ 0
2223
@@ -17790,6 +20013,7 @@
false
false
false
+ 0
2224
@@ -17798,6 +20022,7 @@
false
false
false
+ 0
2225
@@ -17806,6 +20031,7 @@
false
false
false
+ 0
2226
@@ -17814,6 +20040,7 @@
false
false
false
+ 0
2227
@@ -17822,6 +20049,7 @@
false
false
false
+ 0
2228
@@ -17830,6 +20058,7 @@
false
false
false
+ 0
2229
@@ -17838,6 +20067,7 @@
false
false
false
+ 0
2230
@@ -17846,6 +20076,7 @@
false
false
false
+ 0
2231
@@ -17854,6 +20085,7 @@
false
false
false
+ 0
2232
@@ -17862,6 +20094,7 @@
false
false
false
+ 0
2233
@@ -17870,6 +20103,7 @@
false
false
false
+ 0
2234
@@ -17878,6 +20112,7 @@
false
false
false
+ 0
2235
@@ -17886,6 +20121,7 @@
false
false
false
+ 0
2236
@@ -17894,6 +20130,7 @@
false
false
false
+ 0
2237
@@ -17902,6 +20139,7 @@
false
false
false
+ 0
2238
@@ -17910,6 +20148,7 @@
false
false
false
+ 0
2239
@@ -17918,6 +20157,7 @@
false
false
false
+ 0
2240
@@ -17926,6 +20166,7 @@
false
false
false
+ 0
2241
@@ -17934,6 +20175,7 @@
false
false
false
+ 0
2242
@@ -17942,6 +20184,7 @@
false
false
false
+ 0
2243
@@ -17950,6 +20193,7 @@
false
false
false
+ 0
2244
@@ -17958,6 +20202,7 @@
false
false
false
+ 0
2245
@@ -17966,6 +20211,7 @@
false
false
false
+ 0
2246
@@ -17974,6 +20220,7 @@
false
false
false
+ 0
2247
@@ -17982,6 +20229,7 @@
false
false
false
+ 0
2248
@@ -17990,6 +20238,7 @@
false
false
false
+ 0
2249
@@ -17998,6 +20247,7 @@
false
false
false
+ 0
2250
@@ -18006,6 +20256,7 @@
false
false
false
+ 0
2251
@@ -18014,6 +20265,7 @@
false
false
false
+ 0
2252
@@ -18022,6 +20274,7 @@
false
false
false
+ 0
2253
@@ -18030,6 +20283,7 @@
false
false
false
+ 0
2254
@@ -18038,6 +20292,7 @@
false
false
false
+ 0
2255
@@ -18046,6 +20301,7 @@
false
false
false
+ 0
2256
@@ -18054,6 +20310,7 @@
false
false
false
+ 0
2257
@@ -18062,6 +20319,7 @@
false
false
false
+ 0
2258
@@ -18070,6 +20328,7 @@
false
false
false
+ 0
2259
@@ -18078,6 +20337,7 @@
false
false
false
+ 0
2260
@@ -18086,6 +20346,7 @@
false
false
false
+ 0
2261
@@ -18094,6 +20355,7 @@
false
false
false
+ 0
2262
@@ -18102,6 +20364,7 @@
false
false
false
+ 0
2263
@@ -18110,6 +20373,7 @@
false
false
false
+ 0
2264
@@ -18118,6 +20382,7 @@
false
false
false
+ 0
2265
@@ -18126,6 +20391,7 @@
false
false
false
+ 0
2266
@@ -18134,6 +20400,7 @@
false
false
false
+ 0
2267
@@ -18142,6 +20409,7 @@
false
false
false
+ 0
2268
@@ -18150,6 +20418,7 @@
false
false
false
+ 0
2269
@@ -18158,6 +20427,7 @@
false
false
false
+ 0
2270
@@ -18166,6 +20436,7 @@
false
false
false
+ 0
2271
@@ -18174,6 +20445,7 @@
false
false
false
+ 0
2272
@@ -18182,6 +20454,7 @@
false
false
false
+ 0
2273
@@ -18190,6 +20463,7 @@
false
false
false
+ 0
2274
@@ -18198,6 +20472,7 @@
false
false
false
+ 0
2275
@@ -18206,6 +20481,7 @@
false
false
false
+ 0
2276
@@ -18214,6 +20490,7 @@
false
false
false
+ 0
2277
@@ -18222,6 +20499,7 @@
false
false
false
+ 0
2278
@@ -18230,6 +20508,7 @@
false
false
false
+ 0
2279
@@ -18238,6 +20517,7 @@
false
false
false
+ 0
2280
@@ -18246,6 +20526,7 @@
false
false
false
+ 0
2281
@@ -18254,6 +20535,7 @@
false
false
false
+ 0
2282
@@ -18262,6 +20544,7 @@
false
false
false
+ 0
2283
@@ -18270,6 +20553,7 @@
false
false
false
+ 0
2284
@@ -18278,6 +20562,7 @@
false
false
false
+ 0
2285
@@ -18286,6 +20571,7 @@
false
false
false
+ 0
2286
@@ -18294,6 +20580,7 @@
false
false
false
+ 0
2287
@@ -18302,6 +20589,7 @@
false
false
false
+ 0
2288
@@ -18310,6 +20598,7 @@
false
false
false
+ 0
2289
@@ -18318,6 +20607,7 @@
false
false
false
+ 0
2290
@@ -18326,6 +20616,7 @@
false
false
false
+ 0
2291
@@ -18334,6 +20625,7 @@
false
false
false
+ 0
2292
@@ -18342,6 +20634,7 @@
false
false
false
+ 0
2293
@@ -18350,6 +20643,7 @@
false
false
false
+ 0
2294
@@ -18358,6 +20652,7 @@
false
false
false
+ 0
2295
@@ -18366,6 +20661,7 @@
false
false
false
+ 0
2296
@@ -18374,6 +20670,7 @@
false
false
false
+ 0
2297
@@ -18382,6 +20679,7 @@
false
false
false
+ 0
2298
@@ -18390,6 +20688,7 @@
false
false
false
+ 0
2299
@@ -18398,6 +20697,7 @@
false
false
false
+ 0
2300
@@ -18406,6 +20706,7 @@
false
false
false
+ 0
2301
@@ -18414,6 +20715,7 @@
false
false
false
+ 0
2302
@@ -18422,6 +20724,7 @@
false
false
false
+ 0
2303
@@ -18430,6 +20733,7 @@
false
false
false
+ 0
2304
@@ -18438,6 +20742,7 @@
false
false
false
+ 0
2305
@@ -18446,6 +20751,7 @@
false
false
false
+ 0
2306
@@ -18454,6 +20760,7 @@
false
false
false
+ 0
2307
@@ -18462,6 +20769,7 @@
false
false
false
+ 0
2308
@@ -18470,6 +20778,7 @@
false
false
false
+ 0
2309
@@ -18478,6 +20787,7 @@
false
false
false
+ 0
2310
@@ -18486,6 +20796,7 @@
false
false
false
+ 0
2311
@@ -18494,6 +20805,7 @@
false
false
false
+ 0
2312
@@ -18502,6 +20814,7 @@
false
false
false
+ 0
2313
@@ -18510,6 +20823,7 @@
false
false
false
+ 0
2314
@@ -18518,6 +20832,7 @@
false
false
false
+ 0
2315
@@ -18526,6 +20841,7 @@
false
false
false
+ 0
2316
@@ -18534,6 +20850,7 @@
false
false
false
+ 0
2317
@@ -18542,6 +20859,7 @@
false
false
false
+ 0
2318
@@ -18550,6 +20868,7 @@
false
false
false
+ 0
2319
@@ -18558,6 +20877,7 @@
false
false
false
+ 0
2320
@@ -18566,6 +20886,7 @@
false
false
false
+ 0
2321
@@ -18574,6 +20895,7 @@
false
false
false
+ 0
2322
@@ -18582,6 +20904,7 @@
false
false
false
+ 0
2323
@@ -18590,6 +20913,7 @@
false
false
false
+ 0
2324
@@ -18598,6 +20922,7 @@
false
false
false
+ 0
2325
@@ -18606,6 +20931,7 @@
false
false
false
+ 0
2326
@@ -18614,6 +20940,7 @@
false
false
false
+ 0
2327
@@ -18622,6 +20949,7 @@
false
false
false
+ 0
2328
@@ -18630,6 +20958,7 @@
false
false
false
+ 0
2329
@@ -18638,6 +20967,7 @@
false
false
false
+ 0
2330
@@ -18646,6 +20976,7 @@
false
false
false
+ 0
2331
@@ -18654,6 +20985,7 @@
false
false
false
+ 0
2332
@@ -18662,6 +20994,7 @@
false
false
false
+ 0
2333
@@ -18670,6 +21003,7 @@
false
false
false
+ 0
2334
@@ -18678,6 +21012,7 @@
false
false
false
+ 0
2335
@@ -18686,6 +21021,7 @@
false
false
false
+ 0
2336
@@ -18694,6 +21030,7 @@
false
false
false
+ 0
2337
@@ -18702,6 +21039,7 @@
false
false
false
+ 0
2338
@@ -18710,6 +21048,7 @@
false
false
false
+ 0
2339
@@ -18718,6 +21057,7 @@
false
false
false
+ 0
2340
@@ -18726,6 +21066,7 @@
false
false
false
+ 0
2341
@@ -18734,6 +21075,7 @@
false
false
false
+ 0
2342
@@ -18742,6 +21084,7 @@
false
false
false
+ 0
2343
@@ -18750,6 +21093,7 @@
false
false
false
+ 0
2344
@@ -18758,6 +21102,7 @@
false
false
false
+ 0
2345
@@ -18766,6 +21111,7 @@
false
false
false
+ 0
2346
@@ -18774,6 +21120,7 @@
false
false
false
+ 0
2347
@@ -18782,6 +21129,7 @@
false
false
false
+ 0
2348
@@ -18790,6 +21138,7 @@
false
false
false
+ 0
2349
@@ -18798,6 +21147,7 @@
false
false
false
+ 0
2350
@@ -18806,6 +21156,7 @@
false
false
false
+ 0
2351
@@ -18814,6 +21165,7 @@
false
false
false
+ 0
2352
@@ -18822,6 +21174,7 @@
false
false
false
+ 0
2353
@@ -18830,6 +21183,7 @@
false
false
false
+ 0
2354
@@ -18838,6 +21192,7 @@
false
false
false
+ 0
2355
@@ -18846,6 +21201,7 @@
false
false
false
+ 0
2356
@@ -18854,6 +21210,7 @@
false
false
false
+ 0
2357
@@ -18862,6 +21219,7 @@
false
false
false
+ 0
2358
@@ -18870,6 +21228,7 @@
false
false
false
+ 0
2359
@@ -18878,6 +21237,7 @@
false
false
false
+ 0
2360
@@ -18886,6 +21246,7 @@
false
false
false
+ 0
2361
@@ -18894,6 +21255,7 @@
false
false
false
+ 0
2362
@@ -18902,6 +21264,7 @@
false
false
false
+ 0
2363
@@ -18910,6 +21273,7 @@
false
false
false
+ 0
2364
@@ -18918,6 +21282,7 @@
false
false
false
+ 0
2365
@@ -18926,6 +21291,7 @@
false
false
false
+ 0
2366
@@ -18934,6 +21300,7 @@
false
false
false
+ 0
2367
@@ -18942,6 +21309,7 @@
false
false
false
+ 0
2368
@@ -18950,6 +21318,7 @@
false
false
false
+ 0
2369
@@ -18958,6 +21327,7 @@
false
false
false
+ 0
2370
@@ -18966,6 +21336,7 @@
false
false
false
+ 0
2371
@@ -18974,6 +21345,7 @@
false
false
false
+ 0
2372
@@ -18982,6 +21354,7 @@
false
false
false
+ 0
2373
@@ -18990,6 +21363,7 @@
false
false
false
+ 0
2374
@@ -18998,6 +21372,7 @@
false
false
false
+ 0
2375
@@ -19006,6 +21381,7 @@
false
false
false
+ 0
2376
@@ -19014,6 +21390,7 @@
false
false
false
+ 0
2377
@@ -19022,6 +21399,7 @@
false
false
false
+ 0
2378
@@ -19030,6 +21408,7 @@
false
false
false
+ 0
2379
@@ -19038,6 +21417,7 @@
false
false
false
+ 0
2380
@@ -19046,6 +21426,7 @@
false
false
false
+ 0
2381
@@ -19054,6 +21435,7 @@
false
false
false
+ 0
2382
@@ -19062,6 +21444,7 @@
false
false
false
+ 0
2383
@@ -19070,6 +21453,7 @@
false
false
false
+ 0
2384
@@ -19078,6 +21462,7 @@
false
false
false
+ 0
2385
@@ -19086,6 +21471,7 @@
false
false
false
+ 0
2386
@@ -19094,6 +21480,7 @@
false
false
false
+ 0
2387
@@ -19102,6 +21489,7 @@
false
false
false
+ 0
2388
@@ -19110,6 +21498,7 @@
false
false
false
+ 0
2389
@@ -19118,6 +21507,7 @@
false
false
false
+ 0
2390
@@ -19126,6 +21516,7 @@
false
false
false
+ 0
2391
@@ -19134,6 +21525,7 @@
false
false
false
+ 0
2392
@@ -19142,6 +21534,7 @@
false
false
false
+ 0
2393
@@ -19150,6 +21543,7 @@
false
false
false
+ 0
2394
@@ -19158,6 +21552,7 @@
false
false
false
+ 0
2395
@@ -19166,6 +21561,7 @@
false
false
false
+ 0
2396
@@ -19174,6 +21570,7 @@
false
false
false
+ 0
2397
@@ -19182,6 +21579,7 @@
false
false
false
+ 0
2398
@@ -19190,6 +21588,7 @@
false
false
false
+ 0
2399
@@ -19198,6 +21597,7 @@
false
false
false
+ 0
2400
@@ -19206,6 +21606,7 @@
false
false
false
+ 0
2401
@@ -19214,6 +21615,7 @@
false
false
false
+ 0
2402
@@ -19222,6 +21624,7 @@
false
false
false
+ 0
2403
@@ -19230,6 +21633,7 @@
false
false
false
+ 0
2404
@@ -19238,6 +21642,7 @@
false
false
false
+ 0
2405
@@ -19246,6 +21651,7 @@
false
false
false
+ 0
2406
@@ -19254,6 +21660,7 @@
false
false
false
+ 0
2407
@@ -19262,6 +21669,7 @@
false
false
false
+ 0
2408
@@ -19270,6 +21678,7 @@
false
false
false
+ 0
2409
@@ -19278,6 +21687,7 @@
false
false
false
+ 0
2410
@@ -19286,6 +21696,7 @@
false
false
false
+ 0
2411
@@ -19294,6 +21705,7 @@
false
false
false
+ 0
2412
@@ -19302,6 +21714,7 @@
false
false
false
+ 0
2413
@@ -19310,6 +21723,7 @@
false
false
false
+ 0
2414
@@ -19318,6 +21732,7 @@
false
false
false
+ 0
2415
@@ -19326,6 +21741,7 @@
false
false
false
+ 0
2416
@@ -19334,6 +21750,7 @@
false
false
false
+ 0
2417
@@ -19342,6 +21759,7 @@
false
false
false
+ 0
2418
@@ -19350,6 +21768,7 @@
false
false
false
+ 0
2419
@@ -19358,6 +21777,7 @@
false
false
false
+ 0
2420
@@ -19366,6 +21786,7 @@
false
false
false
+ 0
2421
@@ -19374,6 +21795,7 @@
false
false
false
+ 0
2422
@@ -19382,6 +21804,7 @@
false
false
false
+ 0
2423
@@ -19390,6 +21813,7 @@
false
false
false
+ 0
2424
@@ -19398,6 +21822,7 @@
false
false
false
+ 0
2425
@@ -19406,6 +21831,7 @@
false
false
false
+ 0
2426
@@ -19414,6 +21840,7 @@
false
false
false
+ 0
2427
@@ -19422,6 +21849,7 @@
false
false
false
+ 0
2428
@@ -19430,6 +21858,7 @@
false
false
false
+ 0
2429
@@ -19438,6 +21867,7 @@
false
false
false
+ 0
2430
@@ -19446,6 +21876,7 @@
false
false
false
+ 0
2431
@@ -19454,6 +21885,7 @@
false
false
false
+ 0
2432
@@ -19462,6 +21894,7 @@
false
false
false
+ 0
2433
@@ -19470,6 +21903,7 @@
false
false
false
+ 0
2434
@@ -19478,6 +21912,7 @@
false
false
false
+ 0
2435
@@ -19486,6 +21921,7 @@
false
false
false
+ 0
2436
@@ -19494,6 +21930,7 @@
false
false
false
+ 0
2437
@@ -19502,6 +21939,7 @@
false
false
false
+ 0
2438
@@ -19510,6 +21948,7 @@
false
false
false
+ 0
2439
@@ -19518,6 +21957,7 @@
false
false
false
+ 0
2440
@@ -19526,6 +21966,7 @@
false
false
false
+ 0
2441
@@ -19534,6 +21975,7 @@
false
false
false
+ 0
2442
@@ -19542,6 +21984,7 @@
false
false
false
+ 0
2443
@@ -19550,6 +21993,7 @@
false
false
false
+ 0
2444
@@ -19558,6 +22002,7 @@
false
false
false
+ 0
2445
@@ -19566,6 +22011,7 @@
false
false
false
+ 0
2446
@@ -19574,6 +22020,7 @@
false
false
false
+ 0
2447
@@ -19582,6 +22029,7 @@
false
false
false
+ 0
2448
@@ -19590,6 +22038,7 @@
false
false
false
+ 0
2449
@@ -19598,6 +22047,7 @@
false
false
false
+ 0
2450
@@ -19606,6 +22056,7 @@
false
false
false
+ 0
2451
@@ -19614,6 +22065,7 @@
false
false
false
+ 0
2452
@@ -19622,6 +22074,7 @@
false
false
false
+ 0
2453
@@ -19630,6 +22083,7 @@
false
false
false
+ 0
2454
@@ -19638,6 +22092,7 @@
false
false
false
+ 0
2455
@@ -19646,6 +22101,7 @@
false
false
false
+ 0
2456
@@ -19654,6 +22110,7 @@
false
false
false
+ 0
2457
@@ -19662,6 +22119,7 @@
false
false
false
+ 0
2458
@@ -19670,6 +22128,7 @@
false
false
false
+ 0
2459
@@ -19678,6 +22137,7 @@
false
false
false
+ 0
2460
@@ -19686,6 +22146,7 @@
false
false
false
+ 0
2461
@@ -19694,6 +22155,7 @@
false
false
false
+ 0
2462
@@ -19702,6 +22164,7 @@
false
false
false
+ 0
2463
@@ -19710,6 +22173,7 @@
false
false
false
+ 0
2464
@@ -19718,6 +22182,7 @@
false
false
false
+ 0
2465
@@ -19726,6 +22191,7 @@
false
false
false
+ 0
2466
@@ -19734,6 +22200,7 @@
false
false
false
+ 0
2467
@@ -19742,6 +22209,7 @@
false
false
false
+ 0
2468
@@ -19750,6 +22218,7 @@
false
false
false
+ 0
2469
@@ -19758,6 +22227,7 @@
false
false
false
+ 0
2470
@@ -19766,6 +22236,7 @@
false
false
false
+ 0
2471
@@ -19774,6 +22245,7 @@
false
false
false
+ 0
2472
@@ -19782,6 +22254,7 @@
false
false
false
+ 0
2473
@@ -19790,6 +22263,7 @@
false
false
false
+ 0
2474
@@ -19798,6 +22272,7 @@
false
false
false
+ 0
2475
@@ -19806,6 +22281,7 @@
false
false
false
+ 0
2476
@@ -19814,6 +22290,7 @@
false
false
false
+ 0
2477
@@ -19822,6 +22299,7 @@
false
false
false
+ 0
2478
@@ -19830,6 +22308,7 @@
false
false
false
+ 0
2479
@@ -19838,6 +22317,7 @@
false
false
false
+ 0
2480
@@ -19846,6 +22326,7 @@
false
false
false
+ 0
2481
@@ -19854,6 +22335,7 @@
false
false
false
+ 0
2482
@@ -19862,6 +22344,7 @@
false
false
false
+ 0
2483
@@ -19870,6 +22353,7 @@
false
false
false
+ 0
2484
@@ -19878,6 +22362,7 @@
false
false
false
+ 0
2485
@@ -19886,6 +22371,7 @@
false
false
false
+ 0
2486
@@ -19894,6 +22380,7 @@
false
false
false
+ 0
2487
@@ -19902,6 +22389,7 @@
false
false
false
+ 0
2488
@@ -19910,6 +22398,7 @@
false
false
false
+ 0
2489
@@ -19918,6 +22407,7 @@
false
false
false
+ 0
2490
@@ -19926,6 +22416,7 @@
false
false
false
+ 0
2491
@@ -19934,6 +22425,7 @@
false
false
false
+ 0
2492
@@ -19942,6 +22434,7 @@
false
false
false
+ 0
2493
@@ -19950,6 +22443,7 @@
false
false
false
+ 0
2494
@@ -19958,6 +22452,7 @@
false
false
false
+ 0
2495
@@ -19966,6 +22461,7 @@
false
false
false
+ 0
2496
@@ -19974,6 +22470,7 @@
false
false
false
+ 0
2497
@@ -19982,6 +22479,7 @@
false
false
false
+ 0
2498
@@ -19990,6 +22488,7 @@
false
false
false
+ 0
2499
@@ -19998,6 +22497,7 @@
false
false
false
+ 0
2500
@@ -20006,6 +22506,7 @@
false
false
false
+ 0
2501
@@ -20014,6 +22515,7 @@
false
false
false
+ 0
2502
@@ -20022,6 +22524,7 @@
false
false
false
+ 0
2503
@@ -20030,6 +22533,7 @@
false
false
false
+ 0
2504
@@ -20038,6 +22542,7 @@
false
false
false
+ 0
2505
@@ -20046,6 +22551,7 @@
false
false
false
+ 0
2506
@@ -20054,6 +22560,7 @@
false
false
false
+ 0
2507
@@ -20062,6 +22569,7 @@
false
false
false
+ 0
2508
@@ -20070,6 +22578,7 @@
false
false
false
+ 0
2509
@@ -20078,6 +22587,7 @@
false
false
false
+ 0
2510
@@ -20086,6 +22596,7 @@
false
false
false
+ 0
2511
@@ -20094,6 +22605,7 @@
false
false
false
+ 0
2512
@@ -20102,6 +22614,7 @@
false
false
false
+ 0
2513
@@ -20110,6 +22623,7 @@
false
false
false
+ 0
2514
@@ -20118,6 +22632,7 @@
false
false
false
+ 0
2515
@@ -20126,6 +22641,7 @@
false
false
false
+ 0
2516
@@ -20134,6 +22650,7 @@
false
false
false
+ 0
2517
@@ -20142,6 +22659,7 @@
false
false
false
+ 0
2518
@@ -20150,6 +22668,7 @@
false
false
false
+ 0
2519
@@ -20158,6 +22677,7 @@
false
false
false
+ 0
2520
@@ -20166,6 +22686,7 @@
false
false
false
+ 0
2521
@@ -20174,6 +22695,7 @@
false
false
false
+ 0
2522
@@ -20182,6 +22704,7 @@
false
false
false
+ 0
2523
@@ -20190,6 +22713,7 @@
false
false
false
+ 0
2524
@@ -20198,6 +22722,7 @@
false
false
false
+ 0
2525
@@ -20206,6 +22731,7 @@
false
false
false
+ 0
2526
@@ -20214,6 +22740,7 @@
false
false
false
+ 0
2527
@@ -20222,6 +22749,7 @@
false
false
false
+ 0
2528
@@ -20230,6 +22758,7 @@
false
false
false
+ 0
2529
@@ -20238,6 +22767,7 @@
false
false
false
+ 0
2530
@@ -20246,6 +22776,7 @@
false
false
false
+ 0
2531
@@ -20254,6 +22785,7 @@
false
false
false
+ 0
2532
@@ -20262,6 +22794,7 @@
false
false
false
+ 0
2533
@@ -20270,6 +22803,7 @@
false
false
false
+ 0
2534
@@ -20278,6 +22812,7 @@
false
false
false
+ 0
2535
@@ -20286,6 +22821,7 @@
false
false
false
+ 0
2536
@@ -20294,6 +22830,7 @@
false
false
false
+ 0
2537
@@ -20302,6 +22839,7 @@
false
false
false
+ 0
2538
@@ -20310,6 +22848,7 @@
false
false
false
+ 0
2539
@@ -20318,6 +22857,7 @@
false
false
false
+ 0
2540
@@ -20326,6 +22866,7 @@
false
false
false
+ 0
2541
@@ -20334,6 +22875,7 @@
false
false
false
+ 0
2542
@@ -20342,6 +22884,7 @@
false
false
false
+ 0
2543
@@ -20350,6 +22893,7 @@
false
false
false
+ 0
2544
@@ -20358,6 +22902,7 @@
false
false
false
+ 0
2545
@@ -20366,6 +22911,7 @@
false
false
false
+ 0
2546
@@ -20374,6 +22920,7 @@
false
false
false
+ 0
2547
@@ -20382,6 +22929,7 @@
false
false
false
+ 0
2548
@@ -20390,6 +22938,7 @@
false
false
false
+ 0
2549
@@ -20398,6 +22947,7 @@
false
false
false
+ 0
2550
@@ -20406,6 +22956,7 @@
false
false
false
+ 0
2551
@@ -20414,6 +22965,7 @@
false
false
false
+ 0
2552
@@ -20422,6 +22974,7 @@
false
false
false
+ 0
2553
@@ -20430,6 +22983,7 @@
false
false
false
+ 0
2554
@@ -20438,6 +22992,7 @@
false
false
false
+ 0
2555
@@ -20446,6 +23001,7 @@
false
false
false
+ 0
2556
@@ -20454,6 +23010,7 @@
false
false
false
+ 0
2557
@@ -20462,6 +23019,7 @@
false
false
false
+ 0
2558
@@ -20470,6 +23028,7 @@
false
false
false
+ 0
2559
@@ -20478,6 +23037,7 @@
false
false
false
+ 0
2560
@@ -20486,6 +23046,7 @@
false
false
false
+ 0
2561
@@ -20494,6 +23055,7 @@
false
false
false
+ 0
2562
@@ -20502,6 +23064,7 @@
false
false
false
+ 0
2563
@@ -20510,6 +23073,7 @@
false
false
false
+ 0
2564
@@ -20518,6 +23082,7 @@
false
false
false
+ 0
2565
@@ -20526,6 +23091,7 @@
false
false
false
+ 0
2566
@@ -20534,6 +23100,7 @@
false
false
false
+ 0
2567
@@ -20542,6 +23109,7 @@
false
false
false
+ 0
2568
@@ -20550,6 +23118,7 @@
false
false
false
+ 0
2569
@@ -20558,6 +23127,7 @@
false
false
false
+ 0
2570
@@ -20566,6 +23136,7 @@
false
false
false
+ 0
2571
@@ -20574,6 +23145,7 @@
false
false
false
+ 0
2572
@@ -20582,6 +23154,7 @@
false
false
false
+ 0
2573
@@ -20590,6 +23163,7 @@
false
false
false
+ 0
2574
@@ -20598,6 +23172,7 @@
false
false
false
+ 0
2575
@@ -20606,6 +23181,7 @@
false
false
false
+ 0
2576
@@ -20614,6 +23190,7 @@
false
false
false
+ 0
2577
@@ -20622,6 +23199,7 @@
false
false
false
+ 0
2578
@@ -20630,6 +23208,7 @@
false
false
false
+ 0
2579
@@ -20638,6 +23217,7 @@
false
false
false
+ 0
2580
@@ -20646,6 +23226,7 @@
false
false
false
+ 0
2581
@@ -20654,6 +23235,7 @@
false
false
false
+ 0
2582
@@ -20662,6 +23244,7 @@
false
false
false
+ 0
2583
@@ -20670,6 +23253,7 @@
false
false
false
+ 0
2584
@@ -20678,6 +23262,7 @@
false
false
false
+ 0
2585
@@ -20686,6 +23271,7 @@
false
false
false
+ 0
2586
@@ -20694,6 +23280,7 @@
false
false
false
+ 0
2587
@@ -20702,6 +23289,7 @@
false
false
false
+ 0
2588
@@ -20710,6 +23298,7 @@
false
false
false
+ 0
2589
@@ -20718,6 +23307,7 @@
false
false
false
+ 0
2590
@@ -20726,6 +23316,7 @@
false
false
false
+ 0
2591
@@ -20734,6 +23325,7 @@
false
false
false
+ 0
2592
@@ -20742,6 +23334,7 @@
false
false
false
+ 0
2593
@@ -20750,6 +23343,7 @@
false
false
false
+ 0
2594
@@ -20758,6 +23352,7 @@
false
false
false
+ 0
2595
@@ -20766,6 +23361,7 @@
false
false
false
+ 0
2596
@@ -20774,6 +23370,7 @@
false
false
false
+ 0
2597
@@ -20782,6 +23379,7 @@
false
false
false
+ 0
2598
@@ -20790,6 +23388,7 @@
false
false
false
+ 0
2599
@@ -20798,6 +23397,7 @@
false
false
false
+ 0
2600
@@ -20806,6 +23406,7 @@
false
false
false
+ 0
2601
@@ -20814,6 +23415,7 @@
false
false
false
+ 0
2602
@@ -20822,6 +23424,7 @@
false
false
false
+ 0
2603
@@ -20830,6 +23433,7 @@
false
false
false
+ 0
2604
@@ -20838,6 +23442,7 @@
false
false
false
+ 0
2605
@@ -20846,6 +23451,7 @@
false
false
false
+ 0
2606
@@ -20854,6 +23460,7 @@
false
false
false
+ 0
2607
@@ -20862,6 +23469,7 @@
false
false
false
+ 0
2608
@@ -20870,6 +23478,7 @@
false
false
false
+ 0
2609
@@ -20878,6 +23487,7 @@
false
false
false
+ 0
2610
@@ -20886,6 +23496,7 @@
false
false
false
+ 0
2611
@@ -20894,6 +23505,7 @@
false
false
false
+ 0
2612
@@ -20902,6 +23514,7 @@
false
false
false
+ 0
2613
@@ -20910,6 +23523,7 @@
false
false
false
+ 0
2614
@@ -20918,6 +23532,7 @@
false
false
false
+ 0
2615
@@ -20926,6 +23541,7 @@
false
false
false
+ 0
2616
@@ -20934,6 +23550,7 @@
false
false
false
+ 0
2617
@@ -20942,6 +23559,7 @@
false
false
false
+ 0
2618
@@ -20950,6 +23568,7 @@
false
false
false
+ 0
2619
@@ -20958,6 +23577,7 @@
false
false
false
+ 0
2620
@@ -20966,6 +23586,7 @@
false
false
false
+ 0
2621
@@ -20974,6 +23595,7 @@
false
false
false
+ 0
2622
@@ -20982,6 +23604,7 @@
false
false
false
+ 0
2623
@@ -20990,6 +23613,7 @@
false
false
false
+ 0
2624
@@ -20998,6 +23622,7 @@
false
false
false
+ 0
2625
@@ -21006,6 +23631,7 @@
false
false
false
+ 0
2626
@@ -21014,6 +23640,7 @@
false
false
false
+ 0
2627
@@ -21022,6 +23649,7 @@
false
false
false
+ 0
2628
@@ -21030,6 +23658,7 @@
false
false
false
+ 0
2629
@@ -21038,6 +23667,7 @@
false
false
false
+ 0
2630
@@ -21046,6 +23676,7 @@
false
false
false
+ 0
2631
@@ -21054,6 +23685,7 @@
false
false
false
+ 0
2632
@@ -21062,6 +23694,7 @@
false
false
false
+ 0
2633
@@ -21070,6 +23703,7 @@
false
false
false
+ 0
2634
@@ -21078,6 +23712,7 @@
false
false
false
+ 0
2635
@@ -21086,6 +23721,7 @@
false
false
false
+ 0
2636
@@ -21094,6 +23730,7 @@
false
false
false
+ 0
2637
@@ -21102,6 +23739,7 @@
false
false
false
+ 0
2638
@@ -21110,6 +23748,7 @@
false
false
false
+ 0
2639
@@ -21118,6 +23757,7 @@
false
false
false
+ 0
2640
@@ -21126,6 +23766,7 @@
false
false
false
+ 0
2641
@@ -21134,6 +23775,7 @@
false
false
false
+ 0
2642
@@ -21142,6 +23784,7 @@
false
false
false
+ 0
2643
@@ -21150,6 +23793,7 @@
false
false
false
+ 0
2644
@@ -21158,6 +23802,7 @@
false
false
false
+ 0
2645
@@ -21166,6 +23811,7 @@
false
false
false
+ 0
2646
@@ -21174,6 +23820,7 @@
false
false
false
+ 0
2647
@@ -21182,6 +23829,7 @@
false
false
false
+ 0
2648
@@ -21190,6 +23838,7 @@
false
false
false
+ 0
2649
@@ -21198,6 +23847,7 @@
false
false
false
+ 0
2650
@@ -21206,6 +23856,7 @@
false
false
false
+ 0
2651
@@ -21214,6 +23865,7 @@
false
false
false
+ 0
2652
@@ -21222,6 +23874,7 @@
false
false
false
+ 0
2653
@@ -21230,6 +23883,7 @@
false
false
false
+ 0
2654
@@ -21238,6 +23892,7 @@
false
false
false
+ 0
2655
@@ -21246,6 +23901,7 @@
false
false
false
+ 0
2656
@@ -21254,6 +23910,7 @@
false
false
false
+ 0
2657
@@ -21262,6 +23919,7 @@
false
false
false
+ 0
2658
@@ -21270,6 +23928,7 @@
false
false
false
+ 0
2659
@@ -21278,6 +23937,7 @@
false
false
false
+ 0
2660
@@ -21286,6 +23946,7 @@
false
false
false
+ 0
2661
@@ -21294,6 +23955,7 @@
false
false
false
+ 0
2662
@@ -21302,6 +23964,7 @@
false
false
false
+ 0
2663
@@ -21310,6 +23973,7 @@
false
false
false
+ 0
2664
@@ -21318,6 +23982,7 @@
false
false
false
+ 0
2665
@@ -21326,6 +23991,7 @@
false
false
false
+ 0
2666
@@ -21334,6 +24000,7 @@
false
false
false
+ 0
2667
@@ -21342,6 +24009,7 @@
false
false
false
+ 0
2668
@@ -21350,6 +24018,7 @@
false
false
false
+ 0
2669
@@ -21358,6 +24027,7 @@
false
false
false
+ 0
2670
@@ -21366,6 +24036,7 @@
false
false
false
+ 0
2671
@@ -21374,6 +24045,7 @@
false
false
false
+ 0
2672
@@ -21382,6 +24054,7 @@
false
false
false
+ 0
2673
@@ -21390,6 +24063,7 @@
false
false
false
+ 0
2674
@@ -21398,6 +24072,7 @@
false
false
false
+ 0
2675
@@ -21406,6 +24081,7 @@
false
false
false
+ 0
2676
@@ -21414,6 +24090,7 @@
false
false
false
+ 0
2677
@@ -21422,6 +24099,7 @@
false
false
false
+ 0
2678
@@ -21430,6 +24108,7 @@
false
false
false
+ 0
2679
@@ -21438,6 +24117,7 @@
false
false
false
+ 0
2680
@@ -21446,6 +24126,7 @@
false
false
false
+ 0
2681
@@ -21454,6 +24135,7 @@
false
false
false
+ 0
2682
@@ -21462,6 +24144,7 @@
false
false
false
+ 0
2683
@@ -21470,6 +24153,7 @@
false
false
false
+ 0
2684
@@ -21478,6 +24162,7 @@
false
false
false
+ 0
2685
@@ -21486,6 +24171,7 @@
false
false
false
+ 0
2686
@@ -21494,6 +24180,7 @@
false
false
false
+ 0
2687
@@ -21502,6 +24189,7 @@
false
false
false
+ 0
2688
@@ -21510,6 +24198,7 @@
false
false
false
+ 0
2689
@@ -21518,6 +24207,7 @@
false
false
false
+ 0
2690
@@ -21526,6 +24216,7 @@
false
false
false
+ 0
2691
@@ -21534,6 +24225,7 @@
false
false
false
+ 0
2692
@@ -21542,6 +24234,7 @@
false
false
false
+ 0
2693
@@ -21550,6 +24243,7 @@
false
false
false
+ 0
2694
@@ -21558,6 +24252,7 @@
false
false
false
+ 0
2695
@@ -21566,6 +24261,7 @@
false
false
false
+ 0
2696
@@ -21574,6 +24270,7 @@
false
false
false
+ 0
2697
@@ -21582,6 +24279,7 @@
false
false
false
+ 0
2698
@@ -21590,6 +24288,7 @@
false
false
false
+ 0
2699
@@ -21598,6 +24297,7 @@
false
false
false
+ 0
2700
@@ -21606,6 +24306,7 @@
false
false
false
+ 0
2701
@@ -21614,6 +24315,7 @@
false
false
false
+ 0
2702
@@ -21622,6 +24324,7 @@
false
false
false
+ 0
2703
@@ -21630,6 +24333,7 @@
false
false
false
+ 0
2704
@@ -21638,6 +24342,7 @@
false
false
false
+ 0
2705
@@ -21646,6 +24351,7 @@
false
false
false
+ 0
2706
@@ -21654,6 +24360,7 @@
false
false
false
+ 0
2707
@@ -21662,6 +24369,7 @@
false
false
false
+ 0
2708
@@ -21670,6 +24378,7 @@
false
false
false
+ 0
2709
@@ -21678,6 +24387,7 @@
false
false
false
+ 0
2710
@@ -21686,6 +24396,7 @@
false
false
false
+ 0
2711
@@ -21694,6 +24405,7 @@
false
false
false
+ 0
2712
@@ -21702,6 +24414,7 @@
false
false
false
+ 0
2713
@@ -21710,6 +24423,7 @@
false
false
false
+ 0
2714
@@ -21718,6 +24432,7 @@
false
false
false
+ 0
2715
@@ -21726,6 +24441,7 @@
false
false
false
+ 0
2716
@@ -21734,6 +24450,7 @@
false
false
false
+ 0
2717
@@ -21742,6 +24459,7 @@
false
false
false
+ 0
2718
@@ -21750,6 +24468,7 @@
false
false
false
+ 0
2719
@@ -21758,6 +24477,7 @@
false
false
false
+ 0
2720
@@ -21766,6 +24486,7 @@
false
false
false
+ 0
2721
@@ -21774,6 +24495,7 @@
false
false
false
+ 0
2722
@@ -21782,6 +24504,7 @@
false
false
false
+ 0
2723
@@ -21790,6 +24513,7 @@
false
false
false
+ 0
2724
@@ -21798,6 +24522,7 @@
false
false
false
+ 0
2725
@@ -21806,6 +24531,7 @@
false
false
false
+ 0
2726
@@ -21814,6 +24540,7 @@
false
false
false
+ 0
2727
@@ -21822,6 +24549,7 @@
false
false
false
+ 0
2728
@@ -21830,6 +24558,7 @@
false
false
false
+ 0
2729
@@ -21838,6 +24567,7 @@
false
false
false
+ 0
2730
@@ -21846,6 +24576,7 @@
false
false
false
+ 0
2731
@@ -21854,6 +24585,7 @@
false
false
false
+ 0
2732
@@ -21862,6 +24594,7 @@
false
false
false
+ 0
2733
@@ -21870,6 +24603,7 @@
false
false
false
+ 0
2734
@@ -21878,6 +24612,7 @@
false
false
false
+ 0
2735
@@ -21886,6 +24621,7 @@
false
false
false
+ 0
2736
@@ -21894,6 +24630,7 @@
false
false
false
+ 0
2737
@@ -21902,6 +24639,7 @@
false
false
false
+ 0
2738
@@ -21910,6 +24648,7 @@
false
false
false
+ 0
2739
@@ -21918,6 +24657,7 @@
false
false
false
+ 0
2740
@@ -21926,6 +24666,7 @@
false
false
false
+ 0
2741
@@ -21934,6 +24675,7 @@
false
false
false
+ 0
2742
@@ -21942,6 +24684,7 @@
false
false
false
+ 0
2743
@@ -21950,6 +24693,7 @@
false
false
false
+ 0
2744
@@ -21958,6 +24702,7 @@
false
false
false
+ 0
2745
@@ -21966,6 +24711,7 @@
false
false
false
+ 0
2746
@@ -21974,6 +24720,7 @@
false
false
false
+ 0
2747
@@ -21982,6 +24729,7 @@
false
false
false
+ 0
2748
@@ -21990,6 +24738,7 @@
false
false
false
+ 0
2749
@@ -21998,6 +24747,7 @@
false
false
false
+ 0
2750
@@ -22006,6 +24756,7 @@
false
false
false
+ 0
2751
@@ -22014,6 +24765,7 @@
false
false
false
+ 0
2752
@@ -22022,6 +24774,7 @@
false
false
false
+ 0
2753
@@ -22030,6 +24783,7 @@
false
false
false
+ 0
2754
@@ -22038,6 +24792,7 @@
false
false
false
+ 0
2755
@@ -22046,6 +24801,7 @@
false
false
false
+ 0
2756
@@ -22054,6 +24810,7 @@
false
false
false
+ 0
2757
@@ -22062,6 +24819,7 @@
false
false
false
+ 0
2758
@@ -22070,6 +24828,7 @@
false
false
false
+ 0
2759
@@ -22078,6 +24837,7 @@
false
false
false
+ 0
2760
@@ -22086,6 +24846,7 @@
false
false
false
+ 0
2761
@@ -22094,6 +24855,7 @@
false
false
false
+ 0
2762
@@ -22102,6 +24864,7 @@
false
false
false
+ 0
2763
@@ -22110,6 +24873,7 @@
false
false
false
+ 0
2764
@@ -22118,6 +24882,7 @@
false
false
false
+ 0
2765
@@ -22126,6 +24891,7 @@
false
false
false
+ 0
2766
@@ -22134,6 +24900,7 @@
false
false
false
+ 0
2767
@@ -22142,6 +24909,7 @@
false
false
false
+ 0
2768
@@ -22150,6 +24918,7 @@
false
false
false
+ 0
2769
@@ -22158,6 +24927,7 @@
false
false
false
+ 0
2770
@@ -22166,6 +24936,7 @@
false
false
false
+ 0
2771
@@ -22174,6 +24945,7 @@
false
false
false
+ 0
2772
@@ -22182,6 +24954,7 @@
false
false
false
+ 0
2773
@@ -22190,6 +24963,7 @@
false
false
false
+ 0
2774
@@ -22198,6 +24972,7 @@
false
false
false
+ 0
2775
@@ -22206,6 +24981,7 @@
false
false
false
+ 0
2776
@@ -22214,6 +24990,7 @@
false
false
false
+ 0
2777
@@ -22222,6 +24999,7 @@
false
false
false
+ 0
2778
@@ -22230,6 +25008,7 @@
false
false
false
+ 0
2779
@@ -22238,6 +25017,7 @@
false
false
false
+ 0
2780
@@ -22246,6 +25026,7 @@
false
false
false
+ 0
2781
@@ -22254,6 +25035,7 @@
false
false
false
+ 0
2782
@@ -22262,6 +25044,7 @@
false
false
false
+ 0
2783
@@ -22270,6 +25053,7 @@
false
false
false
+ 0
2784
@@ -22278,6 +25062,7 @@
false
false
false
+ 0
2785
@@ -22286,6 +25071,7 @@
false
false
false
+ 0
2786
@@ -22294,6 +25080,7 @@
false
false
false
+ 0
2787
@@ -22302,6 +25089,7 @@
false
false
false
+ 0
2788
@@ -22310,6 +25098,7 @@
false
false
false
+ 0
2789
@@ -22318,6 +25107,7 @@
false
false
false
+ 0
2790
@@ -22326,6 +25116,7 @@
false
false
false
+ 0
2791
@@ -22334,6 +25125,7 @@
false
false
false
+ 0
2792
@@ -22342,6 +25134,7 @@
false
false
false
+ 0
2793
@@ -22350,6 +25143,7 @@
false
false
false
+ 0
2794
@@ -22358,6 +25152,7 @@
false
false
false
+ 0
2795
@@ -22366,6 +25161,7 @@
false
false
false
+ 0
2796
@@ -22374,6 +25170,7 @@
false
false
false
+ 0
2797
@@ -22382,6 +25179,7 @@
false
false
false
+ 0
2798
@@ -22390,6 +25188,7 @@
false
false
false
+ 0
2799
@@ -22398,6 +25197,7 @@
false
false
false
+ 0
2800
@@ -22406,6 +25206,7 @@
false
false
false
+ 0
2801
@@ -22414,6 +25215,7 @@
false
false
false
+ 0
2802
@@ -22422,6 +25224,7 @@
false
false
false
+ 0
2803
@@ -22430,6 +25233,7 @@
false
false
false
+ 0
2804
@@ -22438,6 +25242,7 @@
false
false
false
+ 0
2805
@@ -22446,6 +25251,7 @@
false
false
false
+ 0
2806
@@ -22454,6 +25260,7 @@
false
false
false
+ 0
2807
@@ -22462,6 +25269,7 @@
false
false
false
+ 0
2808
@@ -22470,6 +25278,7 @@
false
false
false
+ 0
2809
@@ -22478,6 +25287,7 @@
false
false
false
+ 0
2810
@@ -22486,6 +25296,7 @@
false
false
false
+ 0
2811
@@ -22494,6 +25305,7 @@
false
false
false
+ 0
2812
@@ -22502,6 +25314,7 @@
false
false
false
+ 0
2813
@@ -22510,6 +25323,7 @@
false
false
false
+ 0
2814
@@ -22518,6 +25332,7 @@
false
false
false
+ 0
2815
@@ -22526,6 +25341,7 @@
false
false
false
+ 0
2816
@@ -22534,6 +25350,7 @@
false
false
false
+ 0
2817
@@ -22542,6 +25359,7 @@
false
false
false
+ 0
2818
@@ -22550,6 +25368,7 @@
false
false
false
+ 0
2819
@@ -22558,6 +25377,7 @@
false
false
false
+ 0
2820
@@ -22566,6 +25386,7 @@
false
false
false
+ 0
2821
@@ -22574,6 +25395,7 @@
false
false
false
+ 0
2822
@@ -22582,6 +25404,7 @@
false
false
false
+ 0
2823
@@ -22590,6 +25413,7 @@
false
false
false
+ 0
2824
@@ -22598,6 +25422,7 @@
false
false
false
+ 0
2825
@@ -22606,6 +25431,7 @@
false
false
false
+ 0
2826
@@ -22614,6 +25440,7 @@
false
false
false
+ 0
2827
@@ -22622,6 +25449,7 @@
false
false
false
+ 0
2828
@@ -22630,6 +25458,7 @@
false
false
false
+ 0
2829
@@ -22638,6 +25467,7 @@
false
false
false
+ 0
2830
@@ -22646,6 +25476,7 @@
false
false
false
+ 0
2831
@@ -22654,6 +25485,7 @@
false
false
false
+ 0
2832
@@ -22662,6 +25494,7 @@
false
false
false
+ 0
2833
@@ -22670,6 +25503,7 @@
false
false
false
+ 0
2834
@@ -22678,6 +25512,7 @@
false
false
false
+ 0
2835
@@ -22686,6 +25521,7 @@
false
false
false
+ 0
2836
@@ -22694,6 +25530,7 @@
false
false
false
+ 0
2837
@@ -22702,6 +25539,7 @@
false
false
false
+ 0
2838
@@ -22710,6 +25548,7 @@
false
false
false
+ 0
2839
@@ -22718,6 +25557,7 @@
false
false
false
+ 0
2840
@@ -22726,6 +25566,7 @@
false
false
false
+ 0
2841
@@ -22734,6 +25575,7 @@
false
false
false
+ 0
2842
@@ -22742,6 +25584,7 @@
false
false
false
+ 0
2843
@@ -22750,6 +25593,7 @@
false
false
false
+ 0
2844
@@ -22758,6 +25602,7 @@
false
false
false
+ 0
2845
@@ -22766,6 +25611,7 @@
false
false
false
+ 0
2846
@@ -22774,6 +25620,7 @@
false
false
false
+ 0
2847
@@ -22782,6 +25629,7 @@
false
false
false
+ 0
2848
@@ -22790,6 +25638,7 @@
false
false
false
+ 0
2849
@@ -22798,6 +25647,7 @@
false
false
false
+ 0
2850
@@ -22806,6 +25656,7 @@
false
false
false
+ 0
2851
@@ -22814,6 +25665,7 @@
false
false
false
+ 0
2852
@@ -22822,6 +25674,7 @@
false
false
false
+ 0
2853
@@ -22830,6 +25683,7 @@
false
false
false
+ 0
2854
@@ -22838,6 +25692,7 @@
false
false
false
+ 0
2855
@@ -22846,6 +25701,7 @@
false
false
false
+ 0
2856
@@ -22854,6 +25710,7 @@
false
false
false
+ 0
2857
@@ -22862,6 +25719,7 @@
false
false
false
+ 0
2858
@@ -22870,6 +25728,7 @@
false
false
false
+ 0
2859
@@ -22878,6 +25737,7 @@
false
false
false
+ 0
2860
@@ -22886,6 +25746,7 @@
false
false
false
+ 0
2861
@@ -22894,6 +25755,7 @@
false
false
false
+ 0
2862
@@ -22902,6 +25764,7 @@
false
false
false
+ 0
2863
@@ -22910,6 +25773,7 @@
false
false
false
+ 0
2864
@@ -22918,6 +25782,7 @@
false
false
false
+ 0
2865
@@ -22926,6 +25791,7 @@
false
false
false
+ 0
2866
@@ -22934,6 +25800,7 @@
false
false
false
+ 0
2867
@@ -22942,6 +25809,7 @@
false
false
false
+ 0
2868
@@ -22950,6 +25818,7 @@
false
false
false
+ 0
2869
@@ -22958,6 +25827,7 @@
false
false
false
+ 0
2870
@@ -22966,6 +25836,7 @@
false
false
false
+ 0
2871
@@ -22974,6 +25845,7 @@
false
false
false
+ 0
2872
@@ -22982,6 +25854,7 @@
false
false
false
+ 0
2873
@@ -22990,6 +25863,7 @@
false
false
false
+ 0
2874
@@ -22998,6 +25872,7 @@
false
false
false
+ 0
2875
@@ -23006,6 +25881,7 @@
false
false
false
+ 0
2876
@@ -23014,6 +25890,7 @@
false
false
false
+ 0
2877
@@ -23022,6 +25899,7 @@
false
false
false
+ 0
2878
@@ -23030,6 +25908,7 @@
false
false
false
+ 0
2879
@@ -23038,6 +25917,7 @@
false
false
false
+ 0
2880
@@ -23046,6 +25926,7 @@
false
false
false
+ 0
2881
@@ -23054,6 +25935,7 @@
false
false
false
+ 0
2882
@@ -23062,6 +25944,7 @@
false
false
false
+ 0
2883
@@ -23070,6 +25953,7 @@
false
false
false
+ 0
2884
@@ -23078,6 +25962,7 @@
false
false
false
+ 0
2885
@@ -23086,6 +25971,7 @@
false
false
false
+ 0
2886
@@ -23094,6 +25980,7 @@
false
false
false
+ 0
2887
@@ -23102,6 +25989,7 @@
false
false
false
+ 0
2888
@@ -23110,6 +25998,7 @@
false
false
false
+ 0
2889
@@ -23118,6 +26007,7 @@
false
false
false
+ 0
2890
@@ -23126,6 +26016,7 @@
false
false
false
+ 0
2891
@@ -23134,6 +26025,7 @@
false
false
false
+ 0
2892
@@ -23142,6 +26034,7 @@
false
false
false
+ 0
2893
@@ -23150,6 +26043,7 @@
false
false
false
+ 0
2894
@@ -23158,6 +26052,7 @@
false
false
false
+ 0
2895
@@ -23166,6 +26061,7 @@
false
false
false
+ 0
2896
@@ -23174,6 +26070,7 @@
false
false
false
+ 0
2897
@@ -23182,6 +26079,7 @@
false
false
false
+ 0
2898
@@ -23190,6 +26088,7 @@
false
false
false
+ 0
2899
@@ -23198,6 +26097,7 @@
false
false
false
+ 0
2900
@@ -23206,6 +26106,7 @@
false
false
false
+ 0
2901
@@ -23214,6 +26115,7 @@
false
false
false
+ 0
2902
@@ -23222,6 +26124,7 @@
false
false
false
+ 0
2903
@@ -23230,6 +26133,7 @@
false
false
false
+ 0
2904
@@ -23238,6 +26142,7 @@
false
false
false
+ 0
2905
@@ -23246,6 +26151,7 @@
false
false
false
+ 0
2906
@@ -23254,6 +26160,7 @@
false
false
false
+ 0
2907
@@ -23262,6 +26169,7 @@
false
false
false
+ 0
2908
@@ -23270,6 +26178,7 @@
false
false
false
+ 0
2909
@@ -23278,6 +26187,7 @@
false
false
false
+ 0
2910
@@ -23286,6 +26196,7 @@
false
false
false
+ 0
2911
@@ -23294,6 +26205,7 @@
false
false
false
+ 0
2912
@@ -23302,6 +26214,7 @@
false
false
false
+ 0
2913
@@ -23310,6 +26223,7 @@
false
false
false
+ 0
2914
@@ -23318,6 +26232,7 @@
false
false
false
+ 0
2915
@@ -23326,6 +26241,7 @@
false
false
false
+ 0
2916
@@ -23334,6 +26250,7 @@
false
false
false
+ 0
2917
@@ -23342,6 +26259,7 @@
false
false
false
+ 0
2918
@@ -23350,6 +26268,7 @@
false
false
false
+ 0
2919
@@ -23358,6 +26277,7 @@
false
false
false
+ 0
2920
@@ -23366,6 +26286,7 @@
false
false
false
+ 0
2921
@@ -23374,6 +26295,7 @@
false
false
false
+ 0
2922
@@ -23382,6 +26304,7 @@
false
false
false
+ 0
2923
@@ -23390,6 +26313,7 @@
false
false
false
+ 0
2924
@@ -23398,6 +26322,7 @@
false
false
false
+ 0
2925
@@ -23406,6 +26331,7 @@
false
false
false
+ 0
2926
@@ -23414,6 +26340,7 @@
false
false
false
+ 0
2927
@@ -23422,6 +26349,7 @@
false
false
false
+ 0
2928
@@ -23430,6 +26358,7 @@
false
false
false
+ 0
2929
@@ -23438,6 +26367,7 @@
false
false
false
+ 0
2930
@@ -23446,6 +26376,7 @@
false
false
false
+ 0
2931
@@ -23454,6 +26385,7 @@
false
false
false
+ 0
2932
@@ -23462,6 +26394,7 @@
false
false
false
+ 0
2933
@@ -23470,6 +26403,7 @@
false
false
false
+ 0
2934
@@ -23478,6 +26412,7 @@
false
false
false
+ 0
2935
@@ -23486,6 +26421,7 @@
false
false
false
+ 0
2936
@@ -23494,6 +26430,7 @@
false
false
false
+ 0
2937
@@ -23502,6 +26439,7 @@
false
false
false
+ 0
2938
@@ -23510,6 +26448,7 @@
false
false
false
+ 0
2939
@@ -23518,6 +26457,7 @@
false
false
false
+ 0
2940
@@ -23526,6 +26466,7 @@
false
false
false
+ 0
2941
@@ -23534,6 +26475,7 @@
false
false
false
+ 0
2942
@@ -23542,6 +26484,7 @@
false
false
false
+ 0
2943
@@ -23550,6 +26493,7 @@
false
false
false
+ 0
2944
@@ -23558,6 +26502,7 @@
false
false
false
+ 0
2945
@@ -23566,6 +26511,7 @@
false
false
false
+ 0
2946
@@ -23574,6 +26520,7 @@
false
false
false
+ 0
2947
@@ -23582,6 +26529,7 @@
false
false
false
+ 0
2948
@@ -23590,6 +26538,7 @@
false
false
false
+ 0
2949
@@ -23598,6 +26547,7 @@
false
false
false
+ 0
2950
@@ -23606,6 +26556,7 @@
false
false
false
+ 0
2951
@@ -23614,6 +26565,7 @@
false
false
false
+ 0
2952
@@ -23622,6 +26574,7 @@
false
false
false
+ 0
2953
@@ -23630,6 +26583,7 @@
false
false
false
+ 0
2954
@@ -23638,6 +26592,7 @@
false
false
false
+ 0
2955
@@ -23646,6 +26601,7 @@
false
false
false
+ 0
2956
@@ -23654,6 +26610,7 @@
false
false
false
+ 0
2957
@@ -23662,6 +26619,7 @@
false
false
false
+ 0
2958
@@ -23670,6 +26628,7 @@
false
false
false
+ 0
2959
@@ -23678,6 +26637,7 @@
false
false
false
+ 0
2960
@@ -23686,6 +26646,7 @@
false
false
false
+ 0
2961
@@ -23694,6 +26655,7 @@
false
false
false
+ 0
2962
@@ -23702,6 +26664,7 @@
false
false
false
+ 0
2963
@@ -23710,6 +26673,7 @@
false
false
false
+ 0
2964
@@ -23718,6 +26682,7 @@
false
false
false
+ 0
2965
@@ -23726,6 +26691,7 @@
false
false
false
+ 0
2966
@@ -23734,6 +26700,7 @@
false
false
false
+ 0
2967
@@ -23742,6 +26709,7 @@
false
false
false
+ 0
2968
@@ -23750,6 +26718,7 @@
false
false
false
+ 0
2969
@@ -23758,6 +26727,7 @@
false
false
false
+ 0
2970
@@ -23766,6 +26736,7 @@
false
false
false
+ 0
2971
@@ -23774,6 +26745,7 @@
false
false
false
+ 0
2972
@@ -23782,6 +26754,7 @@
false
false
false
+ 0
2973
@@ -23790,6 +26763,7 @@
false
false
false
+ 0
2974
@@ -23798,6 +26772,7 @@
false
false
false
+ 0
2975
@@ -23806,6 +26781,7 @@
false
false
false
+ 0
2976
@@ -23814,6 +26790,7 @@
false
false
false
+ 0
2977
@@ -23822,6 +26799,7 @@
false
false
false
+ 0
2978
@@ -23830,6 +26808,7 @@
false
false
false
+ 0
2979
@@ -23838,6 +26817,7 @@
false
false
false
+ 0
2980
@@ -23846,6 +26826,7 @@
false
false
false
+ 0
2981
@@ -23854,6 +26835,7 @@
false
false
false
+ 0
2982
@@ -23862,6 +26844,7 @@
false
false
false
+ 0
2983
@@ -23870,6 +26853,7 @@
false
false
false
+ 0
2984
@@ -23878,6 +26862,7 @@
false
false
false
+ 0
2985
@@ -23886,6 +26871,7 @@
false
false
false
+ 0
2986
@@ -23894,6 +26880,7 @@
false
false
false
+ 0
2987
@@ -23902,6 +26889,7 @@
false
false
false
+ 0
2988
@@ -23910,6 +26898,7 @@
false
false
false
+ 0
2989
@@ -23918,6 +26907,7 @@
false
false
false
+ 0
2990
@@ -23926,6 +26916,7 @@
false
false
false
+ 0
2991
@@ -23934,6 +26925,7 @@
false
false
false
+ 0
2992
@@ -23942,6 +26934,7 @@
false
false
false
+ 0
2993
@@ -23950,6 +26943,7 @@
false
false
false
+ 0
2994
@@ -23958,6 +26952,7 @@
false
false
false
+ 0
2995
@@ -23966,6 +26961,7 @@
false
false
false
+ 0
2996
@@ -23974,6 +26970,7 @@
false
false
false
+ 0
2997
@@ -23982,6 +26979,7 @@
false
false
false
+ 0
2998
@@ -23990,6 +26988,7 @@
false
false
false
+ 0
2999
@@ -23998,6 +26997,7 @@
false
false
false
+ 0
3000
@@ -24006,6 +27006,7 @@
false
false
false
+ 0
3001
@@ -24014,6 +27015,7 @@
false
false
false
+ 0
3002
@@ -24022,6 +27024,7 @@
false
false
false
+ 0
3003
@@ -24030,6 +27033,7 @@
false
false
false
+ 0
3004
@@ -24038,6 +27042,7 @@
false
false
false
+ 0
3005
@@ -24046,6 +27051,7 @@
false
false
false
+ 0
3006
@@ -24054,6 +27060,7 @@
false
false
false
+ 0
3007
@@ -24062,6 +27069,7 @@
false
false
false
+ 0
3008
@@ -24070,6 +27078,7 @@
false
false
false
+ 0
3009
@@ -24078,6 +27087,7 @@
false
false
false
+ 0
3010
@@ -24086,6 +27096,7 @@
false
false
false
+ 0
3011
@@ -24094,6 +27105,7 @@
false
false
false
+ 0
3012
@@ -24102,6 +27114,7 @@
false
false
false
+ 0
3013
@@ -24110,6 +27123,7 @@
false
false
false
+ 0
3014
@@ -24118,6 +27132,7 @@
false
false
false
+ 0
3015
@@ -24126,6 +27141,7 @@
false
false
false
+ 0
3016
@@ -24134,6 +27150,7 @@
false
false
false
+ 0
3017
@@ -24142,6 +27159,7 @@
false
false
false
+ 0
3018
@@ -24150,6 +27168,7 @@
false
false
false
+ 0
3019
@@ -24158,6 +27177,7 @@
false
false
false
+ 0
3020
@@ -24166,6 +27186,7 @@
false
false
false
+ 0
3021
@@ -24174,6 +27195,7 @@
false
false
false
+ 0
3022
@@ -24182,6 +27204,7 @@
false
false
false
+ 0
3023
@@ -24190,6 +27213,7 @@
false
false
false
+ 0
3024
@@ -24198,6 +27222,7 @@
false
false
false
+ 0
3025
@@ -24206,6 +27231,7 @@
false
false
false
+ 0
3026
@@ -24214,6 +27240,7 @@
false
false
false
+ 0
3027
@@ -24222,6 +27249,7 @@
false
false
false
+ 0
3028
@@ -24230,6 +27258,7 @@
false
false
false
+ 0
3029
@@ -24238,6 +27267,7 @@
false
false
false
+ 0
3030
@@ -24246,6 +27276,7 @@
false
false
false
+ 0
3031
@@ -24254,6 +27285,7 @@
false
false
false
+ 0
3032
@@ -24262,6 +27294,7 @@
false
false
false
+ 0
3033
@@ -24270,6 +27303,7 @@
false
false
false
+ 0
3034
@@ -24278,6 +27312,7 @@
false
false
false
+ 0
3035
@@ -24286,6 +27321,7 @@
false
false
false
+ 0
3036
@@ -24294,6 +27330,7 @@
false
false
false
+ 0
3037
@@ -24302,6 +27339,7 @@
false
false
false
+ 0
3038
@@ -24310,6 +27348,7 @@
false
false
false
+ 0
3039
@@ -24318,6 +27357,7 @@
false
false
false
+ 0
3040
@@ -24326,6 +27366,7 @@
false
false
false
+ 0
3041
@@ -24334,6 +27375,7 @@
false
false
false
+ 0
3042
@@ -24342,6 +27384,7 @@
false
false
false
+ 0
3043
@@ -24350,6 +27393,7 @@
false
false
false
+ 0
3044
@@ -24358,6 +27402,7 @@
false
false
false
+ 0
3045
@@ -24366,6 +27411,7 @@
false
false
false
+ 0
3046
@@ -24374,6 +27420,7 @@
false
false
false
+ 0
3047
@@ -24382,6 +27429,7 @@
false
false
false
+ 0
3048
@@ -24390,6 +27438,7 @@
false
false
false
+ 0
3049
@@ -24398,6 +27447,7 @@
false
false
false
+ 0
3050
@@ -24406,6 +27456,7 @@
false
false
false
+ 0
3051
@@ -24414,6 +27465,7 @@
false
false
false
+ 0
3052
@@ -24422,6 +27474,7 @@
false
false
false
+ 0
3053
@@ -24430,6 +27483,7 @@
false
false
false
+ 0
3054
@@ -24438,6 +27492,7 @@
false
false
false
+ 0
3055
@@ -24446,6 +27501,7 @@
false
false
false
+ 0
3056
@@ -24454,6 +27510,7 @@
false
false
false
+ 0
3057
@@ -24462,6 +27519,7 @@
false
false
false
+ 0
3058
@@ -24470,6 +27528,7 @@
false
false
false
+ 0
3059
@@ -24478,6 +27537,7 @@
false
false
false
+ 0
3060
@@ -24486,6 +27546,7 @@
false
false
false
+ 0
3061
@@ -24494,6 +27555,7 @@
false
false
false
+ 0
3062
@@ -24502,6 +27564,7 @@
false
false
false
+ 0
3063
@@ -24510,6 +27573,7 @@
false
false
false
+ 0
3064
@@ -24518,6 +27582,7 @@
false
false
false
+ 0
3065
@@ -24526,6 +27591,7 @@
false
false
false
+ 0
3066
@@ -24534,6 +27600,7 @@
false
false
false
+ 0
3067
@@ -24542,6 +27609,7 @@
false
false
false
+ 0
3068
@@ -24550,6 +27618,7 @@
false
false
false
+ 0
3069
@@ -24558,6 +27627,7 @@
false
false
false
+ 0
3070
@@ -24566,6 +27636,7 @@
false
false
false
+ 0
3071
@@ -24574,6 +27645,7 @@
false
false
false
+ 0
3072
@@ -24582,6 +27654,7 @@
false
false
false
+ 0
3073
@@ -24590,6 +27663,7 @@
false
false
false
+ 0
3074
@@ -24598,6 +27672,7 @@
false
false
false
+ 0
3075
@@ -24606,6 +27681,7 @@
false
false
false
+ 0
3076
@@ -24614,6 +27690,7 @@
false
false
false
+ 0
3077
@@ -24622,6 +27699,7 @@
false
false
false
+ 0
3078
@@ -24630,6 +27708,7 @@
false
false
false
+ 0
3079
@@ -24638,6 +27717,7 @@
false
false
false
+ 0
3080
@@ -24646,6 +27726,7 @@
false
false
false
+ 0
3081
@@ -24654,6 +27735,7 @@
false
false
false
+ 0
3082
@@ -24662,6 +27744,7 @@
false
false
false
+ 0
3083
@@ -24670,6 +27753,7 @@
false
false
false
+ 0
3084
@@ -24678,6 +27762,7 @@
false
false
false
+ 0
3085
@@ -24686,6 +27771,7 @@
false
false
false
+ 0
3086
@@ -24694,6 +27780,7 @@
false
false
false
+ 0
3087
@@ -24702,6 +27789,7 @@
false
false
false
+ 0
3088
@@ -24710,6 +27798,7 @@
false
false
false
+ 0
3089
@@ -24718,6 +27807,7 @@
false
false
false
+ 0
3090
@@ -24726,6 +27816,7 @@
false
false
false
+ 0
3091
@@ -24734,6 +27825,7 @@
false
false
false
+ 0
3092
@@ -24742,6 +27834,7 @@
false
false
false
+ 0
3093
@@ -24750,6 +27843,7 @@
false
false
false
+ 0
3094
@@ -24758,6 +27852,7 @@
false
false
false
+ 0
3095
@@ -24766,6 +27861,7 @@
false
false
false
+ 0
3096
@@ -24774,6 +27870,7 @@
false
false
false
+ 0
3097
@@ -24782,6 +27879,7 @@
false
false
false
+ 0
3098
@@ -24790,6 +27888,7 @@
false
false
false
+ 0
3099
@@ -24798,6 +27897,7 @@
false
false
false
+ 0
3100
@@ -24806,6 +27906,7 @@
false
false
false
+ 0
3101
@@ -24814,6 +27915,7 @@
false
false
false
+ 0
3102
@@ -24822,6 +27924,7 @@
false
false
false
+ 0
3103
@@ -24830,6 +27933,7 @@
false
false
false
+ 0
3104
@@ -24838,6 +27942,7 @@
false
false
false
+ 0
3105
@@ -24846,6 +27951,7 @@
false
false
false
+ 0
3106
@@ -24854,6 +27960,7 @@
false
false
false
+ 0
3107
@@ -24862,6 +27969,7 @@
false
false
false
+ 0
3108
@@ -24870,6 +27978,7 @@
false
false
false
+ 0
3109
@@ -24878,6 +27987,7 @@
false
false
false
+ 0
3110
@@ -24886,6 +27996,7 @@
false
false
false
+ 0
3111
@@ -24894,6 +28005,7 @@
false
false
false
+ 0
3112
@@ -24902,6 +28014,7 @@
false
false
false
+ 0
3113
@@ -24910,6 +28023,7 @@
false
false
false
+ 0
3114
@@ -24918,6 +28032,7 @@
false
false
false
+ 0
3115
@@ -24926,6 +28041,7 @@
false
false
false
+ 0
3116
@@ -24934,6 +28050,7 @@
false
false
false
+ 0
3117
@@ -24942,6 +28059,7 @@
false
false
false
+ 0
3118
@@ -24950,6 +28068,7 @@
false
false
false
+ 0
3119
@@ -24958,6 +28077,7 @@
false
false
false
+ 0
3120
@@ -24966,6 +28086,7 @@
false
false
false
+ 0
3121
@@ -24974,6 +28095,7 @@
false
false
false
+ 0
3122
@@ -24982,6 +28104,7 @@
false
false
false
+ 0
3123
@@ -24990,6 +28113,7 @@
false
false
false
+ 0
3124
@@ -24998,6 +28122,7 @@
false
false
false
+ 0
3125
@@ -25006,6 +28131,7 @@
false
false
false
+ 0
3126
@@ -25014,6 +28140,7 @@
false
false
false
+ 0
3127
@@ -25022,6 +28149,7 @@
false
false
false
+ 0
3128
@@ -25030,6 +28158,7 @@
false
false
false
+ 0
3129
@@ -25038,6 +28167,7 @@
false
false
false
+ 0
3130
@@ -25046,6 +28176,7 @@
false
false
false
+ 0
3131
@@ -25054,6 +28185,7 @@
false
false
false
+ 0
3132
@@ -25062,6 +28194,7 @@
false
false
false
+ 0
3133
@@ -25070,6 +28203,7 @@
false
false
false
+ 0
3134
@@ -25078,6 +28212,7 @@
false
false
false
+ 0
3135
@@ -25086,6 +28221,7 @@
false
false
false
+ 0
3136
@@ -25094,6 +28230,7 @@
false
false
false
+ 0
3137
@@ -25102,6 +28239,7 @@
false
false
false
+ 0
3138
@@ -25110,6 +28248,7 @@
false
false
false
+ 0
3139
@@ -25118,6 +28257,7 @@
false
false
false
+ 0
3140
@@ -25126,6 +28266,7 @@
false
false
false
+ 0
3141
@@ -25134,6 +28275,7 @@
false
false
false
+ 0
3142
@@ -25142,6 +28284,7 @@
false
false
false
+ 0
3143
@@ -25150,6 +28293,7 @@
false
false
false
+ 0
3144
@@ -25158,6 +28302,7 @@
false
false
false
+ 0
3145
@@ -25166,6 +28311,7 @@
false
false
false
+ 0
3146
@@ -25174,6 +28320,7 @@
false
false
false
+ 0
3147
@@ -25182,6 +28329,7 @@
false
false
false
+ 0
3148
@@ -25190,6 +28338,7 @@
false
false
false
+ 0
3149
@@ -25198,6 +28347,7 @@
false
false
false
+ 0
3150
@@ -25206,6 +28356,7 @@
false
false
false
+ 0
3151
@@ -25214,6 +28365,7 @@
false
false
false
+ 0
3152
@@ -25222,6 +28374,7 @@
false
false
false
+ 0
3153
@@ -25230,6 +28383,7 @@
false
false
false
+ 0
3154
@@ -25238,6 +28392,7 @@
false
false
false
+ 0
3155
@@ -25246,6 +28401,7 @@
false
false
false
+ 0
3156
@@ -25254,6 +28410,7 @@
false
false
false
+ 0
3157
@@ -25262,6 +28419,7 @@
false
false
false
+ 0
3158
@@ -25270,6 +28428,7 @@
false
false
false
+ 0
3159
@@ -25278,6 +28437,7 @@
false
false
false
+ 0
3160
@@ -25286,6 +28446,7 @@
false
false
false
+ 0
3161
@@ -25294,6 +28455,7 @@
false
false
false
+ 0
3162
@@ -25302,6 +28464,7 @@
false
false
false
+ 0
3163
@@ -25310,6 +28473,7 @@
false
false
false
+ 0
3164
@@ -25318,6 +28482,7 @@
false
false
false
+ 0
3165
@@ -25326,6 +28491,7 @@
false
false
false
+ 0
3166
@@ -25334,6 +28500,7 @@
false
false
false
+ 0
3167
@@ -25342,6 +28509,7 @@
false
false
false
+ 0
3168
@@ -25350,6 +28518,7 @@
false
false
false
+ 0
3169
@@ -25358,6 +28527,7 @@
false
false
false
+ 0
3170
@@ -25366,6 +28536,7 @@
false
false
false
+ 0
3171
@@ -25374,6 +28545,7 @@
false
false
false
+ 0
3172
@@ -25382,6 +28554,7 @@
false
false
false
+ 0
3173
@@ -25390,6 +28563,7 @@
false
false
false
+ 0
3174
@@ -25398,6 +28572,7 @@
false
false
false
+ 0
3175
@@ -25406,6 +28581,7 @@
false
false
false
+ 0
3176
@@ -25414,6 +28590,7 @@
false
false
false
+ 0
3177
@@ -25422,6 +28599,7 @@
false
false
false
+ 0
3178
@@ -25430,6 +28608,7 @@
false
false
false
+ 0
3179
@@ -25438,6 +28617,7 @@
false
false
false
+ 0
3180
@@ -25446,6 +28626,7 @@
false
false
false
+ 0
3181
@@ -25454,6 +28635,7 @@
false
false
false
+ 0
3182
@@ -25462,6 +28644,7 @@
false
false
false
+ 0
3183
@@ -25470,6 +28653,7 @@
false
false
false
+ 0
3184
@@ -25478,6 +28662,7 @@
false
false
false
+ 0
3185
@@ -25486,6 +28671,7 @@
false
false
false
+ 0
3186
@@ -25494,6 +28680,7 @@
false
false
false
+ 0
3187
@@ -25502,6 +28689,7 @@
false
false
false
+ 0
3188
@@ -25510,6 +28698,7 @@
false
false
false
+ 0
3189
@@ -25518,6 +28707,7 @@
false
false
false
+ 0
3190
@@ -25526,6 +28716,7 @@
false
false
false
+ 0
3191
@@ -25534,6 +28725,7 @@
false
false
false
+ 0
3192
@@ -25542,6 +28734,7 @@
false
false
false
+ 0
3193
@@ -25550,6 +28743,7 @@
false
false
false
+ 0
3194
@@ -25558,6 +28752,7 @@
false
false
false
+ 0
3195
@@ -25566,6 +28761,7 @@
false
false
false
+ 0
3196
@@ -25574,6 +28770,7 @@
false
false
false
+ 0
3197
@@ -25582,6 +28779,7 @@
false
false
false
+ 0
3198
@@ -25590,6 +28788,7 @@
false
false
false
+ 0
3199
@@ -25598,6 +28797,7 @@
false
false
false
+ 0
3200
@@ -25606,6 +28806,7 @@
false
false
false
+ 0
3201
@@ -25614,6 +28815,7 @@
false
false
false
+ 0
3202
@@ -25622,6 +28824,7 @@
false
false
false
+ 0
3203
@@ -25630,6 +28833,7 @@
false
false
false
+ 0
3204
@@ -25638,6 +28842,7 @@
false
false
false
+ 0
3205
@@ -25646,6 +28851,7 @@
false
false
false
+ 0
3206
@@ -25654,6 +28860,7 @@
false
false
false
+ 0
3207
@@ -25662,6 +28869,7 @@
false
false
false
+ 0
3208
@@ -25670,6 +28878,7 @@
false
false
false
+ 0
3209
@@ -25678,6 +28887,7 @@
false
false
false
+ 0
3210
@@ -25686,6 +28896,7 @@
false
false
false
+ 0
3211
@@ -25694,6 +28905,7 @@
false
false
false
+ 0
3212
@@ -25702,6 +28914,7 @@
false
false
false
+ 0
3213
@@ -25710,6 +28923,7 @@
false
false
false
+ 0
3214
@@ -25718,6 +28932,7 @@
false
false
false
+ 0
3215
@@ -25726,6 +28941,7 @@
false
false
false
+ 0
3216
@@ -25734,6 +28950,7 @@
false
false
false
+ 0
3217
@@ -25742,6 +28959,7 @@
false
false
false
+ 0
3218
@@ -25750,6 +28968,7 @@
false
false
false
+ 0
3219
@@ -25758,6 +28977,7 @@
false
false
false
+ 0
3220
@@ -25766,6 +28986,7 @@
false
false
false
+ 0
3221
@@ -25774,6 +28995,7 @@
false
false
false
+ 0
3222
@@ -25782,6 +29004,7 @@
false
false
false
+ 0
3223
@@ -25790,6 +29013,7 @@
false
false
false
+ 0
3224
@@ -25798,6 +29022,7 @@
false
false
false
+ 0
3225
@@ -25806,6 +29031,7 @@
false
false
false
+ 0
3226
@@ -25814,6 +29040,7 @@
false
false
false
+ 0
3227
@@ -25822,6 +29049,7 @@
false
false
false
+ 0
3228
@@ -25830,6 +29058,7 @@
false
false
false
+ 0
3229
@@ -25838,6 +29067,7 @@
false
false
false
+ 0
3230
@@ -25846,6 +29076,7 @@
false
false
false
+ 0
3231
@@ -25854,6 +29085,7 @@
false
false
false
+ 0
3232
@@ -25862,6 +29094,7 @@
false
false
false
+ 0
3233
@@ -25870,6 +29103,7 @@
false
false
false
+ 0
3234
@@ -25878,6 +29112,7 @@
false
false
false
+ 0
3235
@@ -25886,6 +29121,7 @@
false
false
false
+ 0
3236
@@ -25894,6 +29130,7 @@
false
false
false
+ 0
3237
@@ -25902,6 +29139,7 @@
false
false
false
+ 0
3238
@@ -25910,6 +29148,7 @@
false
false
false
+ 0
3239
@@ -25918,6 +29157,7 @@
false
false
false
+ 0
3240
@@ -25926,6 +29166,7 @@
false
false
false
+ 0
3241
@@ -25934,6 +29175,7 @@
false
false
false
+ 0
3242
@@ -25942,6 +29184,7 @@
false
false
false
+ 0
3243
@@ -25950,6 +29193,7 @@
false
false
false
+ 0
3244
@@ -25958,6 +29202,7 @@
false
false
false
+ 0
3245
@@ -25966,6 +29211,7 @@
false
false
false
+ 0
3246
@@ -25974,6 +29220,7 @@
false
false
false
+ 0
3247
@@ -25982,6 +29229,7 @@
false
false
false
+ 0
3248
@@ -25990,6 +29238,7 @@
false
false
false
+ 0
3249
@@ -25998,6 +29247,7 @@
false
false
false
+ 0
3250
@@ -26006,6 +29256,7 @@
false
false
false
+ 0
3251
@@ -26014,6 +29265,7 @@
false
false
false
+ 0
3252
@@ -26022,6 +29274,7 @@
false
false
false
+ 0
3253
@@ -26030,6 +29283,7 @@
false
false
false
+ 0
3254
@@ -26038,6 +29292,7 @@
false
false
false
+ 0
3255
@@ -26046,6 +29301,7 @@
false
false
false
+ 0
3256
@@ -26054,6 +29310,7 @@
false
false
false
+ 0
3257
@@ -26062,6 +29319,7 @@
false
false
false
+ 0
3258
@@ -26070,6 +29328,7 @@
false
false
false
+ 0
3259
@@ -26078,6 +29337,7 @@
false
false
false
+ 0
3260
@@ -26086,6 +29346,7 @@
false
false
false
+ 0
3261
@@ -26094,6 +29355,7 @@
false
false
false
+ 0
3262
@@ -26102,6 +29364,7 @@
false
false
false
+ 0
3263
@@ -26110,6 +29373,7 @@
false
false
false
+ 0
3264
@@ -26118,6 +29382,7 @@
false
false
false
+ 0
3265
@@ -26126,6 +29391,7 @@
false
false
false
+ 0
3266
@@ -26134,6 +29400,7 @@
false
false
false
+ 0
3267
@@ -26142,6 +29409,7 @@
false
false
false
+ 0
3268
@@ -26150,6 +29418,7 @@
false
false
false
+ 0
3269
@@ -26158,6 +29427,7 @@
false
false
false
+ 0
3270
@@ -26166,6 +29436,7 @@
false
false
false
+ 0
3271
@@ -26174,6 +29445,7 @@
false
false
false
+ 0
3272
@@ -26182,6 +29454,7 @@
false
false
false
+ 0
3273
@@ -26190,6 +29463,7 @@
false
false
false
+ 0
3274
@@ -26198,6 +29472,7 @@
false
false
false
+ 0
3275
@@ -26206,6 +29481,7 @@
false
false
false
+ 0
3276
@@ -26214,6 +29490,7 @@
false
false
false
+ 0
3277
@@ -26222,6 +29499,7 @@
false
false
false
+ 0
3278
@@ -26230,6 +29508,7 @@
false
false
false
+ 0
3279
@@ -26238,6 +29517,7 @@
false
false
false
+ 0
3280
@@ -26246,6 +29526,7 @@
false
false
false
+ 0
3281
@@ -26254,6 +29535,7 @@
false
false
false
+ 0
3282
@@ -26262,6 +29544,7 @@
false
false
false
+ 0
3283
@@ -26270,6 +29553,7 @@
false
false
false
+ 0
3284
@@ -26278,6 +29562,7 @@
false
false
false
+ 0
3285
@@ -26286,6 +29571,7 @@
false
false
false
+ 0
3286
@@ -26294,6 +29580,7 @@
false
false
false
+ 0
3287
@@ -26302,6 +29589,7 @@
false
false
false
+ 0
3288
@@ -26310,6 +29598,7 @@
false
false
false
+ 0
3289
@@ -26318,6 +29607,7 @@
false
false
false
+ 0
3290
@@ -26326,6 +29616,7 @@
false
false
false
+ 0
3291
@@ -26334,6 +29625,7 @@
false
false
false
+ 0
3292
@@ -26342,6 +29634,7 @@
false
false
false
+ 0
3293
@@ -26350,6 +29643,7 @@
false
false
false
+ 0
3294
@@ -26358,6 +29652,7 @@
false
false
false
+ 0
3295
@@ -26366,6 +29661,7 @@
false
false
false
+ 0
3296
@@ -26374,6 +29670,7 @@
false
false
false
+ 0
3297
@@ -26382,6 +29679,7 @@
false
false
false
+ 0
3298
@@ -26390,6 +29688,7 @@
false
false
false
+ 0
3299
@@ -26398,6 +29697,7 @@
false
false
false
+ 0
3300
@@ -26406,6 +29706,7 @@
false
false
false
+ 0
3301
@@ -26414,6 +29715,7 @@
false
false
false
+ 0
3302
@@ -26422,6 +29724,7 @@
false
false
false
+ 0
3303
@@ -26430,6 +29733,7 @@
false
false
false
+ 0
3304
@@ -26438,6 +29742,7 @@
false
false
false
+ 0
3305
@@ -26446,6 +29751,7 @@
false
false
false
+ 0
3306
@@ -26454,6 +29760,7 @@
false
false
false
+ 0
3307
@@ -26462,6 +29769,7 @@
false
false
false
+ 0
3308
@@ -26470,6 +29778,7 @@
false
false
false
+ 0
3309
@@ -26478,6 +29787,7 @@
false
false
false
+ 0
3310
@@ -26486,6 +29796,7 @@
false
false
false
+ 0
3311
@@ -26494,6 +29805,7 @@
false
false
false
+ 0
3312
@@ -26502,6 +29814,7 @@
false
false
false
+ 0
3313
@@ -26510,6 +29823,7 @@
false
false
false
+ 0
3314
@@ -26518,6 +29832,7 @@
false
false
false
+ 0
3315
@@ -26526,6 +29841,7 @@
false
false
false
+ 0
3316
@@ -26534,6 +29850,7 @@
false
false
false
+ 0
3317
@@ -26542,6 +29859,7 @@
false
false
false
+ 0
3318
@@ -26550,6 +29868,7 @@
false
false
false
+ 0
3319
@@ -26558,6 +29877,7 @@
false
false
false
+ 0
3320
@@ -26566,6 +29886,7 @@
false
false
false
+ 0
3321
@@ -26574,6 +29895,7 @@
false
false
false
+ 0
3322
@@ -26582,6 +29904,7 @@
false
false
false
+ 0
3323
@@ -26590,6 +29913,7 @@
false
false
false
+ 0
3324
@@ -26598,6 +29922,7 @@
false
false
false
+ 0
3325
@@ -26606,6 +29931,7 @@
false
false
false
+ 0
3326
@@ -26614,6 +29940,7 @@
false
false
false
+ 0
3327
@@ -26622,6 +29949,7 @@
false
false
false
+ 0
3328
@@ -26630,6 +29958,7 @@
false
false
false
+ 0
3329
@@ -26638,6 +29967,7 @@
false
false
false
+ 0
3330
@@ -26646,6 +29976,7 @@
false
false
false
+ 0
3331
@@ -26654,6 +29985,7 @@
false
false
false
+ 0
3332
@@ -26662,6 +29994,7 @@
false
false
false
+ 0
3333
@@ -26670,6 +30003,7 @@
false
false
false
+ 0
3334
@@ -26678,6 +30012,7 @@
false
false
false
+ 0
3335
@@ -26686,6 +30021,7 @@
false
false
false
+ 0
3336
@@ -26694,6 +30030,7 @@
false
false
false
+ 0
3337
@@ -26702,6 +30039,7 @@
false
false
false
+ 0
3338
@@ -26710,6 +30048,7 @@
false
false
false
+ 0
3339
@@ -26718,6 +30057,7 @@
false
false
false
+ 0
3340
@@ -26726,6 +30066,7 @@
false
false
false
+ 0
3341
@@ -26734,6 +30075,7 @@
false
false
false
+ 0
3342
@@ -26742,6 +30084,7 @@
false
false
false
+ 0
3343
@@ -26750,6 +30093,7 @@
false
false
false
+ 0
3344
@@ -26758,6 +30102,7 @@
false
false
false
+ 0
3345
@@ -26766,6 +30111,7 @@
false
false
false
+ 0
3346
@@ -26774,6 +30120,7 @@
false
false
false
+ 0
3347
@@ -26782,6 +30129,7 @@
false
false
false
+ 0
3348
@@ -26790,6 +30138,7 @@
false
false
false
+ 0
3349
@@ -26798,6 +30147,7 @@
false
false
false
+ 0
3350
@@ -26806,6 +30156,7 @@
false
false
false
+ 0
3351
@@ -26814,6 +30165,7 @@
false
false
false
+ 0
3352
@@ -26822,6 +30174,7 @@
false
false
false
+ 0
3353
@@ -26830,6 +30183,7 @@
false
false
false
+ 0
3354
@@ -26838,6 +30192,7 @@
false
false
false
+ 0
3355
@@ -26846,6 +30201,7 @@
false
false
false
+ 0
3356
@@ -26854,6 +30210,7 @@
false
false
false
+ 0
3357
@@ -26862,6 +30219,7 @@
false
false
false
+ 0
3358
@@ -26870,6 +30228,7 @@
false
false
false
+ 0
3359
@@ -26878,6 +30237,7 @@
false
false
false
+ 0
3360
@@ -26886,6 +30246,7 @@
false
false
false
+ 0
3361
@@ -26894,6 +30255,7 @@
false
false
false
+ 0
3362
@@ -26902,6 +30264,7 @@
false
false
false
+ 0
3363
@@ -26910,6 +30273,7 @@
false
false
false
+ 0
3364
@@ -26918,6 +30282,7 @@
false
false
false
+ 0
3365
@@ -26926,6 +30291,7 @@
false
false
false
+ 0
3366
@@ -26934,6 +30300,7 @@
false
false
false
+ 0
3367
@@ -26942,6 +30309,7 @@
false
false
false
+ 0
3368
@@ -26950,6 +30318,7 @@
false
false
false
+ 0
3369
@@ -26958,6 +30327,7 @@
false
false
false
+ 0
3370
@@ -26966,6 +30336,7 @@
false
false
false
+ 0
3371
@@ -26974,6 +30345,7 @@
false
false
false
+ 0
3372
@@ -26982,6 +30354,7 @@
false
false
false
+ 0
3373
@@ -26990,6 +30363,7 @@
false
false
false
+ 0
3374
@@ -26998,6 +30372,7 @@
false
false
false
+ 0
3375
@@ -27006,6 +30381,7 @@
false
false
false
+ 0
3376
@@ -27014,6 +30390,7 @@
false
false
false
+ 0
3377
@@ -27022,6 +30399,7 @@
false
false
false
+ 0
3378
@@ -27030,6 +30408,7 @@
false
false
false
+ 0
3379
@@ -27038,6 +30417,7 @@
false
false
false
+ 0
3380
@@ -27046,6 +30426,7 @@
false
false
false
+ 0
3381
@@ -27054,6 +30435,7 @@
false
false
false
+ 0
3382
@@ -27062,6 +30444,7 @@
false
false
false
+ 0
3383
@@ -27070,6 +30453,7 @@
false
false
false
+ 0
3384
@@ -27078,6 +30462,7 @@
false
false
false
+ 0
3385
@@ -27086,6 +30471,7 @@
false
false
false
+ 0
3386
@@ -27094,6 +30480,7 @@
false
false
false
+ 0
3387
@@ -27102,6 +30489,7 @@
false
false
false
+ 0
3388
@@ -27110,6 +30498,7 @@
false
false
false
+ 0
3389
@@ -27118,6 +30507,7 @@
false
false
false
+ 0
3390
@@ -27126,6 +30516,7 @@
false
false
false
+ 0
3391
@@ -27134,6 +30525,7 @@
false
false
false
+ 0
3392
@@ -27142,6 +30534,7 @@
false
false
false
+ 0
3393
@@ -27150,6 +30543,7 @@
false
false
false
+ 0
3394
@@ -27158,6 +30552,7 @@
false
false
false
+ 0
3395
@@ -27166,6 +30561,7 @@
false
false
false
+ 0
3396
@@ -27174,6 +30570,7 @@
false
false
false
+ 0
3397
@@ -27182,6 +30579,7 @@
false
false
false
+ 0
3398
@@ -27190,6 +30588,7 @@
false
false
false
+ 0
3399
@@ -27198,6 +30597,7 @@
false
false
false
+ 0
3400
@@ -27206,6 +30606,7 @@
false
false
false
+ 0
3401
@@ -27214,6 +30615,7 @@
false
false
false
+ 0
3402
@@ -27222,6 +30624,7 @@
false
false
false
+ 0
3403
@@ -27230,6 +30633,7 @@
false
false
false
+ 0
3404
@@ -27238,6 +30642,7 @@
false
false
false
+ 0
3405
@@ -27246,6 +30651,7 @@
false
false
false
+ 0
3406
@@ -27254,6 +30660,7 @@
false
false
false
+ 0
3407
@@ -27262,6 +30669,7 @@
false
false
false
+ 0
3408
@@ -27270,6 +30678,7 @@
false
false
false
+ 0
3409
@@ -27278,6 +30687,7 @@
false
false
false
+ 0
3410
@@ -27286,6 +30696,7 @@
false
false
false
+ 0
3411
@@ -27294,6 +30705,7 @@
false
false
false
+ 0
3412
@@ -27302,6 +30714,7 @@
false
false
false
+ 0
3413
@@ -27310,6 +30723,7 @@
false
false
false
+ 0
3414
@@ -27318,6 +30732,7 @@
false
false
false
+ 0
3415
@@ -27326,6 +30741,7 @@
false
false
false
+ 0
3416
@@ -27334,6 +30750,7 @@
false
false
false
+ 0
3417
@@ -27342,6 +30759,7 @@
false
false
false
+ 0
3418
@@ -27350,6 +30768,7 @@
false
false
false
+ 0
3419
@@ -27358,6 +30777,7 @@
false
false
false
+ 0
3420
@@ -27366,6 +30786,7 @@
false
false
false
+ 0
3421
@@ -27374,6 +30795,7 @@
false
false
false
+ 0
3422
@@ -27382,6 +30804,7 @@
false
false
false
+ 0
3423
@@ -27390,6 +30813,7 @@
false
false
false
+ 0
3424
@@ -27398,6 +30822,7 @@
false
false
false
+ 0
3425
@@ -27406,6 +30831,7 @@
false
false
false
+ 0
3426
@@ -27414,6 +30840,7 @@
false
false
false
+ 0
3427
@@ -27422,6 +30849,7 @@
false
false
false
+ 0
3428
@@ -27430,6 +30858,7 @@
false
false
false
+ 0
3429
@@ -27438,6 +30867,7 @@
false
false
false
+ 0
3430
@@ -27446,6 +30876,7 @@
false
false
false
+ 0
3431
@@ -27454,6 +30885,7 @@
false
false
false
+ 0
3432
@@ -27462,6 +30894,7 @@
false
false
false
+ 0
3433
@@ -27470,6 +30903,7 @@
false
false
false
+ 0
3434
@@ -27478,6 +30912,7 @@
false
false
false
+ 0
3435
@@ -27486,6 +30921,7 @@
false
false
false
+ 0
3436
@@ -27494,6 +30930,7 @@
false
false
false
+ 0
3437
@@ -27502,6 +30939,7 @@
false
false
false
+ 0
3438
@@ -27510,6 +30948,7 @@
false
false
false
+ 0
3439
@@ -27518,6 +30957,7 @@
false
false
false
+ 0
3440
@@ -27526,6 +30966,7 @@
false
false
false
+ 0
3441
@@ -27534,6 +30975,7 @@
false
false
false
+ 0
3442
@@ -27542,6 +30984,7 @@
false
false
false
+ 0
3443
@@ -27550,6 +30993,7 @@
false
false
false
+ 0
3444
@@ -27558,6 +31002,7 @@
false
false
false
+ 0
3445
@@ -27566,6 +31011,7 @@
false
false
false
+ 0
3446
@@ -27574,6 +31020,7 @@
false
false
false
+ 0
3447
@@ -27582,6 +31029,7 @@
false
false
false
+ 0
3448
@@ -27590,6 +31038,7 @@
false
false
false
+ 0
3449
@@ -27598,6 +31047,7 @@
false
false
false
+ 0
3450
@@ -27606,6 +31056,7 @@
false
false
false
+ 0
3451
@@ -27614,6 +31065,7 @@
false
false
false
+ 0
3452
@@ -27622,6 +31074,7 @@
false
false
false
+ 0
3453
@@ -27630,6 +31083,7 @@
false
false
false
+ 0
3454
@@ -27638,6 +31092,7 @@
false
false
false
+ 0
3455
@@ -27646,6 +31101,7 @@
false
false
false
+ 0
3456
@@ -27654,6 +31110,7 @@
false
false
false
+ 0
3457
@@ -27662,6 +31119,7 @@
false
false
false
+ 0
3458
@@ -27670,6 +31128,7 @@
false
false
false
+ 0
3459
@@ -27678,6 +31137,7 @@
false
false
false
+ 0
3460
@@ -27686,6 +31146,7 @@
false
false
false
+ 0
3461
@@ -27694,6 +31155,7 @@
false
false
false
+ 0
3462
@@ -27702,6 +31164,7 @@
false
false
false
+ 0
3463
@@ -27710,6 +31173,7 @@
false
false
false
+ 0
3464
@@ -27718,6 +31182,7 @@
false
false
false
+ 0
3465
@@ -27726,6 +31191,7 @@
false
false
false
+ 0
3466
@@ -27734,6 +31200,7 @@
false
false
false
+ 0
3467
@@ -27742,6 +31209,7 @@
false
false
false
+ 0
3468
@@ -27750,6 +31218,7 @@
false
false
false
+ 0
3469
@@ -27758,6 +31227,7 @@
false
false
false
+ 0
3470
@@ -27766,6 +31236,7 @@
false
false
false
+ 0
3471
@@ -27774,6 +31245,7 @@
false
false
false
+ 0
3472
@@ -27782,6 +31254,7 @@
false
false
false
+ 0
3473
@@ -27790,6 +31263,7 @@
false
false
false
+ 0
3474
@@ -27798,6 +31272,7 @@
false
false
false
+ 0
3475
@@ -27806,6 +31281,7 @@
false
false
false
+ 0
3476
@@ -27814,6 +31290,7 @@
false
false
false
+ 0
3477
@@ -27822,6 +31299,7 @@
false
false
false
+ 0
3478
@@ -27830,6 +31308,7 @@
false
false
false
+ 0
3479
@@ -27838,6 +31317,7 @@
false
false
false
+ 0
3480
@@ -27846,6 +31326,7 @@
false
false
false
+ 0
3481
@@ -27854,6 +31335,7 @@
false
false
false
+ 0
3482
@@ -27862,6 +31344,7 @@
false
false
false
+ 0
3483
@@ -27870,6 +31353,7 @@
false
false
false
+ 0
3484
@@ -27878,6 +31362,7 @@
false
false
false
+ 0
3485
@@ -27886,6 +31371,7 @@
false
false
false
+ 0
3486
@@ -27894,6 +31380,7 @@
false
false
false
+ 0
3487
@@ -27902,6 +31389,7 @@
false
false
false
+ 0
3488
@@ -27910,6 +31398,7 @@
false
false
false
+ 0
3489
@@ -27918,6 +31407,7 @@
false
false
false
+ 0
3490
@@ -27926,6 +31416,7 @@
false
false
false
+ 0
3491
@@ -27934,6 +31425,7 @@
false
false
false
+ 0
3492
@@ -27942,6 +31434,7 @@
false
false
false
+ 0
3493
@@ -27950,6 +31443,7 @@
false
false
false
+ 0
3494
@@ -27958,6 +31452,7 @@
false
false
false
+ 0
3495
@@ -27966,6 +31461,7 @@
false
false
false
+ 0
3496
@@ -27974,6 +31470,7 @@
false
false
false
+ 0
3497
@@ -27982,6 +31479,7 @@
false
false
false
+ 0
3498
@@ -27990,6 +31488,7 @@
false
false
false
+ 0
3499
@@ -27998,6 +31497,7 @@
false
false
false
+ 0
3500
@@ -28006,6 +31506,7 @@
false
false
false
+ 0
3501
@@ -28014,6 +31515,7 @@
false
false
false
+ 0
3502
@@ -28022,6 +31524,7 @@
false
false
false
+ 0
3503
@@ -28030,6 +31533,7 @@
false
false
false
+ 0
3504
@@ -28038,6 +31542,7 @@
false
false
false
+ 0
3505
@@ -28046,6 +31551,7 @@
false
false
false
+ 0
3506
@@ -28054,6 +31560,7 @@
false
false
false
+ 0
3507
@@ -28062,6 +31569,7 @@
false
false
false
+ 0
3508
@@ -28070,6 +31578,7 @@
false
false
false
+ 0
3509
@@ -28078,6 +31587,7 @@
false
false
false
+ 0
3510
@@ -28086,6 +31596,7 @@
false
false
false
+ 0
3511
@@ -28094,6 +31605,7 @@
false
false
false
+ 0
3512
@@ -28102,6 +31614,7 @@
false
false
false
+ 0
3513
@@ -28110,6 +31623,7 @@
false
false
false
+ 0
3514
@@ -28118,6 +31632,7 @@
false
false
false
+ 0
3515
@@ -28126,6 +31641,7 @@
false
false
false
+ 0
3516
@@ -28134,6 +31650,7 @@
false
false
false
+ 0
3517
@@ -28142,6 +31659,7 @@
false
false
false
+ 0
3518
@@ -28150,6 +31668,7 @@
false
false
false
+ 0
3519
@@ -28158,6 +31677,7 @@
false
false
false
+ 0
3520
@@ -28166,6 +31686,7 @@
false
false
false
+ 0
3521
@@ -28174,6 +31695,7 @@
false
false
false
+ 0
3522
@@ -28182,6 +31704,7 @@
false
false
false
+ 0
3523
@@ -28190,6 +31713,7 @@
false
false
false
+ 0
3524
@@ -28198,6 +31722,7 @@
false
false
false
+ 0
3525
@@ -28206,6 +31731,7 @@
false
false
false
+ 0
3526
@@ -28214,6 +31740,7 @@
false
false
false
+ 0
3527
@@ -28222,6 +31749,7 @@
false
false
false
+ 0
3528
@@ -28230,6 +31758,7 @@
false
false
false
+ 0
3529
@@ -28238,6 +31767,7 @@
false
false
false
+ 0
3530
@@ -28246,6 +31776,7 @@
false
false
false
+ 0
3531
@@ -28254,6 +31785,7 @@
false
false
false
+ 0
3532
@@ -28262,6 +31794,7 @@
false
false
false
+ 0
3533
@@ -28270,6 +31803,7 @@
false
false
false
+ 0
3534
@@ -28278,6 +31812,7 @@
false
false
false
+ 0
3535
@@ -28286,6 +31821,7 @@
false
false
false
+ 0
3536
@@ -28294,6 +31830,7 @@
false
false
false
+ 0
3537
@@ -28302,6 +31839,7 @@
false
false
false
+ 0
3538
@@ -28310,6 +31848,7 @@
false
false
false
+ 0
3539
@@ -28318,6 +31857,7 @@
false
false
false
+ 0
3540
@@ -28326,6 +31866,7 @@
false
false
false
+ 0
3541
@@ -28334,6 +31875,7 @@
false
false
false
+ 0
3542
@@ -28342,6 +31884,7 @@
false
false
false
+ 0
3543
@@ -28350,6 +31893,7 @@
false
false
false
+ 0
3544
@@ -28358,6 +31902,7 @@
false
false
false
+ 0
3545
@@ -28366,6 +31911,7 @@
false
false
false
+ 0
3546
@@ -28374,6 +31920,7 @@
false
false
false
+ 0
3547
@@ -28382,6 +31929,7 @@
false
false
false
+ 0
3548
@@ -28390,6 +31938,7 @@
false
false
false
+ 0
3549
@@ -28398,6 +31947,7 @@
false
false
false
+ 0
3550
@@ -28406,6 +31956,7 @@
false
false
false
+ 0
3551
@@ -28414,6 +31965,7 @@
false
false
false
+ 0
3552
@@ -28422,6 +31974,7 @@
false
false
false
+ 0
3553
@@ -28430,6 +31983,7 @@
false
false
false
+ 0
3554
@@ -28438,6 +31992,7 @@
false
false
false
+ 0
3555
@@ -28446,6 +32001,7 @@
false
false
false
+ 0
3556
@@ -28454,6 +32010,7 @@
false
false
false
+ 0
3557
@@ -28462,6 +32019,7 @@
false
false
false
+ 0
3558
@@ -28470,6 +32028,7 @@
false
false
false
+ 0
3559
@@ -28478,6 +32037,7 @@
false
false
false
+ 0
3560
@@ -28486,6 +32046,7 @@
false
false
false
+ 0
3561
@@ -28494,6 +32055,7 @@
false
false
false
+ 0
3562
@@ -28502,6 +32064,7 @@
false
false
false
+ 0
3563
@@ -28510,6 +32073,7 @@
false
false
false
+ 0
3564
@@ -28518,6 +32082,7 @@
false
false
false
+ 0
3565
@@ -28526,6 +32091,7 @@
false
false
false
+ 0
3566
@@ -28534,6 +32100,7 @@
false
false
false
+ 0
3567
@@ -28542,6 +32109,7 @@
false
false
false
+ 0
3568
@@ -28550,6 +32118,7 @@
false
false
false
+ 0
3569
@@ -28558,6 +32127,7 @@
false
false
false
+ 0
3570
@@ -28566,6 +32136,7 @@
false
false
false
+ 0
3571
@@ -28574,6 +32145,7 @@
false
false
false
+ 0
3572
@@ -28582,6 +32154,7 @@
false
false
false
+ 0
3573
@@ -28590,6 +32163,7 @@
false
false
false
+ 0
3574
@@ -28598,6 +32172,7 @@
false
false
false
+ 0
3575
@@ -28606,6 +32181,7 @@
false
false
false
+ 0
3576
@@ -28614,6 +32190,7 @@
false
false
false
+ 0
3577
@@ -28622,6 +32199,7 @@
false
false
false
+ 0
3578
@@ -28630,6 +32208,7 @@
false
false
false
+ 0
3579
@@ -28638,6 +32217,7 @@
false
false
false
+ 0
3580
@@ -28646,6 +32226,7 @@
false
false
false
+ 0
3581
@@ -28654,6 +32235,7 @@
false
false
false
+ 0
3582
@@ -28662,6 +32244,7 @@
false
false
false
+ 0
3583
@@ -28670,6 +32253,7 @@
false
false
false
+ 0
3584
@@ -28678,6 +32262,7 @@
false
false
false
+ 0
3585
@@ -28686,6 +32271,7 @@
false
false
false
+ 0
3586
@@ -28694,6 +32280,7 @@
false
false
false
+ 0
3587
@@ -28702,6 +32289,7 @@
false
false
false
+ 0
3588
@@ -28710,6 +32298,7 @@
false
false
false
+ 0
3589
@@ -28718,6 +32307,7 @@
false
false
false
+ 0
3590
@@ -28726,6 +32316,7 @@
false
false
false
+ 0
3591
@@ -28734,6 +32325,7 @@
false
false
false
+ 0
3592
@@ -28742,6 +32334,7 @@
false
false
false
+ 0
3593
@@ -28750,6 +32343,7 @@
false
false
false
+ 0
3594
@@ -28758,6 +32352,7 @@
false
false
false
+ 0
3595
@@ -28766,6 +32361,7 @@
false
false
false
+ 0
3596
@@ -28774,6 +32370,7 @@
false
false
false
+ 0
3597
@@ -28782,6 +32379,7 @@
false
false
false
+ 0
3598
@@ -28790,6 +32388,7 @@
false
false
false
+ 0
3599
@@ -28798,6 +32397,7 @@
false
false
false
+ 0
3600
@@ -28806,6 +32406,7 @@
false
false
false
+ 0
3601
@@ -28814,6 +32415,7 @@
false
false
false
+ 0
3602
@@ -28822,6 +32424,7 @@
false
false
false
+ 0
3603
@@ -28830,6 +32433,7 @@
false
false
false
+ 0
3604
@@ -28838,6 +32442,7 @@
false
false
false
+ 0
3605
@@ -28846,6 +32451,7 @@
false
false
false
+ 0
3606
@@ -28854,6 +32460,7 @@
false
false
false
+ 0
3607
@@ -28862,6 +32469,7 @@
false
false
false
+ 0
3608
@@ -28870,6 +32478,7 @@
false
false
false
+ 0
3609
@@ -28878,6 +32487,7 @@
false
false
false
+ 0
3610
@@ -28886,6 +32496,7 @@
false
false
false
+ 0
3611
@@ -28894,6 +32505,7 @@
false
false
false
+ 0
3612
@@ -28902,6 +32514,7 @@
false
false
false
+ 0
3613
@@ -28910,6 +32523,7 @@
false
false
false
+ 0
3614
@@ -28918,6 +32532,7 @@
false
false
false
+ 0
3615
@@ -28926,6 +32541,7 @@
false
false
false
+ 0
3616
@@ -28934,6 +32550,7 @@
false
false
false
+ 0
3617
@@ -28942,6 +32559,7 @@
false
false
false
+ 0
3618
@@ -28950,6 +32568,7 @@
false
false
false
+ 0
3619
@@ -28958,6 +32577,7 @@
false
false
false
+ 0
3620
@@ -28966,6 +32586,7 @@
false
false
false
+ 0
3621
@@ -28974,6 +32595,7 @@
false
false
false
+ 0
3622
@@ -28982,6 +32604,7 @@
false
false
false
+ 0
3623
@@ -28990,6 +32613,7 @@
false
false
false
+ 0
3624
@@ -28998,6 +32622,7 @@
false
false
false
+ 0
3625
@@ -29006,6 +32631,7 @@
false
false
false
+ 0
3626
@@ -29014,6 +32640,7 @@
false
false
false
+ 0
3627
@@ -29022,6 +32649,7 @@
false
false
false
+ 0
3628
@@ -29030,6 +32658,7 @@
false
false
false
+ 0
3629
@@ -29038,6 +32667,7 @@
false
false
false
+ 0
3630
@@ -29046,6 +32676,7 @@
false
false
false
+ 0
3631
@@ -29054,6 +32685,7 @@
false
false
false
+ 0
3632
@@ -29062,6 +32694,7 @@
false
false
false
+ 0
3633
@@ -29070,6 +32703,7 @@
false
false
false
+ 0
3634
@@ -29078,6 +32712,7 @@
false
false
false
+ 0
3635
@@ -29086,6 +32721,7 @@
false
false
false
+ 0
3636
@@ -29094,6 +32730,7 @@
false
false
false
+ 0
3637
@@ -29102,6 +32739,7 @@
false
false
false
+ 0
3638
@@ -29110,6 +32748,7 @@
false
false
false
+ 0
3639
@@ -29118,6 +32757,7 @@
false
false
false
+ 0
3640
@@ -29126,6 +32766,7 @@
false
false
false
+ 0
3641
@@ -29134,6 +32775,7 @@
false
false
false
+ 0
3642
@@ -29142,6 +32784,7 @@
false
false
false
+ 0
3643
@@ -29150,6 +32793,7 @@
false
false
false
+ 0
3644
@@ -29158,6 +32802,7 @@
false
false
false
+ 0
3645
@@ -29166,6 +32811,7 @@
false
false
false
+ 0
3646
@@ -29174,6 +32820,7 @@
false
false
false
+ 0
3647
@@ -29182,6 +32829,7 @@
false
false
false
+ 0
3648
@@ -29190,6 +32838,7 @@
false
false
false
+ 0
3649
@@ -29198,6 +32847,7 @@
false
false
false
+ 0
3650
@@ -29206,6 +32856,7 @@
false
false
false
+ 0
3651
@@ -29214,6 +32865,7 @@
false
false
false
+ 0
3652
@@ -29222,6 +32874,7 @@
false
false
false
+ 0
3653
@@ -29230,6 +32883,7 @@
false
false
false
+ 0
3654
@@ -29238,6 +32892,7 @@
false
false
false
+ 0
3655
@@ -29246,6 +32901,7 @@
false
false
false
+ 0
3656
@@ -29254,6 +32910,7 @@
false
false
false
+ 0
3657
@@ -29262,6 +32919,7 @@
false
false
false
+ 0
3658
@@ -29270,6 +32928,7 @@
false
false
false
+ 0
3659
@@ -29278,6 +32937,7 @@
false
false
false
+ 0
3660
@@ -29286,6 +32946,7 @@
false
false
false
+ 0
3661
@@ -29294,6 +32955,7 @@
false
false
false
+ 0
3662
@@ -29302,6 +32964,7 @@
false
false
false
+ 0
3663
@@ -29310,6 +32973,7 @@
false
false
false
+ 0
3664
@@ -29318,6 +32982,7 @@
false
false
false
+ 0
3665
@@ -29326,6 +32991,7 @@
false
false
false
+ 0
3666
@@ -29334,6 +33000,7 @@
false
false
false
+ 0
3667
@@ -29342,6 +33009,7 @@
false
false
false
+ 0
3668
@@ -29350,6 +33018,7 @@
false
false
false
+ 0
3669
@@ -29358,6 +33027,7 @@
false
false
false
+ 0
3670
@@ -29366,6 +33036,7 @@
false
false
false
+ 0
3671
@@ -29374,6 +33045,7 @@
false
false
false
+ 0
3672
@@ -29382,6 +33054,7 @@
false
false
false
+ 0
3673
@@ -29390,6 +33063,7 @@
false
false
false
+ 0
3674
@@ -29398,6 +33072,7 @@
false
false
false
+ 0
3675
@@ -29406,6 +33081,7 @@
false
false
false
+ 0
3676
@@ -29414,6 +33090,7 @@
false
false
false
+ 0
3677
@@ -29422,6 +33099,7 @@
false
false
false
+ 0
3678
@@ -29430,6 +33108,7 @@
false
false
false
+ 0
3679
@@ -29438,6 +33117,7 @@
false
false
false
+ 0
3680
@@ -29446,6 +33126,7 @@
false
false
false
+ 0
3681
@@ -29454,6 +33135,7 @@
false
false
false
+ 0
3682
@@ -29462,6 +33144,7 @@
false
false
false
+ 0
3683
@@ -29470,6 +33153,7 @@
false
false
false
+ 0
3684
@@ -29478,6 +33162,7 @@
false
false
false
+ 0
3685
@@ -29486,6 +33171,7 @@
false
false
false
+ 0
3686
@@ -29494,6 +33180,7 @@
false
false
false
+ 0
3687
@@ -29502,6 +33189,7 @@
false
false
false
+ 0
3688
@@ -29510,6 +33198,7 @@
false
false
false
+ 0
3689
@@ -29518,6 +33207,7 @@
false
false
false
+ 0
3690
@@ -29526,6 +33216,7 @@
false
false
false
+ 0
3691
@@ -29534,6 +33225,7 @@
false
false
false
+ 0
3692
@@ -29542,6 +33234,7 @@
false
false
false
+ 0
3693
@@ -29550,6 +33243,7 @@
false
false
false
+ 0
3694
@@ -29558,6 +33252,7 @@
false
false
false
+ 0
3695
@@ -29566,6 +33261,7 @@
false
false
false
+ 0
3696
@@ -29574,6 +33270,7 @@
false
false
false
+ 0
3697
@@ -29582,6 +33279,7 @@
false
false
false
+ 0
3698
@@ -29590,6 +33288,7 @@
false
false
false
+ 0
3699
@@ -29598,6 +33297,7 @@
false
false
false
+ 0
3700
@@ -29606,6 +33306,7 @@
false
false
false
+ 0
3701
@@ -29614,6 +33315,7 @@
false
false
false
+ 0
3702
@@ -29622,6 +33324,7 @@
false
false
false
+ 0
3703
@@ -29630,6 +33333,7 @@
false
false
false
+ 0
3704
@@ -29638,6 +33342,7 @@
false
false
false
+ 0
3705
@@ -29646,6 +33351,7 @@
false
false
false
+ 0
3706
@@ -29654,6 +33360,7 @@
false
false
false
+ 0
3707
@@ -29662,6 +33369,7 @@
false
false
false
+ 0
3708
@@ -29670,6 +33378,7 @@
false
false
false
+ 0
3709
@@ -29678,6 +33387,7 @@
false
false
false
+ 0
3710
@@ -29686,6 +33396,7 @@
false
false
false
+ 0
3711
@@ -29694,6 +33405,7 @@
false
false
false
+ 0
3712
@@ -29702,6 +33414,7 @@
false
false
false
+ 0
3713
@@ -29710,6 +33423,7 @@
false
false
false
+ 0
3714
@@ -29718,6 +33432,7 @@
false
false
false
+ 0
3715
@@ -29726,6 +33441,7 @@
false
false
false
+ 0
3716
@@ -29734,6 +33450,7 @@
false
false
false
+ 0
3717
@@ -29742,6 +33459,7 @@
false
false
false
+ 0
3718
@@ -29750,6 +33468,7 @@
false
false
false
+ 0
3719
@@ -29758,6 +33477,7 @@
false
false
false
+ 0
3720
@@ -29766,6 +33486,7 @@
false
false
false
+ 0
3721
@@ -29774,6 +33495,7 @@
false
false
false
+ 0
3722
@@ -29782,6 +33504,7 @@
false
false
false
+ 0
3723
@@ -29790,6 +33513,7 @@
false
false
false
+ 0
3724
@@ -29798,6 +33522,7 @@
false
false
false
+ 0
3725
@@ -29806,6 +33531,7 @@
false
false
false
+ 0
3726
@@ -29814,6 +33540,7 @@
false
false
false
+ 0
3727
@@ -29822,6 +33549,7 @@
false
false
false
+ 0
3728
@@ -29830,6 +33558,7 @@
false
false
false
+ 0
3729
@@ -29838,6 +33567,7 @@
false
false
false
+ 0
3730
@@ -29846,6 +33576,7 @@
false
false
false
+ 0
3731
@@ -29854,6 +33585,7 @@
false
false
false
+ 0
3732
@@ -29862,6 +33594,7 @@
false
false
false
+ 0
3733
@@ -29870,6 +33603,7 @@
false
false
false
+ 0
3734
@@ -29878,6 +33612,7 @@
false
false
false
+ 0
3735
@@ -29886,6 +33621,7 @@
false
false
false
+ 0
3736
@@ -29894,6 +33630,7 @@
false
false
false
+ 0
3737
@@ -29902,6 +33639,7 @@
false
false
false
+ 0
3738
@@ -29910,6 +33648,7 @@
false
false
false
+ 0
3739
@@ -29918,6 +33657,7 @@
false
false
false
+ 0
3740
@@ -29926,6 +33666,7 @@
false
false
false
+ 0
3741
@@ -29934,6 +33675,7 @@
false
false
false
+ 0
3742
@@ -29942,6 +33684,7 @@
false
false
false
+ 0
3743
@@ -29950,6 +33693,7 @@
false
false
false
+ 0
3744
@@ -29958,6 +33702,7 @@
false
false
false
+ 0
3745
@@ -29966,6 +33711,7 @@
false
false
false
+ 0
3746
@@ -29974,6 +33720,7 @@
false
false
false
+ 0
3747
@@ -29982,6 +33729,7 @@
false
false
false
+ 0
3748
@@ -29990,6 +33738,7 @@
false
false
false
+ 0
3749
@@ -29998,6 +33747,7 @@
false
false
false
+ 0
3750
@@ -30006,6 +33756,7 @@
false
false
false
+ 0
3751
@@ -30014,6 +33765,7 @@
false
false
false
+ 0
3752
@@ -30022,6 +33774,7 @@
false
false
false
+ 0
3753
@@ -30030,6 +33783,7 @@
false
false
false
+ 0
3754
@@ -30038,6 +33792,7 @@
false
false
false
+ 0
3755
@@ -30046,6 +33801,7 @@
false
false
false
+ 0
3756
@@ -30054,6 +33810,7 @@
false
false
false
+ 0
3757
@@ -30062,6 +33819,7 @@
false
false
false
+ 0
3758
@@ -30070,6 +33828,7 @@
false
false
false
+ 0
3759
@@ -30078,6 +33837,7 @@
false
false
false
+ 0
3760
@@ -30086,6 +33846,7 @@
false
false
false
+ 0
3761
@@ -30094,6 +33855,7 @@
false
false
false
+ 0
3762
@@ -30102,6 +33864,7 @@
false
false
false
+ 0
3763
@@ -30110,6 +33873,7 @@
false
false
false
+ 0
3764
@@ -30118,6 +33882,7 @@
false
false
false
+ 0
3765
@@ -30126,6 +33891,7 @@
false
false
false
+ 0
3766
@@ -30134,6 +33900,7 @@
false
false
false
+ 0
3767
@@ -30142,6 +33909,7 @@
false
false
false
+ 0
3768
@@ -30150,6 +33918,7 @@
false
false
false
+ 0
3769
@@ -30158,6 +33927,7 @@
false
false
false
+ 0
3770
@@ -30166,6 +33936,7 @@
false
false
false
+ 0
3771
@@ -30174,6 +33945,7 @@
false
false
false
+ 0
3772
@@ -30182,6 +33954,7 @@
false
false
false
+ 0
3773
@@ -30190,6 +33963,7 @@
false
false
false
+ 0
3774
@@ -30198,6 +33972,7 @@
false
false
false
+ 0
3775
@@ -30206,6 +33981,7 @@
false
false
false
+ 0
3776
@@ -30214,6 +33990,7 @@
false
false
false
+ 0
3777
@@ -30222,6 +33999,7 @@
false
false
false
+ 0
3778
@@ -30230,6 +34008,7 @@
false
false
false
+ 0
3779
@@ -30238,6 +34017,7 @@
false
false
false
+ 0
3780
@@ -30246,6 +34026,7 @@
false
false
false
+ 0
3781
@@ -30254,6 +34035,7 @@
false
false
false
+ 0
3782
@@ -30262,6 +34044,7 @@
false
false
false
+ 0
3783
@@ -30270,6 +34053,7 @@
false
false
false
+ 0
3784
@@ -30278,6 +34062,7 @@
false
false
false
+ 0
3785
@@ -30286,6 +34071,7 @@
false
false
false
+ 0
3786
@@ -30294,6 +34080,7 @@
false
false
false
+ 0
3787
@@ -30302,6 +34089,7 @@
false
false
false
+ 0
3788
@@ -30310,6 +34098,7 @@
false
false
false
+ 0
3789
@@ -30318,6 +34107,7 @@
false
false
false
+ 0
3790
@@ -30326,6 +34116,7 @@
false
false
false
+ 0
3791
@@ -30334,6 +34125,7 @@
false
false
false
+ 0
3792
@@ -30342,6 +34134,7 @@
false
false
false
+ 0
3793
@@ -30350,6 +34143,7 @@
false
false
false
+ 0
3794
@@ -30358,6 +34152,7 @@
false
false
false
+ 0
3795
@@ -30366,6 +34161,7 @@
false
false
false
+ 0
3796
@@ -30374,6 +34170,7 @@
false
false
false
+ 0
3797
@@ -30382,6 +34179,7 @@
false
false
false
+ 0
3798
@@ -30390,6 +34188,7 @@
false
false
false
+ 0
3799
@@ -30398,6 +34197,7 @@
false
false
false
+ 0
3800
@@ -30406,6 +34206,7 @@
false
false
false
+ 0
3801
@@ -30414,6 +34215,7 @@
false
false
false
+ 0
3802
@@ -30422,6 +34224,7 @@
false
false
false
+ 0
3803
@@ -30430,6 +34233,7 @@
false
false
false
+ 0
3804
@@ -30438,6 +34242,7 @@
false
false
false
+ 0
3805
@@ -30446,6 +34251,7 @@
false
false
false
+ 0
3806
@@ -30454,6 +34260,7 @@
false
false
false
+ 0
3807
@@ -30462,6 +34269,7 @@
false
false
false
+ 0
3808
@@ -30470,6 +34278,7 @@
false
false
false
+ 0
3809
@@ -30478,6 +34287,7 @@
false
false
false
+ 0
3810
@@ -30486,6 +34296,7 @@
false
false
false
+ 0
3811
@@ -30494,6 +34305,7 @@
false
false
false
+ 0
3812
@@ -30502,6 +34314,7 @@
false
false
false
+ 0
3813
@@ -30510,6 +34323,7 @@
false
false
false
+ 0
3814
@@ -30518,6 +34332,7 @@
false
false
false
+ 0
3815
@@ -30526,6 +34341,7 @@
false
false
false
+ 0
3816
@@ -30534,6 +34350,7 @@
false
false
false
+ 0
3817
@@ -30542,6 +34359,7 @@
false
false
false
+ 0
3818
@@ -30550,6 +34368,7 @@
false
false
false
+ 0
3819
@@ -30558,6 +34377,7 @@
false
false
false
+ 0
3820
@@ -30566,6 +34386,7 @@
false
false
false
+ 0
3821
@@ -30574,6 +34395,7 @@
false
false
false
+ 0
3822
@@ -30582,6 +34404,7 @@
false
false
false
+ 0
3823
@@ -30590,6 +34413,7 @@
false
false
false
+ 0
3824
@@ -30598,6 +34422,7 @@
false
false
false
+ 0
3825
@@ -30606,6 +34431,7 @@
false
false
false
+ 0
3826
@@ -30614,6 +34440,7 @@
false
false
false
+ 0
3827
@@ -30622,6 +34449,7 @@
false
false
false
+ 0
3828
@@ -30630,6 +34458,7 @@
false
false
false
+ 0
3829
@@ -30638,6 +34467,7 @@
false
false
false
+ 0
3830
@@ -30646,6 +34476,7 @@
false
false
false
+ 0
3831
@@ -30654,6 +34485,7 @@
false
false
false
+ 0
3832
@@ -30662,6 +34494,7 @@
false
false
false
+ 0
3833
@@ -30670,6 +34503,7 @@
false
false
false
+ 0
3834
@@ -30678,6 +34512,7 @@
false
false
false
+ 0
3835
@@ -30686,6 +34521,7 @@
false
false
false
+ 0
3836
@@ -30694,6 +34530,7 @@
false
false
false
+ 0
3837
@@ -30702,6 +34539,7 @@
false
false
false
+ 0
3838
@@ -30710,6 +34548,7 @@
false
false
false
+ 0
3839
@@ -30718,6 +34557,7 @@
false
false
false
+ 0
3840
@@ -30726,6 +34566,7 @@
false
false
false
+ 0
3841
@@ -30734,6 +34575,7 @@
false
false
false
+ 0
3842
@@ -30742,6 +34584,7 @@
false
false
false
+ 0
3843
@@ -30750,6 +34593,7 @@
false
false
false
+ 0
3844
@@ -30758,6 +34602,7 @@
false
false
false
+ 0
3845
@@ -30766,6 +34611,7 @@
false
false
false
+ 0
3846
@@ -30774,6 +34620,7 @@
false
false
false
+ 0
3847
@@ -30782,6 +34629,7 @@
false
false
false
+ 0
3848
@@ -30790,6 +34638,7 @@
false
false
false
+ 0
3849
@@ -30798,6 +34647,7 @@
false
false
false
+ 0
3850
@@ -30806,6 +34656,7 @@
false
false
false
+ 0
3851
@@ -30814,6 +34665,7 @@
false
false
false
+ 0
3852
@@ -30822,6 +34674,7 @@
false
false
false
+ 0
3853
@@ -30830,6 +34683,7 @@
false
false
false
+ 0
3854
@@ -30838,6 +34692,7 @@
false
false
false
+ 0
3855
@@ -30846,6 +34701,7 @@
false
false
false
+ 0
3856
@@ -30854,6 +34710,7 @@
false
false
false
+ 0
3857
@@ -30862,6 +34719,7 @@
false
false
false
+ 0
3858
@@ -30870,6 +34728,7 @@
false
false
false
+ 0
3859
@@ -30878,6 +34737,7 @@
false
false
false
+ 0
3860
@@ -30886,6 +34746,7 @@
false
false
false
+ 0
3861
@@ -30894,6 +34755,7 @@
false
false
false
+ 0
3862
@@ -30902,6 +34764,7 @@
false
false
false
+ 0
3863
@@ -30910,6 +34773,7 @@
false
false
false
+ 0
3864
@@ -30918,6 +34782,7 @@
false
false
false
+ 0
3865
@@ -30926,6 +34791,7 @@
false
false
false
+ 0
3866
@@ -30934,6 +34800,7 @@
false
false
false
+ 0
3867
@@ -30942,6 +34809,7 @@
false
false
false
+ 0
3868
@@ -30950,6 +34818,7 @@
false
false
false
+ 0
3869
@@ -30958,6 +34827,7 @@
false
false
false
+ 0
3870
@@ -30966,6 +34836,7 @@
false
false
false
+ 0
3871
@@ -30974,6 +34845,7 @@
false
false
false
+ 0
3872
@@ -30982,6 +34854,7 @@
false
false
false
+ 0
3873
@@ -30990,6 +34863,7 @@
false
false
false
+ 0
3874
@@ -30998,6 +34872,7 @@
false
false
false
+ 0
3875
@@ -31006,6 +34881,7 @@
false
false
false
+ 0
3876
@@ -31014,6 +34890,7 @@
false
false
false
+ 0
3877
@@ -31022,6 +34899,7 @@
false
false
false
+ 0
3878
@@ -31030,6 +34908,7 @@
false
false
false
+ 0
3879
@@ -31038,6 +34917,7 @@
false
false
false
+ 0
3880
@@ -31046,6 +34926,7 @@
false
false
false
+ 0
3881
@@ -31054,6 +34935,7 @@
false
false
false
+ 0
3882
@@ -31062,6 +34944,7 @@
false
false
false
+ 0
3883
@@ -31070,6 +34953,7 @@
false
false
false
+ 0
3884
@@ -31078,6 +34962,7 @@
false
false
false
+ 0
3885
@@ -31086,6 +34971,7 @@
false
false
false
+ 0
3886
@@ -31094,6 +34980,7 @@
false
false
false
+ 0
3887
@@ -31102,6 +34989,7 @@
false
false
false
+ 0
3888
@@ -31110,6 +34998,7 @@
false
false
false
+ 0
3889
@@ -31118,6 +35007,7 @@
false
false
false
+ 0
3890
@@ -31126,6 +35016,7 @@
false
false
false
+ 0
3891
@@ -31134,6 +35025,7 @@
false
false
false
+ 0
3892
@@ -31142,6 +35034,7 @@
false
false
false
+ 0
3893
@@ -31150,6 +35043,7 @@
false
false
false
+ 0
3894
@@ -31158,6 +35052,7 @@
false
false
false
+ 0
3895
@@ -31166,6 +35061,7 @@
false
false
false
+ 0
3896
@@ -31174,6 +35070,7 @@
false
false
false
+ 0
3897
@@ -31182,6 +35079,7 @@
false
false
false
+ 0
3898
@@ -31190,6 +35088,7 @@
false
false
false
+ 0
3899
@@ -31198,6 +35097,7 @@
false
false
false
+ 0
3900
@@ -31206,6 +35106,7 @@
false
false
false
+ 0
3901
@@ -31214,6 +35115,7 @@
false
false
false
+ 0
3902
@@ -31222,6 +35124,7 @@
false
false
false
+ 0
3903
@@ -31230,6 +35133,7 @@
false
false
false
+ 0
3904
@@ -31238,6 +35142,7 @@
false
false
false
+ 0
3905
@@ -31246,6 +35151,7 @@
false
false
false
+ 0
3906
@@ -31254,6 +35160,7 @@
false
false
false
+ 0
3907
@@ -31262,6 +35169,7 @@
false
false
false
+ 0
3908
@@ -31270,6 +35178,7 @@
false
false
false
+ 0
3909
@@ -31278,6 +35187,7 @@
false
false
false
+ 0
3910
@@ -31286,6 +35196,7 @@
false
false
false
+ 0
3911
@@ -31294,6 +35205,7 @@
false
false
false
+ 0
3912
@@ -31302,6 +35214,7 @@
false
false
false
+ 0
3913
@@ -31310,6 +35223,7 @@
false
false
false
+ 0
3914
@@ -31318,6 +35232,7 @@
false
false
false
+ 0
3915
@@ -31326,6 +35241,7 @@
false
false
false
+ 0
3916
@@ -31334,6 +35250,7 @@
false
false
false
+ 0
3917
@@ -31342,6 +35259,7 @@
false
false
false
+ 0
3918
@@ -31350,6 +35268,7 @@
false
false
false
+ 0
3919
@@ -31358,6 +35277,7 @@
false
false
false
+ 0
3920
@@ -31366,6 +35286,7 @@
false
false
false
+ 0
3921
@@ -31374,6 +35295,7 @@
false
false
false
+ 0
3922
@@ -31382,6 +35304,7 @@
false
false
false
+ 0
3923
@@ -31390,6 +35313,7 @@
false
false
false
+ 0
3924
@@ -31398,6 +35322,7 @@
false
false
false
+ 0
3925
@@ -31406,6 +35331,7 @@
false
false
false
+ 0
3926
@@ -31414,6 +35340,7 @@
false
false
false
+ 0
3927
@@ -31422,6 +35349,7 @@
false
false
false
+ 0
3928
@@ -31430,6 +35358,7 @@
false
false
false
+ 0
3929
@@ -31438,6 +35367,7 @@
false
false
false
+ 0
3930
@@ -31446,6 +35376,7 @@
false
false
false
+ 0
3931
@@ -31454,6 +35385,7 @@
false
false
false
+ 0
3932
@@ -31462,6 +35394,7 @@
false
false
false
+ 0
3933
@@ -31470,6 +35403,7 @@
false
false
false
+ 0
3934
@@ -31478,6 +35412,7 @@
false
false
false
+ 0
3935
@@ -31486,6 +35421,7 @@
false
false
false
+ 0
3936
@@ -31494,6 +35430,7 @@
false
false
false
+ 0
3937
@@ -31502,6 +35439,7 @@
false
false
false
+ 0
3938
@@ -31510,6 +35448,7 @@
false
false
false
+ 0
3939
@@ -31518,6 +35457,7 @@
false
false
false
+ 0
3940
@@ -31526,6 +35466,7 @@
false
false
false
+ 0
3941
@@ -31534,6 +35475,7 @@
false
false
false
+ 0
3942
@@ -31542,6 +35484,7 @@
false
false
false
+ 0
3943
@@ -31550,6 +35493,7 @@
false
false
false
+ 0
3944
@@ -31558,6 +35502,7 @@
false
false
false
+ 0
3945
@@ -31566,6 +35511,7 @@
false
false
false
+ 0
3946
@@ -31574,6 +35520,7 @@
false
false
false
+ 0
3947
@@ -31582,6 +35529,7 @@
false
false
false
+ 0
3948
@@ -31590,6 +35538,7 @@
false
false
false
+ 0
3949
@@ -31598,6 +35547,7 @@
false
false
false
+ 0
3950
@@ -31606,6 +35556,7 @@
false
false
false
+ 0
3951
@@ -31614,6 +35565,7 @@
false
false
false
+ 0
3952
@@ -31622,6 +35574,7 @@
false
false
false
+ 0
3953
@@ -31630,6 +35583,7 @@
false
false
false
+ 0
3954
@@ -31638,6 +35592,7 @@
false
false
false
+ 0
3955
@@ -31646,6 +35601,7 @@
false
false
false
+ 0
3956
@@ -31654,6 +35610,7 @@
false
false
false
+ 0
3957
@@ -31662,6 +35619,7 @@
false
false
false
+ 0
3958
@@ -31670,6 +35628,7 @@
false
false
false
+ 0
3959
@@ -31678,6 +35637,7 @@
false
false
false
+ 0
3960
@@ -31686,6 +35646,7 @@
false
false
false
+ 0
3961
@@ -31694,6 +35655,7 @@
false
false
false
+ 0
3962
@@ -31702,6 +35664,7 @@
false
false
false
+ 0
3963
@@ -31710,6 +35673,7 @@
false
false
false
+ 0
3964
@@ -31718,6 +35682,7 @@
false
false
false
+ 0
3965
@@ -31726,6 +35691,7 @@
false
false
false
+ 0
3966
@@ -31734,6 +35700,7 @@
false
false
false
+ 0
3967
@@ -31742,6 +35709,7 @@
false
false
false
+ 0
3968
@@ -31750,6 +35718,7 @@
false
false
false
+ 0
3969
@@ -31758,6 +35727,7 @@
false
false
false
+ 0
3970
@@ -31766,6 +35736,7 @@
false
false
false
+ 0
3971
@@ -31774,6 +35745,7 @@
false
false
false
+ 0
3972
@@ -31782,6 +35754,7 @@
false
false
false
+ 0
3973
@@ -31790,6 +35763,7 @@
false
false
false
+ 0
3974
@@ -31798,6 +35772,7 @@
false
false
false
+ 0
3975
@@ -31806,6 +35781,7 @@
false
false
false
+ 0
3976
@@ -31814,6 +35790,7 @@
false
false
false
+ 0
3977
@@ -31822,6 +35799,7 @@
false
false
false
+ 0
3978
@@ -31830,6 +35808,7 @@
false
false
false
+ 0
3979
@@ -31838,6 +35817,7 @@
false
false
false
+ 0
3980
@@ -31846,6 +35826,7 @@
false
false
false
+ 0
3981
@@ -31854,6 +35835,7 @@
false
false
false
+ 0
3982
@@ -31862,6 +35844,7 @@
false
false
false
+ 0
3983
@@ -31870,6 +35853,7 @@
false
false
false
+ 0
3984
@@ -31878,6 +35862,7 @@
false
false
false
+ 0
3985
@@ -31886,6 +35871,7 @@
false
false
false
+ 0
3986
@@ -31894,6 +35880,7 @@
false
false
false
+ 0
3987
@@ -31902,6 +35889,7 @@
false
false
false
+ 0
3988
@@ -31910,6 +35898,7 @@
false
false
false
+ 0
3989
@@ -31918,6 +35907,7 @@
false
false
false
+ 0
3990
@@ -31926,6 +35916,7 @@
false
false
false
+ 0
3991
@@ -31934,6 +35925,7 @@
false
false
false
+ 0
3992
@@ -31942,6 +35934,7 @@
false
false
false
+ 0
3993
@@ -31950,6 +35943,7 @@
false
false
false
+ 0
3994
@@ -31958,6 +35952,7 @@
false
false
false
+ 0
3995
@@ -31966,6 +35961,7 @@
false
false
false
+ 0
3996
@@ -31974,6 +35970,7 @@
false
false
false
+ 0
3997
@@ -31982,6 +35979,7 @@
false
false
false
+ 0
3998
@@ -31990,6 +35988,7 @@
false
false
false
+ 0
3999
@@ -31998,6 +35997,7 @@
false
false
false
+ 0
4000
@@ -32006,6 +36006,7 @@
false
false
false
+ 0
4001
@@ -32014,6 +36015,7 @@
false
false
false
+ 0
4002
@@ -32022,6 +36024,7 @@
false
false
false
+ 0
4003
@@ -32030,6 +36033,7 @@
false
false
false
+ 0
4004
@@ -32038,6 +36042,7 @@
false
false
false
+ 0
4005
@@ -32046,6 +36051,7 @@
false
false
false
+ 0
4006
@@ -32054,6 +36060,7 @@
false
false
false
+ 0
4007
@@ -32062,6 +36069,7 @@
false
false
false
+ 0
4008
@@ -32070,6 +36078,7 @@
false
false
false
+ 0
4009
@@ -32078,6 +36087,7 @@
false
false
false
+ 0
4010
@@ -32086,6 +36096,7 @@
false
false
false
+ 0
4011
@@ -32094,6 +36105,7 @@
false
false
false
+ 0
4012
@@ -32102,6 +36114,7 @@
false
false
false
+ 0
4013
@@ -32110,6 +36123,7 @@
false
false
false
+ 0
4014
@@ -32118,6 +36132,7 @@
false
false
false
+ 0
4015
@@ -32126,6 +36141,7 @@
false
false
false
+ 0
4016
@@ -32134,6 +36150,7 @@
false
false
false
+ 0
4017
@@ -32142,6 +36159,7 @@
false
false
false
+ 0
4018
@@ -32150,6 +36168,7 @@
false
false
false
+ 0
4019
@@ -32158,6 +36177,7 @@
false
false
false
+ 0
4020
@@ -32166,6 +36186,7 @@
false
false
false
+ 0
4021
@@ -32174,6 +36195,7 @@
false
false
false
+ 0
4022
@@ -32182,6 +36204,7 @@
false
false
false
+ 0
4023
@@ -32190,6 +36213,7 @@
false
false
false
+ 0
4024
@@ -32198,6 +36222,7 @@
false
false
false
+ 0
4025
@@ -32206,6 +36231,7 @@
false
false
false
+ 0
4026
@@ -32214,6 +36240,7 @@
false
false
false
+ 0
4027
@@ -32222,6 +36249,7 @@
false
false
false
+ 0
4028
@@ -32230,6 +36258,7 @@
false
false
false
+ 0
4029
@@ -32238,6 +36267,7 @@
false
false
false
+ 0
4030
@@ -32246,6 +36276,7 @@
false
false
false
+ 0
4031
@@ -32254,6 +36285,7 @@
false
false
false
+ 0
4032
@@ -32262,6 +36294,7 @@
false
false
false
+ 0
4033
@@ -32270,6 +36303,7 @@
false
false
false
+ 0
4034
@@ -32278,6 +36312,7 @@
false
false
false
+ 0
4035
@@ -32286,6 +36321,7 @@
false
false
false
+ 0
4036
@@ -32294,6 +36330,7 @@
false
false
false
+ 0
4037
@@ -32302,6 +36339,7 @@
false
false
false
+ 0
4038
@@ -32310,6 +36348,7 @@
false
false
false
+ 0
4039
@@ -32318,6 +36357,7 @@
false
false
false
+ 0
4040
@@ -32326,6 +36366,7 @@
false
false
false
+ 0
4041
@@ -32334,6 +36375,7 @@
false
false
false
+ 0
4042
@@ -32342,6 +36384,7 @@
false
false
false
+ 0
4043
@@ -32350,6 +36393,7 @@
false
false
false
+ 0
4044
@@ -32358,6 +36402,7 @@
false
false
false
+ 0
4045
@@ -32366,6 +36411,7 @@
false
false
false
+ 0
4046
@@ -32374,6 +36420,7 @@
false
false
false
+ 0
4047
@@ -32382,6 +36429,7 @@
false
false
false
+ 0
4048
@@ -32390,6 +36438,7 @@
false
false
false
+ 0
4049
@@ -32398,6 +36447,7 @@
false
false
false
+ 0
4050
@@ -32406,6 +36456,7 @@
false
false
false
+ 0
4051
@@ -32414,6 +36465,7 @@
false
false
false
+ 0
4052
@@ -32422,6 +36474,7 @@
false
false
false
+ 0
4053
@@ -32430,6 +36483,7 @@
false
false
false
+ 0
4054
@@ -32438,6 +36492,7 @@
false
false
false
+ 0
4055
@@ -32446,6 +36501,7 @@
false
false
false
+ 0
4056
@@ -32454,6 +36510,7 @@
false
false
false
+ 0
4057
@@ -32462,6 +36519,7 @@
false
false
false
+ 0
4058
@@ -32470,6 +36528,7 @@
false
false
false
+ 0
4059
@@ -32478,6 +36537,7 @@
false
false
false
+ 0
4060
@@ -32486,6 +36546,7 @@
false
false
false
+ 0
4061
@@ -32494,6 +36555,7 @@
false
false
false
+ 0
4062
@@ -32502,6 +36564,7 @@
false
false
false
+ 0
4063
@@ -32510,6 +36573,7 @@
false
false
false
+ 0
4064
@@ -32518,6 +36582,7 @@
false
false
false
+ 0
4065
@@ -32526,6 +36591,7 @@
false
false
false
+ 0
4066
@@ -32534,6 +36600,7 @@
false
false
false
+ 0
4067
@@ -32542,6 +36609,7 @@
false
false
false
+ 0
4068
@@ -32550,6 +36618,7 @@
false
false
false
+ 0
4069
@@ -32558,6 +36627,7 @@
false
false
false
+ 0
4070
@@ -32566,6 +36636,7 @@
false
false
false
+ 0
4071
@@ -32574,6 +36645,7 @@
false
false
false
+ 0
4072
@@ -32582,6 +36654,7 @@
false
false
false
+ 0
4073
@@ -32590,6 +36663,7 @@
false
false
false
+ 0
4074
@@ -32598,6 +36672,7 @@
false
false
false
+ 0
4075
@@ -32606,6 +36681,7 @@
false
false
false
+ 0
4076
@@ -32614,6 +36690,7 @@
false
false
false
+ 0
4077
@@ -32622,6 +36699,7 @@
false
false
false
+ 0
4078
@@ -32630,6 +36708,7 @@
false
false
false
+ 0
4079
@@ -32638,6 +36717,7 @@
false
false
false
+ 0
4080
@@ -32646,6 +36726,7 @@
false
false
false
+ 0
4081
@@ -32654,6 +36735,7 @@
false
false
false
+ 0
4082
@@ -32662,6 +36744,7 @@
false
false
false
+ 0
4083
@@ -32670,6 +36753,7 @@
false
false
false
+ 0
4084
@@ -32678,6 +36762,7 @@
false
false
false
+ 0
4085
@@ -32686,6 +36771,7 @@
false
false
false
+ 0
4086
@@ -32694,6 +36780,7 @@
false
false
false
+ 0
4087
@@ -32702,6 +36789,7 @@
false
false
false
+ 0
4088
@@ -32710,6 +36798,7 @@
false
false
false
+ 0
4089
@@ -32718,6 +36807,7 @@
false
false
false
+ 0
4090
@@ -32726,6 +36816,7 @@
false
false
false
+ 0
4091
@@ -32734,6 +36825,7 @@
false
false
false
+ 0
4092
@@ -32742,6 +36834,7 @@
false
false
false
+ 0
4093
@@ -32750,6 +36843,7 @@
false
false
false
+ 0
4094
@@ -32758,6 +36852,7 @@
false
false
false
+ 0
4095
@@ -32766,6 +36861,7 @@
false
false
false
+ 0
4096
@@ -32774,6 +36870,7 @@
false
false
false
+ 0
4097
@@ -32782,6 +36879,7 @@
false
false
false
+ 0
4098
@@ -32790,6 +36888,7 @@
false
false
false
+ 0
4099
@@ -32798,6 +36897,7 @@
false
false
false
+ 0
4100
@@ -32806,6 +36906,7 @@
false
false
false
+ 0
4101
@@ -32814,6 +36915,7 @@
false
false
false
+ 0
4102
@@ -32822,6 +36924,7 @@
false
false
false
+ 0
4103
@@ -32830,6 +36933,7 @@
false
false
false
+ 0
4104
@@ -32838,6 +36942,7 @@
false
false
false
+ 0
4105
@@ -32846,6 +36951,7 @@
false
false
false
+ 0
4106
@@ -32854,6 +36960,7 @@
false
false
false
+ 0
4107
@@ -32862,6 +36969,7 @@
false
false
false
+ 0
4108
@@ -32870,6 +36978,7 @@
false
false
false
+ 0
4109
@@ -32878,6 +36987,7 @@
false
false
false
+ 0
4110
@@ -32886,6 +36996,7 @@
false
false
false
+ 0
4111
@@ -32894,6 +37005,7 @@
false
false
false
+ 0
4112
@@ -32902,6 +37014,7 @@
false
false
false
+ 0
4113
@@ -32910,6 +37023,7 @@
false
false
false
+ 0
4114
@@ -32918,6 +37032,7 @@
false
false
false
+ 0
4115
@@ -32926,6 +37041,7 @@
false
false
false
+ 0
4116
@@ -32934,6 +37050,7 @@
false
false
false
+ 0
4117
@@ -32942,6 +37059,7 @@
false
false
false
+ 0
4118
@@ -32950,6 +37068,7 @@
false
false
false
+ 0
4119
@@ -32958,6 +37077,7 @@
false
false
false
+ 0
4120
@@ -32966,6 +37086,7 @@
false
false
false
+ 0
4121
@@ -32974,6 +37095,7 @@
false
false
false
+ 0
4122
@@ -32982,6 +37104,7 @@
false
false
false
+ 0
4123
@@ -32990,6 +37113,7 @@
false
false
false
+ 0
4124
@@ -32998,6 +37122,7 @@
false
false
false
+ 0
4125
@@ -33006,6 +37131,7 @@
false
false
false
+ 0
4126
@@ -33014,6 +37140,7 @@
false
false
false
+ 0
4127
@@ -33022,6 +37149,7 @@
false
false
false
+ 0
4128
@@ -33030,6 +37158,7 @@
false
false
false
+ 0
4129
@@ -33038,6 +37167,7 @@
false
false
false
+ 0
4130
@@ -33046,6 +37176,7 @@
false
false
false
+ 0
4131
@@ -33054,6 +37185,7 @@
false
false
false
+ 0
4132
@@ -33062,6 +37194,7 @@
false
false
false
+ 0
4133
@@ -33070,6 +37203,7 @@
false
false
false
+ 0
4134
@@ -33078,6 +37212,7 @@
false
false
false
+ 0
4135
@@ -33086,6 +37221,7 @@
false
false
false
+ 0
4136
@@ -33094,6 +37230,7 @@
false
false
false
+ 0
4137
@@ -33102,6 +37239,7 @@
false
false
false
+ 0
4138
@@ -33110,6 +37248,7 @@
false
false
false
+ 0
4139
@@ -33118,6 +37257,7 @@
false
false
false
+ 0
4140
@@ -33126,6 +37266,7 @@
false
false
false
+ 0
4141
@@ -33134,6 +37275,7 @@
false
false
false
+ 0
4142
@@ -33142,6 +37284,7 @@
false
false
false
+ 0
4143
@@ -33150,6 +37293,7 @@
false
false
false
+ 0
4144
@@ -33158,6 +37302,7 @@
false
false
false
+ 0
4145
@@ -33166,6 +37311,7 @@
false
false
false
+ 0
4146
@@ -33174,6 +37320,7 @@
false
false
false
+ 0
4147
@@ -33182,6 +37329,7 @@
false
false
false
+ 0
4148
@@ -33190,6 +37338,7 @@
false
false
false
+ 0
4149
@@ -33198,6 +37347,7 @@
false
false
false
+ 0
4150
@@ -33206,6 +37356,7 @@
false
false
false
+ 0
4151
@@ -33214,6 +37365,7 @@
false
false
false
+ 0
4152
@@ -33222,6 +37374,7 @@
false
false
false
+ 0
4153
@@ -33230,6 +37383,7 @@
false
false
false
+ 0
4154
@@ -33238,6 +37392,7 @@
false
false
false
+ 0
4155
@@ -33246,6 +37401,7 @@
false
false
false
+ 0
4156
@@ -33254,6 +37410,7 @@
false
false
false
+ 0
4157
@@ -33262,6 +37419,7 @@
false
false
false
+ 0
4158
@@ -33270,6 +37428,7 @@
false
false
false
+ 0
4159
@@ -33278,6 +37437,7 @@
false
false
false
+ 0
4160
@@ -33286,6 +37446,7 @@
false
false
false
+ 0
4161
@@ -33294,6 +37455,7 @@
false
false
false
+ 0
4162
@@ -33302,6 +37464,7 @@
false
false
false
+ 0
4163
@@ -33310,6 +37473,7 @@
false
false
false
+ 0
4164
@@ -33318,6 +37482,7 @@
false
false
false
+ 0
4165
@@ -33326,6 +37491,7 @@
false
false
false
+ 0
4166
@@ -33334,6 +37500,7 @@
false
false
false
+ 0
4167
@@ -33342,6 +37509,7 @@
false
false
false
+ 0
4168
@@ -33350,6 +37518,7 @@
false
false
false
+ 0
4169
@@ -33358,6 +37527,7 @@
false
false
false
+ 0
4170
@@ -33366,6 +37536,7 @@
false
false
false
+ 0
4171
@@ -33374,6 +37545,7 @@
false
false
false
+ 0
4172
@@ -33382,6 +37554,7 @@
false
false
false
+ 0
4173
@@ -33390,6 +37563,7 @@
false
false
false
+ 0
4174
@@ -33398,6 +37572,7 @@
false
false
false
+ 0
4175
@@ -33406,6 +37581,7 @@
false
false
false
+ 0
4176
@@ -33414,6 +37590,7 @@
false
false
false
+ 0
4177
@@ -33422,6 +37599,7 @@
false
false
false
+ 0
4178
@@ -33430,6 +37608,7 @@
false
false
false
+ 0
4179
@@ -33438,6 +37617,7 @@
false
false
false
+ 0
4180
@@ -33446,6 +37626,7 @@
false
false
false
+ 0
4181
@@ -33454,6 +37635,7 @@
false
false
false
+ 0
4182
@@ -33462,6 +37644,7 @@
false
false
false
+ 0
4183
@@ -33470,6 +37653,7 @@
false
false
false
+ 0
4184
@@ -33478,6 +37662,7 @@
false
false
false
+ 0
4185
@@ -33486,6 +37671,7 @@
false
false
false
+ 0
4186
@@ -33494,6 +37680,7 @@
false
false
false
+ 0
4187
@@ -33502,6 +37689,7 @@
false
false
false
+ 0
4188
@@ -33510,6 +37698,7 @@
false
false
false
+ 0
4189
@@ -33518,6 +37707,7 @@
false
false
false
+ 0
4190
@@ -33526,6 +37716,7 @@
false
false
false
+ 0
4191
@@ -33534,6 +37725,7 @@
false
false
false
+ 0
4192
@@ -33542,6 +37734,7 @@
false
false
false
+ 0
4193
@@ -33550,6 +37743,7 @@
false
false
false
+ 0
4194
@@ -33558,6 +37752,7 @@
false
false
false
+ 0
4195
@@ -33566,6 +37761,7 @@
false
false
false
+ 0
4196
@@ -33574,6 +37770,7 @@
false
false
false
+ 0
4197
@@ -33582,6 +37779,7 @@
false
false
false
+ 0
4198
@@ -33590,6 +37788,7 @@
false
false
false
+ 0
4199
@@ -33598,6 +37797,7 @@
false
false
false
+ 0
4200
@@ -33606,6 +37806,7 @@
false
false
false
+ 0
4201
@@ -33614,6 +37815,7 @@
false
false
false
+ 0
4202
@@ -33622,6 +37824,7 @@
false
false
false
+ 0
4203
@@ -33630,6 +37833,7 @@
false
false
false
+ 0
4204
@@ -33638,6 +37842,7 @@
false
false
false
+ 0
4205
@@ -33646,6 +37851,7 @@
false
false
false
+ 0
4206
@@ -33654,6 +37860,7 @@
false
false
false
+ 0
4207
@@ -33662,6 +37869,7 @@
false
false
false
+ 0
4208
@@ -33670,6 +37878,7 @@
false
false
false
+ 0
4209
@@ -33678,6 +37887,7 @@
false
false
false
+ 0
4210
@@ -33686,6 +37896,7 @@
false
false
false
+ 0
4211
@@ -33694,6 +37905,7 @@
false
false
false
+ 0
4212
@@ -33702,6 +37914,7 @@
false
false
false
+ 0
4213
@@ -33710,6 +37923,7 @@
false
false
false
+ 0
4214
@@ -33718,6 +37932,7 @@
false
false
false
+ 0
4215
@@ -33726,6 +37941,7 @@
false
false
false
+ 0
4216
@@ -33734,6 +37950,7 @@
false
false
false
+ 0
4217
@@ -33742,6 +37959,7 @@
false
false
false
+ 0
4218
@@ -33750,6 +37968,7 @@
false
false
false
+ 0
4219
@@ -33758,6 +37977,7 @@
false
false
false
+ 0
4220
@@ -33766,6 +37986,7 @@
false
false
false
+ 0
4221
@@ -33774,6 +37995,7 @@
false
false
false
+ 0
4222
@@ -33782,6 +38004,7 @@
false
false
false
+ 0
4223
@@ -33790,6 +38013,7 @@
false
false
false
+ 0
4224
@@ -33798,6 +38022,7 @@
false
false
false
+ 0
4225
@@ -33806,6 +38031,7 @@
false
false
false
+ 0
4226
@@ -33814,6 +38040,7 @@
false
false
false
+ 0
4227
@@ -33822,6 +38049,7 @@
false
false
false
+ 0
4228
@@ -33830,6 +38058,7 @@
false
false
false
+ 0
4229
@@ -33838,6 +38067,7 @@
false
false
false
+ 0
4230
@@ -33846,6 +38076,7 @@
false
false
false
+ 0
4231
@@ -33854,6 +38085,7 @@
false
false
false
+ 0
4232
@@ -33862,6 +38094,7 @@
false
false
false
+ 0
4233
@@ -33870,6 +38103,7 @@
false
false
false
+ 0
4234
@@ -33878,6 +38112,7 @@
false
false
false
+ 0
4235
@@ -33886,6 +38121,7 @@
false
false
false
+ 0
4236
@@ -33894,6 +38130,7 @@
false
false
false
+ 0
4237
@@ -33902,6 +38139,7 @@
false
false
false
+ 0
4238
@@ -33910,6 +38148,7 @@
false
false
false
+ 0
4239
@@ -33918,6 +38157,7 @@
false
false
false
+ 0
4240
@@ -33926,6 +38166,7 @@
false
false
false
+ 0
4241
@@ -33934,6 +38175,7 @@
false
false
false
+ 0
4242
@@ -33942,6 +38184,7 @@
false
false
false
+ 0
4243
@@ -33950,6 +38193,7 @@
false
false
false
+ 0
4244
@@ -33958,6 +38202,7 @@
false
false
false
+ 0
4245
@@ -33966,6 +38211,7 @@
false
false
false
+ 0
4246
@@ -33974,6 +38220,7 @@
false
false
false
+ 0
4247
@@ -33982,6 +38229,7 @@
false
false
false
+ 0
4248
@@ -33990,6 +38238,7 @@
false
false
false
+ 0
4249
@@ -33998,6 +38247,7 @@
false
false
false
+ 0
4250
@@ -34006,6 +38256,7 @@
false
false
false
+ 0
4251
@@ -34014,6 +38265,7 @@
false
false
false
+ 0
4252
@@ -34022,6 +38274,7 @@
false
false
false
+ 0
4253
@@ -34030,6 +38283,7 @@
false
false
false
+ 0
4254
@@ -34038,6 +38292,7 @@
false
false
false
+ 0
4255
@@ -34046,6 +38301,7 @@
false
false
false
+ 0
4256
@@ -34054,6 +38310,7 @@
false
false
false
+ 0
4257
@@ -34062,6 +38319,7 @@
false
false
false
+ 0
4258
@@ -34070,6 +38328,7 @@
false
false
false
+ 0
4259
@@ -34078,6 +38337,7 @@
false
false
false
+ 0
4260
@@ -34086,6 +38346,7 @@
false
false
false
+ 0
4261
@@ -34094,6 +38355,7 @@
false
false
false
+ 0
4262
@@ -34102,6 +38364,7 @@
false
false
false
+ 0
4263
@@ -34110,6 +38373,7 @@
false
false
false
+ 0
4264
@@ -34118,6 +38382,7 @@
false
false
false
+ 0
4265
@@ -34126,6 +38391,7 @@
false
false
false
+ 0
4266
@@ -34134,6 +38400,7 @@
false
false
false
+ 0
4267
@@ -34142,6 +38409,7 @@
false
false
false
+ 0
4268
@@ -34150,6 +38418,7 @@
false
false
false
+ 0
4269
@@ -34158,6 +38427,7 @@
false
false
false
+ 0
4270
@@ -34166,6 +38436,7 @@
false
false
false
+ 0
4271
@@ -34174,6 +38445,7 @@
false
false
false
+ 0
4272
@@ -34182,6 +38454,7 @@
false
false
false
+ 0
4273
@@ -34190,6 +38463,7 @@
false
false
false
+ 0
4274
@@ -34198,6 +38472,7 @@
false
false
false
+ 0
4275
@@ -34206,6 +38481,7 @@
false
false
false
+ 0
4276
@@ -34214,6 +38490,7 @@
false
false
false
+ 0
4277
@@ -34222,6 +38499,7 @@
false
false
false
+ 0
4278
@@ -34230,6 +38508,7 @@
false
false
false
+ 0
4279
@@ -34238,6 +38517,7 @@
false
false
false
+ 0
4280
@@ -34246,6 +38526,7 @@
false
false
false
+ 0
4281
@@ -34254,6 +38535,7 @@
false
false
false
+ 0
4282
@@ -34262,6 +38544,7 @@
false
false
false
+ 0
4283
@@ -34270,6 +38553,7 @@
false
false
false
+ 0
4284
@@ -34278,6 +38562,7 @@
false
false
false
+ 0
4285
@@ -34286,6 +38571,7 @@
false
false
false
+ 0
4286
@@ -34294,6 +38580,7 @@
false
false
false
+ 0
4287
@@ -34302,6 +38589,7 @@
false
false
false
+ 0
4288
@@ -34310,6 +38598,7 @@
false
false
false
+ 0
4289
@@ -34318,6 +38607,7 @@
false
false
false
+ 0
4290
@@ -34326,6 +38616,7 @@
false
false
false
+ 0
4291
@@ -34334,6 +38625,7 @@
false
false
false
+ 0
4292
@@ -34342,6 +38634,7 @@
false
false
false
+ 0
4293
@@ -34350,6 +38643,7 @@
false
false
false
+ 0
4294
@@ -34358,6 +38652,7 @@
false
false
false
+ 0
4295
@@ -34366,6 +38661,7 @@
false
false
false
+ 0
4296
@@ -34374,6 +38670,7 @@
false
false
false
+ 0
4297
@@ -34382,6 +38679,7 @@
false
false
false
+ 0
4298
@@ -34390,6 +38688,7 @@
false
false
false
+ 0
4299
@@ -34398,6 +38697,7 @@
false
false
false
+ 0
4300
@@ -34406,6 +38706,7 @@
false
false
false
+ 0
4301
@@ -34414,6 +38715,7 @@
false
false
false
+ 0
4302
@@ -34422,6 +38724,7 @@
false
false
false
+ 0
4303
@@ -34430,6 +38733,7 @@
false
false
false
+ 0
4304
@@ -34438,6 +38742,7 @@
false
false
false
+ 0
4305
@@ -34446,6 +38751,7 @@
false
false
false
+ 0
4306
@@ -34454,6 +38760,7 @@
false
false
false
+ 0
4307
@@ -34462,6 +38769,7 @@
false
false
false
+ 0
4308
@@ -34470,6 +38778,7 @@
false
false
false
+ 0
4309
@@ -34478,6 +38787,7 @@
false
false
false
+ 0
4310
@@ -34486,6 +38796,7 @@
false
false
false
+ 0
4311
@@ -34494,6 +38805,7 @@
false
false
false
+ 0
4312
@@ -34502,6 +38814,7 @@
false
false
false
+ 0
4313
@@ -34510,6 +38823,7 @@
false
false
false
+ 0
4314
@@ -34518,6 +38832,7 @@
false
false
false
+ 0
4315
@@ -34526,6 +38841,7 @@
false
false
false
+ 0
4316
@@ -34534,6 +38850,7 @@
false
false
false
+ 0
4317
@@ -34542,6 +38859,7 @@
false
false
false
+ 0
4318
@@ -34550,6 +38868,7 @@
false
false
false
+ 0
4319
@@ -34558,6 +38877,7 @@
false
false
false
+ 0
4320
@@ -34566,6 +38886,7 @@
false
false
false
+ 0
4321
@@ -34574,6 +38895,7 @@
false
false
false
+ 0
4322
@@ -34582,6 +38904,7 @@
false
false
false
+ 0
4323
@@ -34590,6 +38913,7 @@
false
false
false
+ 0
4324
@@ -34598,6 +38922,7 @@
false
false
false
+ 0
4325
@@ -34606,6 +38931,7 @@
false
false
false
+ 0
4326
@@ -34614,6 +38940,7 @@
false
false
false
+ 0
4327
@@ -34622,6 +38949,7 @@
false
false
false
+ 0
4328
@@ -34630,6 +38958,7 @@
false
false
false
+ 0
4329
@@ -34638,6 +38967,7 @@
false
false
false
+ 0
4330
@@ -34646,6 +38976,7 @@
false
false
false
+ 0
4331
@@ -34654,6 +38985,7 @@
false
false
false
+ 0
4332
@@ -34662,6 +38994,7 @@
false
false
false
+ 0
4333
@@ -34670,6 +39003,7 @@
false
false
false
+ 0
4334
@@ -34678,6 +39012,7 @@
false
false
false
+ 0
4335
@@ -34686,6 +39021,7 @@
false
false
false
+ 0
4336
@@ -34694,6 +39030,7 @@
false
false
false
+ 0
4337
@@ -34702,6 +39039,7 @@
false
false
false
+ 0
4338
@@ -34710,6 +39048,7 @@
false
false
false
+ 0
4339
@@ -34718,6 +39057,7 @@
false
false
false
+ 0
4340
@@ -34726,6 +39066,7 @@
false
false
false
+ 0
4341
@@ -34734,6 +39075,7 @@
false
false
false
+ 0
4342
@@ -34742,6 +39084,7 @@
false
false
false
+ 0
4343
@@ -34750,6 +39093,7 @@
false
false
false
+ 0
4344
@@ -34758,6 +39102,7 @@
false
false
false
+ 0
4345
@@ -34766,6 +39111,7 @@
false
false
false
+ 0
4346
@@ -34774,6 +39120,7 @@
false
false
false
+ 0
4347
@@ -34782,6 +39129,7 @@
false
false
false
+ 0
4348
@@ -34790,6 +39138,7 @@
false
false
false
+ 0
4349
@@ -34798,6 +39147,7 @@
false
false
false
+ 0
4350
@@ -34806,6 +39156,7 @@
false
false
false
+ 0
4351
@@ -34814,6 +39165,7 @@
false
false
false
+ 0
4352
@@ -34822,6 +39174,7 @@
false
false
false
+ 0
4353
@@ -34830,6 +39183,7 @@
false
false
false
+ 0
4354
@@ -34838,6 +39192,7 @@
false
false
false
+ 0
4355
@@ -34846,6 +39201,7 @@
false
false
false
+ 0
4356
@@ -34854,6 +39210,7 @@
false
false
false
+ 0
4357
@@ -34862,6 +39219,7 @@
false
false
false
+ 0
4358
@@ -34870,6 +39228,7 @@
false
false
false
+ 0
4359
@@ -34878,6 +39237,7 @@
false
false
false
+ 0
4360
@@ -34886,6 +39246,7 @@
false
false
false
+ 0
4361
@@ -34894,6 +39255,7 @@
false
false
false
+ 0
4362
@@ -34902,6 +39264,7 @@
false
false
false
+ 0
4363
@@ -34910,6 +39273,7 @@
false
false
false
+ 0
4364
@@ -34918,6 +39282,7 @@
false
false
false
+ 0
4365
@@ -34926,6 +39291,7 @@
false
false
false
+ 0
4366
@@ -34934,6 +39300,7 @@
false
false
false
+ 0
4367
@@ -34942,6 +39309,7 @@
false
false
false
+ 0
4368
@@ -34950,6 +39318,7 @@
false
false
false
+ 0
4369
@@ -34958,6 +39327,7 @@
false
false
false
+ 0
4370
@@ -34966,6 +39336,7 @@
false
false
false
+ 0
4371
@@ -34974,6 +39345,7 @@
false
false
false
+ 0
4372
@@ -34982,6 +39354,7 @@
false
false
false
+ 0
4373
@@ -34990,6 +39363,7 @@
false
false
false
+ 0
4374
@@ -34998,6 +39372,7 @@
false
false
false
+ 0
4375
@@ -35006,6 +39381,7 @@
false
false
false
+ 0
4376
@@ -35014,6 +39390,7 @@
false
false
false
+ 0
4377
@@ -35022,6 +39399,7 @@
false
false
false
+ 0
4378
@@ -35030,6 +39408,7 @@
false
false
false
+ 0
4379
@@ -35038,6 +39417,7 @@
false
false
false
+ 0
4380
@@ -35046,6 +39426,7 @@
false
false
false
+ 0
4381
@@ -35054,6 +39435,7 @@
false
false
false
+ 0
4382
@@ -35062,6 +39444,7 @@
false
false
false
+ 0
4383
@@ -35070,6 +39453,7 @@
false
false
false
+ 0
4384
@@ -35078,6 +39462,7 @@
false
false
false
+ 0
4385
@@ -35086,6 +39471,7 @@
false
false
false
+ 0
4386
@@ -35094,6 +39480,7 @@
false
false
false
+ 0
4387
@@ -35102,6 +39489,7 @@
false
false
false
+ 0
4388
@@ -35110,6 +39498,7 @@
false
false
false
+ 0
4389
@@ -35118,6 +39507,7 @@
false
false
false
+ 0
4390
@@ -35126,6 +39516,7 @@
false
false
false
+ 0
4391
@@ -35134,6 +39525,7 @@
false
false
false
+ 0
4392
@@ -35142,6 +39534,7 @@
false
false
false
+ 0
4393
@@ -35150,6 +39543,7 @@
false
false
false
+ 0
4394
@@ -35158,6 +39552,7 @@
false
false
false
+ 0
4395
@@ -35166,6 +39561,7 @@
false
false
false
+ 0
4396
@@ -35174,6 +39570,7 @@
false
false
false
+ 0
4397
@@ -35182,6 +39579,7 @@
false
false
false
+ 0
4398
@@ -35190,6 +39588,7 @@
false
false
false
+ 0
4399
@@ -35198,6 +39597,7 @@
false
false
false
+ 0
4400
@@ -35206,6 +39606,7 @@
false
false
false
+ 0
4401
@@ -35214,6 +39615,7 @@
false
false
false
+ 0
4402
@@ -35222,6 +39624,7 @@
false
false
false
+ 0
4403
@@ -35230,6 +39633,7 @@
false
false
false
+ 0
4404
@@ -35238,6 +39642,7 @@
false
false
false
+ 0
4405
@@ -35246,6 +39651,7 @@
false
false
false
+ 0
4406
@@ -35254,6 +39660,7 @@
false
false
false
+ 0
4407
@@ -35262,6 +39669,7 @@
false
false
false
+ 0
4408
@@ -35270,6 +39678,7 @@
false
false
false
+ 0
4409
@@ -35278,6 +39687,7 @@
false
false
false
+ 0
4410
@@ -35286,6 +39696,7 @@
false
false
false
+ 0
4411
@@ -35294,6 +39705,7 @@
false
false
false
+ 0
4412
@@ -35302,6 +39714,7 @@
false
false
false
+ 0
4413
@@ -35310,6 +39723,7 @@
false
false
false
+ 0
4414
@@ -35318,6 +39732,7 @@
false
false
false
+ 0
4415
@@ -35326,6 +39741,7 @@
false
false
false
+ 0
4416
@@ -35334,6 +39750,7 @@
false
false
false
+ 0
4417
@@ -35342,6 +39759,7 @@
false
false
false
+ 0
4418
@@ -35350,6 +39768,7 @@
false
false
false
+ 0
4419
@@ -35358,6 +39777,7 @@
false
false
false
+ 0
4420
@@ -35366,6 +39786,7 @@
false
false
false
+ 0
4421
@@ -35374,6 +39795,7 @@
false
false
false
+ 0
4422
@@ -35382,6 +39804,7 @@
false
false
false
+ 0
4423
@@ -35390,6 +39813,7 @@
false
false
false
+ 0
4424
@@ -35398,6 +39822,7 @@
false
false
false
+ 0
4425
@@ -35406,6 +39831,7 @@
false
false
false
+ 0
4426
@@ -35414,6 +39840,7 @@
false
false
false
+ 0
4427
@@ -35422,6 +39849,7 @@
false
false
false
+ 0
4428
@@ -35430,6 +39858,7 @@
false
false
false
+ 0
4429
@@ -35438,6 +39867,7 @@
false
false
false
+ 0
4430
@@ -35446,6 +39876,7 @@
false
false
false
+ 0
4431
@@ -35454,6 +39885,7 @@
false
false
false
+ 0
4432
@@ -35462,6 +39894,7 @@
false
false
false
+ 0
4433
@@ -35470,6 +39903,7 @@
false
false
false
+ 0
4434
@@ -35478,6 +39912,7 @@
false
false
false
+ 0
4435
@@ -35486,6 +39921,7 @@
false
false
false
+ 0
4436
@@ -35494,6 +39930,7 @@
false
false
false
+ 0
4437
@@ -35502,6 +39939,7 @@
false
false
false
+ 0
4438
@@ -35510,6 +39948,7 @@
false
false
false
+ 0
4439
@@ -35518,6 +39957,7 @@
false
false
false
+ 0
4440
@@ -35526,6 +39966,7 @@
false
false
false
+ 0
4441
@@ -35534,6 +39975,7 @@
false
false
false
+ 0
4442
@@ -35542,6 +39984,7 @@
false
false
false
+ 0
4443
@@ -35550,6 +39993,7 @@
false
false
false
+ 0
4444
@@ -35558,6 +40002,7 @@
false
false
false
+ 0
4445
@@ -35566,6 +40011,7 @@
false
false
false
+ 0
4446
@@ -35574,6 +40020,7 @@
false
false
false
+ 0
4447
@@ -35582,6 +40029,7 @@
false
false
false
+ 0
4448
@@ -35590,6 +40038,7 @@
false
false
false
+ 0
4449
@@ -35598,6 +40047,7 @@
false
false
false
+ 0
4450
@@ -35606,6 +40056,7 @@
false
false
false
+ 0
4451
@@ -35614,6 +40065,7 @@
false
false
false
+ 0
4452
@@ -35622,6 +40074,7 @@
false
false
false
+ 0
4453
@@ -35630,6 +40083,7 @@
false
false
false
+ 0
4454
@@ -35638,6 +40092,7 @@
false
false
false
+ 0
4455
@@ -35646,6 +40101,7 @@
false
false
false
+ 0
4456
@@ -35654,6 +40110,7 @@
false
false
false
+ 0
4457
@@ -35662,6 +40119,7 @@
false
false
false
+ 0
4458
@@ -35670,6 +40128,7 @@
false
false
false
+ 0
4459
@@ -35678,6 +40137,7 @@
false
false
false
+ 0
4460
@@ -35686,6 +40146,7 @@
false
false
false
+ 0
4461
@@ -35694,6 +40155,7 @@
false
false
false
+ 0
4462
@@ -35702,6 +40164,7 @@
false
false
false
+ 0
4463
@@ -35710,6 +40173,7 @@
false
false
false
+ 0
4464
@@ -35718,6 +40182,7 @@
false
false
false
+ 0
4465
@@ -35726,6 +40191,7 @@
false
false
false
+ 0
4466
@@ -35734,6 +40200,7 @@
false
false
false
+ 0
4467
@@ -35742,6 +40209,7 @@
false
false
false
+ 0
4468
@@ -35750,6 +40218,7 @@
false
false
false
+ 0
4469
@@ -35758,6 +40227,7 @@
false
false
false
+ 0
4470
@@ -35766,6 +40236,7 @@
false
false
false
+ 0
4471
@@ -35774,6 +40245,7 @@
false
false
false
+ 0
4472
@@ -35782,6 +40254,7 @@
false
false
false
+ 0
4473
@@ -35790,6 +40263,7 @@
false
false
false
+ 0
4474
@@ -35798,6 +40272,7 @@
false
false
false
+ 0
4475
@@ -35806,6 +40281,7 @@
false
false
false
+ 0
4476
@@ -35814,6 +40290,7 @@
false
false
false
+ 0
4477
@@ -35822,6 +40299,7 @@
false
false
false
+ 0
4478
@@ -35830,6 +40308,7 @@
false
false
false
+ 0
4479
@@ -35838,6 +40317,7 @@
false
false
false
+ 0
4480
@@ -35846,6 +40326,7 @@
false
false
false
+ 0
4481
@@ -35854,6 +40335,7 @@
false
false
false
+ 0
4482
@@ -35862,6 +40344,7 @@
false
false
false
+ 0
4483
@@ -35870,6 +40353,7 @@
false
false
false
+ 0
4484
@@ -35878,6 +40362,7 @@
false
false
false
+ 0
4485
@@ -35886,6 +40371,7 @@
false
false
false
+ 0
4486
@@ -35894,6 +40380,7 @@
false
false
false
+ 0
4487
@@ -35902,6 +40389,7 @@
false
false
false
+ 0
4488
@@ -35910,6 +40398,7 @@
false
false
false
+ 0
4489
@@ -35918,6 +40407,7 @@
false
false
false
+ 0
4490
@@ -35926,6 +40416,7 @@
false
false
false
+ 0
4491
@@ -35934,6 +40425,7 @@
false
false
false
+ 0
4492
@@ -35942,6 +40434,7 @@
false
false
false
+ 0
4493
@@ -35950,6 +40443,7 @@
false
false
false
+ 0
4494
@@ -35958,6 +40452,7 @@
false
false
false
+ 0
4495
@@ -35966,6 +40461,7 @@
false
false
false
+ 0
4496
@@ -35974,6 +40470,7 @@
false
false
false
+ 0
4497
@@ -35982,6 +40479,7 @@
false
false
false
+ 0
4498
@@ -35990,6 +40488,7 @@
false
false
false
+ 0
4499
@@ -35998,6 +40497,7 @@
false
false
false
+ 0
4500
@@ -36006,6 +40506,7 @@
false
false
false
+ 0
4501
@@ -36014,6 +40515,7 @@
false
false
false
+ 0
4502
@@ -36022,6 +40524,7 @@
false
false
false
+ 0
4503
@@ -36030,6 +40533,7 @@
false
false
false
+ 0
4504
@@ -36038,6 +40542,7 @@
false
false
false
+ 0
4505
@@ -36046,6 +40551,7 @@
false
false
false
+ 0
4506
@@ -36054,6 +40560,7 @@
false
false
false
+ 0
4507
@@ -36062,6 +40569,7 @@
false
false
false
+ 0
4508
@@ -36070,6 +40578,7 @@
false
false
false
+ 0
4509
@@ -36078,6 +40587,7 @@
false
false
false
+ 0
4510
@@ -36086,6 +40596,7 @@
false
false
false
+ 0
4511
@@ -36094,6 +40605,7 @@
false
false
false
+ 0
4512
@@ -36102,6 +40614,7 @@
false
false
false
+ 0
4513
@@ -36110,6 +40623,7 @@
false
false
false
+ 0
4514
@@ -36118,6 +40632,7 @@
false
false
false
+ 0
4515
@@ -36126,6 +40641,7 @@
false
false
false
+ 0
4516
@@ -36134,6 +40650,7 @@
false
false
false
+ 0
4517
@@ -36142,6 +40659,7 @@
false
false
false
+ 0
4518
@@ -36150,6 +40668,7 @@
false
false
false
+ 0
4519
@@ -36158,6 +40677,7 @@
false
false
false
+ 0
4520
@@ -36166,6 +40686,7 @@
false
false
false
+ 0
4521
@@ -36174,6 +40695,7 @@
false
false
false
+ 0
4522
@@ -36182,6 +40704,7 @@
false
false
false
+ 0
4523
@@ -36190,6 +40713,7 @@
false
false
false
+ 0
4524
@@ -36198,6 +40722,7 @@
false
false
false
+ 0
4525
@@ -36206,6 +40731,7 @@
false
false
false
+ 0
4526
@@ -36214,6 +40740,7 @@
false
false
false
+ 0
4527
@@ -36222,6 +40749,7 @@
false
false
false
+ 0
4528
@@ -36230,6 +40758,7 @@
false
false
false
+ 0
4529
@@ -36238,6 +40767,7 @@
false
false
false
+ 0
4530
@@ -36246,6 +40776,7 @@
false
false
false
+ 0
4531
@@ -36254,6 +40785,7 @@
false
false
false
+ 0
4532
@@ -36262,6 +40794,7 @@
false
false
false
+ 0
4533
@@ -36270,6 +40803,7 @@
false
false
false
+ 0
4534
@@ -36278,6 +40812,7 @@
false
false
false
+ 0
4535
@@ -36286,6 +40821,7 @@
false
false
false
+ 0
4536
@@ -36294,6 +40830,7 @@
false
false
false
+ 0
4537
@@ -36302,6 +40839,7 @@
false
false
false
+ 0
4538
@@ -36310,6 +40848,7 @@
false
false
false
+ 0
4539
@@ -36318,6 +40857,7 @@
false
false
false
+ 0
4540
@@ -36326,6 +40866,7 @@
false
false
false
+ 0
4541
@@ -36334,6 +40875,7 @@
false
false
false
+ 0
4542
@@ -36342,6 +40884,7 @@
false
false
false
+ 0
4543
@@ -36350,6 +40893,7 @@
false
false
false
+ 0
4544
@@ -36358,6 +40902,7 @@
false
false
false
+ 0
4545
@@ -36366,6 +40911,7 @@
false
false
false
+ 0
4546
@@ -36374,6 +40920,7 @@
false
false
false
+ 0
4547
@@ -36382,6 +40929,7 @@
false
false
false
+ 0
4548
@@ -36390,6 +40938,7 @@
false
false
false
+ 0
4549
@@ -36398,6 +40947,7 @@
false
false
false
+ 0
4550
@@ -36406,6 +40956,7 @@
false
false
false
+ 0
4551
@@ -36414,6 +40965,7 @@
false
false
false
+ 0
4552
@@ -36422,6 +40974,7 @@
false
false
false
+ 0
4553
@@ -36430,6 +40983,7 @@
false
false
false
+ 0
4554
@@ -36438,6 +40992,7 @@
false
false
false
+ 0
4555
@@ -36446,6 +41001,7 @@
false
false
false
+ 0
4556
@@ -36454,6 +41010,7 @@
false
false
false
+ 0
4557
@@ -36462,6 +41019,7 @@
false
false
false
+ 0
4558
@@ -36470,6 +41028,7 @@
false
false
false
+ 0
4559
@@ -36478,6 +41037,7 @@
false
false
false
+ 0
4560
@@ -36486,6 +41046,7 @@
false
false
false
+ 0
4561
@@ -36494,6 +41055,7 @@
false
false
false
+ 0
4562
@@ -36502,6 +41064,7 @@
false
false
false
+ 0
4563
@@ -36510,6 +41073,7 @@
false
false
false
+ 0
4564
@@ -36518,6 +41082,7 @@
false
false
false
+ 0
4565
@@ -36526,6 +41091,7 @@
false
false
false
+ 0
4566
@@ -36534,6 +41100,7 @@
false
false
false
+ 0
4567
@@ -36542,6 +41109,7 @@
false
false
false
+ 0
4568
@@ -36550,6 +41118,7 @@
false
false
false
+ 0
4569
@@ -36558,6 +41127,7 @@
false
false
false
+ 0
4570
@@ -36566,6 +41136,7 @@
false
false
false
+ 0
4571
@@ -36574,6 +41145,7 @@
false
false
false
+ 0
4572
@@ -36582,6 +41154,7 @@
false
false
false
+ 0
4573
@@ -36590,6 +41163,7 @@
false
false
false
+ 0
4574
@@ -36598,6 +41172,7 @@
false
false
false
+ 0
4575
@@ -36606,6 +41181,7 @@
false
false
false
+ 0
4576
@@ -36614,6 +41190,7 @@
false
false
false
+ 0
4577
@@ -36622,6 +41199,7 @@
false
false
false
+ 0
4578
@@ -36630,6 +41208,7 @@
false
false
false
+ 0
4579
@@ -36638,6 +41217,7 @@
false
false
false
+ 0
4580
@@ -36646,6 +41226,7 @@
false
false
false
+ 0
4581
@@ -36654,6 +41235,7 @@
false
false
false
+ 0
4582
@@ -36662,6 +41244,7 @@
false
false
false
+ 0
4583
@@ -36670,6 +41253,7 @@
false
false
false
+ 0
4584
@@ -36678,6 +41262,7 @@
false
false
false
+ 0
4585
@@ -36686,6 +41271,7 @@
false
false
false
+ 0
4586
@@ -36694,6 +41280,7 @@
false
false
false
+ 0
4587
@@ -36702,6 +41289,7 @@
false
false
false
+ 0
4588
@@ -36710,6 +41298,7 @@
false
false
false
+ 0
4589
@@ -36718,6 +41307,7 @@
false
false
false
+ 0
4590
@@ -36726,6 +41316,7 @@
false
false
false
+ 0
4591
@@ -36734,6 +41325,7 @@
false
false
false
+ 0
4592
@@ -36742,6 +41334,7 @@
false
false
false
+ 0
4593
@@ -36750,6 +41343,7 @@
false
false
false
+ 0
4594
@@ -36758,6 +41352,7 @@
false
false
false
+ 0
4595
@@ -36766,6 +41361,7 @@
false
false
false
+ 0
4596
@@ -36774,6 +41370,7 @@
false
false
false
+ 0
4597
@@ -36782,6 +41379,7 @@
false
false
false
+ 0
4598
@@ -36790,6 +41388,7 @@
false
false
false
+ 0
4599
@@ -36798,6 +41397,7 @@
false
false
false
+ 0
4600
@@ -36806,6 +41406,7 @@
false
false
false
+ 0
4601
@@ -36814,6 +41415,7 @@
false
false
false
+ 0
4602
@@ -36822,6 +41424,7 @@
false
false
false
+ 0
4603
@@ -36830,6 +41433,7 @@
false
false
false
+ 0
4604
@@ -36838,6 +41442,7 @@
false
false
false
+ 0
4605
@@ -36846,6 +41451,7 @@
false
false
false
+ 0
4606
@@ -36854,6 +41460,7 @@
false
false
false
+ 0
4607
@@ -36862,6 +41469,7 @@
false
false
false
+ 0
4608
@@ -36870,6 +41478,7 @@
false
false
false
+ 0
4609
@@ -36878,6 +41487,7 @@
false
false
false
+ 0
4610
@@ -36886,6 +41496,7 @@
false
false
false
+ 0
4611
@@ -36894,6 +41505,7 @@
false
false
false
+ 0
4612
@@ -36902,6 +41514,7 @@
false
false
false
+ 0
4613
@@ -36910,6 +41523,7 @@
false
false
false
+ 0
4614
@@ -36918,6 +41532,7 @@
false
false
false
+ 0
4615
@@ -36926,6 +41541,7 @@
false
false
false
+ 0
4616
@@ -36934,6 +41550,7 @@
false
false
false
+ 0
4617
@@ -36942,6 +41559,7 @@
false
false
false
+ 0
4618
@@ -36950,6 +41568,7 @@
false
false
false
+ 0
4619
@@ -36958,6 +41577,7 @@
false
false
false
+ 0
4620
@@ -36966,6 +41586,7 @@
false
false
false
+ 0
4621
@@ -36974,6 +41595,7 @@
false
false
false
+ 0
4622
@@ -36982,6 +41604,7 @@
false
false
false
+ 0
4623
@@ -36990,6 +41613,7 @@
false
false
false
+ 0
4624
@@ -36998,6 +41622,7 @@
false
false
false
+ 0
4625
@@ -37006,6 +41631,7 @@
false
false
false
+ 0
4626
@@ -37014,6 +41640,7 @@
false
false
false
+ 0
4627
@@ -37022,6 +41649,7 @@
false
false
false
+ 0
4628
@@ -37030,6 +41658,7 @@
false
false
false
+ 0
4629
@@ -37038,6 +41667,7 @@
false
false
false
+ 0
4630
@@ -37046,6 +41676,7 @@
false
false
false
+ 0
4631
@@ -37054,6 +41685,7 @@
false
false
false
+ 0
4632
@@ -37062,6 +41694,7 @@
false
false
false
+ 0
4633
@@ -37070,6 +41703,7 @@
false
false
false
+ 0
4634
@@ -37078,6 +41712,7 @@
false
false
false
+ 0
4635
@@ -37086,6 +41721,7 @@
false
false
false
+ 0
4636
@@ -37094,6 +41730,7 @@
false
false
false
+ 0
4637
@@ -37102,6 +41739,7 @@
false
false
false
+ 0
4638
@@ -37110,6 +41748,7 @@
false
false
false
+ 0
4639
@@ -37118,6 +41757,7 @@
false
false
false
+ 0
4640
@@ -37126,6 +41766,7 @@
false
false
false
+ 0
4641
@@ -37134,6 +41775,7 @@
false
false
false
+ 0
4642
@@ -37142,6 +41784,7 @@
false
false
false
+ 0
4643
@@ -37150,6 +41793,7 @@
false
false
false
+ 0
4644
@@ -37158,6 +41802,7 @@
false
false
false
+ 0
4645
@@ -37166,6 +41811,7 @@
false
false
false
+ 0
4646
@@ -37174,6 +41820,7 @@
false
false
false
+ 0
4647
@@ -37182,6 +41829,7 @@
false
false
false
+ 0
4648
@@ -37190,6 +41838,7 @@
false
false
false
+ 0
4649
@@ -37198,6 +41847,7 @@
false
false
false
+ 0
4650
@@ -37206,6 +41856,7 @@
false
false
false
+ 0
4651
@@ -37214,6 +41865,7 @@
false
false
false
+ 0
4652
@@ -37222,6 +41874,7 @@
false
false
false
+ 0
4653
@@ -37230,6 +41883,7 @@
false
false
false
+ 0
4654
@@ -37238,6 +41892,7 @@
false
false
false
+ 0
4655
@@ -37246,6 +41901,7 @@
false
false
false
+ 0
4656
@@ -37254,6 +41910,7 @@
false
false
false
+ 0
4657
@@ -37262,6 +41919,7 @@
false
false
false
+ 0
4658
@@ -37270,6 +41928,7 @@
false
false
false
+ 0
4659
@@ -37278,6 +41937,7 @@
false
false
false
+ 0
4660
@@ -37286,6 +41946,7 @@
false
false
false
+ 0
4661
@@ -37294,6 +41955,7 @@
false
false
false
+ 0
4662
@@ -37302,6 +41964,7 @@
false
false
false
+ 0
4663
@@ -37310,6 +41973,7 @@
false
false
false
+ 0
4664
@@ -37318,6 +41982,7 @@
false
false
false
+ 0
4665
@@ -37326,6 +41991,7 @@
false
false
false
+ 0
4666
@@ -37334,6 +42000,7 @@
false
false
false
+ 0
4667
@@ -37342,6 +42009,7 @@
false
false
false
+ 0
4668
@@ -37350,6 +42018,7 @@
false
false
false
+ 0
4669
@@ -37358,6 +42027,7 @@
false
false
false
+ 0
4670
@@ -37366,6 +42036,7 @@
false
false
false
+ 0
4671
@@ -37374,6 +42045,7 @@
false
false
false
+ 0
4672
@@ -37382,6 +42054,7 @@
false
false
false
+ 0
4673
@@ -37390,6 +42063,7 @@
false
false
false
+ 0
4674
@@ -37398,6 +42072,7 @@
false
false
false
+ 0
4675
@@ -37406,6 +42081,7 @@
false
false
false
+ 0
4676
@@ -37414,6 +42090,7 @@
false
false
false
+ 0
4677
@@ -37422,6 +42099,7 @@
false
false
false
+ 0
4678
@@ -37430,6 +42108,7 @@
false
false
false
+ 0
4679
@@ -37438,6 +42117,7 @@
false
false
false
+ 0
4680
@@ -37446,6 +42126,7 @@
false
false
false
+ 0
4681
@@ -37454,6 +42135,7 @@
false
false
false
+ 0
4682
@@ -37462,6 +42144,7 @@
false
false
false
+ 0
4683
@@ -37470,6 +42153,7 @@
false
false
false
+ 0
4684
@@ -37478,6 +42162,7 @@
false
false
false
+ 0
4685
@@ -37486,6 +42171,7 @@
false
false
false
+ 0
4686
@@ -37494,6 +42180,7 @@
false
false
false
+ 0
4687
@@ -37502,6 +42189,7 @@
false
false
false
+ 0
4688
@@ -37510,6 +42198,7 @@
false
false
false
+ 0
4689
@@ -37518,6 +42207,7 @@
false
false
false
+ 0
4690
@@ -37526,6 +42216,7 @@
false
false
false
+ 0
4691
@@ -37534,6 +42225,7 @@
false
false
false
+ 0
4692
@@ -37542,6 +42234,7 @@
false
false
false
+ 0
4693
@@ -37550,6 +42243,7 @@
false
false
false
+ 0
4694
@@ -37558,6 +42252,7 @@
false
false
false
+ 0
4695
@@ -37566,6 +42261,7 @@
false
false
false
+ 0
4696
@@ -37574,6 +42270,7 @@
false
false
false
+ 0
4697
@@ -37582,6 +42279,7 @@
false
false
false
+ 0
4698
@@ -37590,6 +42288,7 @@
false
false
false
+ 0
4699
@@ -37598,6 +42297,7 @@
false
false
false
+ 0
4700
@@ -37606,6 +42306,7 @@
false
false
false
+ 0
4701
@@ -37614,6 +42315,7 @@
false
false
false
+ 0
4702
@@ -37622,6 +42324,7 @@
false
false
false
+ 0
4703
@@ -37630,6 +42333,7 @@
false
false
false
+ 0
4704
@@ -37638,6 +42342,7 @@
false
false
false
+ 0
4705
@@ -37646,6 +42351,7 @@
false
false
false
+ 0
4706
@@ -37654,6 +42360,7 @@
false
false
false
+ 0
4707
@@ -37662,6 +42369,7 @@
false
false
false
+ 0
4708
@@ -37670,6 +42378,7 @@
false
false
false
+ 0
4709
@@ -37678,6 +42387,7 @@
false
false
false
+ 0
4710
@@ -37686,6 +42396,7 @@
false
false
false
+ 0
4711
@@ -37694,6 +42405,7 @@
false
false
false
+ 0
4712
@@ -37702,6 +42414,7 @@
false
false
false
+ 0
4713
@@ -37710,6 +42423,7 @@
false
false
false
+ 0
4714
@@ -37718,6 +42432,7 @@
false
false
false
+ 0
4715
@@ -37726,6 +42441,7 @@
false
false
false
+ 0
4716
@@ -37734,6 +42450,7 @@
false
false
false
+ 0
4717
@@ -37742,6 +42459,7 @@
false
false
false
+ 0
4718
@@ -37750,6 +42468,7 @@
false
false
false
+ 0
4719
@@ -37758,6 +42477,7 @@
false
false
false
+ 0
4720
@@ -37766,6 +42486,7 @@
false
false
false
+ 0
4721
@@ -37774,6 +42495,7 @@
false
false
false
+ 0
4722
@@ -37782,6 +42504,7 @@
false
false
false
+ 0
4723
@@ -37790,6 +42513,7 @@
false
false
false
+ 0
4724
@@ -37798,6 +42522,7 @@
false
false
false
+ 0
4725
@@ -37806,6 +42531,7 @@
false
false
false
+ 0
4726
@@ -37814,6 +42540,7 @@
false
false
false
+ 0
4727
@@ -37822,6 +42549,7 @@
false
false
false
+ 0
4728
@@ -37830,6 +42558,7 @@
false
false
false
+ 0
4729
@@ -37838,6 +42567,7 @@
false
false
false
+ 0
4730
@@ -37846,6 +42576,7 @@
false
false
false
+ 0
4731
@@ -37854,6 +42585,7 @@
false
false
false
+ 0
4732
@@ -37862,6 +42594,7 @@
false
false
false
+ 0
4733
@@ -37870,6 +42603,7 @@
false
false
false
+ 0
4734
@@ -37878,6 +42612,7 @@
false
false
false
+ 0
4735
@@ -37886,6 +42621,7 @@
false
false
false
+ 0
4736
@@ -37894,6 +42630,7 @@
false
false
false
+ 0
4737
@@ -37902,6 +42639,7 @@
false
false
false
+ 0
4738
@@ -37910,6 +42648,7 @@
false
false
false
+ 0
4739
@@ -37918,6 +42657,7 @@
false
false
false
+ 0
4740
@@ -37926,6 +42666,7 @@
false
false
false
+ 0
4741
@@ -37934,6 +42675,7 @@
false
false
false
+ 0
4742
@@ -37942,6 +42684,7 @@
false
false
false
+ 0
4743
@@ -37950,6 +42693,7 @@
false
false
false
+ 0
4744
@@ -37958,6 +42702,7 @@
false
false
false
+ 0
4745
@@ -37966,6 +42711,7 @@
false
false
false
+ 0
4746
@@ -37974,6 +42720,7 @@
false
false
false
+ 0
4747
@@ -37982,6 +42729,7 @@
false
false
false
+ 0
4748
@@ -37990,6 +42738,7 @@
false
false
false
+ 0
4749
@@ -37998,6 +42747,7 @@
false
false
false
+ 0
4750
@@ -38006,6 +42756,7 @@
false
false
false
+ 0
4751
@@ -38014,6 +42765,7 @@
false
false
false
+ 0
4752
@@ -38022,6 +42774,7 @@
false
false
false
+ 0
4753
@@ -38030,6 +42783,7 @@
false
false
false
+ 0
4754
@@ -38038,6 +42792,7 @@
false
false
false
+ 0
4755
@@ -38046,6 +42801,7 @@
false
false
false
+ 0
4756
@@ -38054,6 +42810,7 @@
false
false
false
+ 0
4757
@@ -38062,6 +42819,7 @@
false
false
false
+ 0
4758
@@ -38070,6 +42828,7 @@
false
false
false
+ 0
4759
@@ -38078,6 +42837,7 @@
false
false
false
+ 0
4760
@@ -38086,6 +42846,7 @@
false
false
false
+ 0
4761
@@ -38094,6 +42855,7 @@
false
false
false
+ 0
4762
@@ -38102,6 +42864,7 @@
false
false
false
+ 0
4763
@@ -38110,6 +42873,7 @@
false
false
false
+ 0
4764
@@ -38118,6 +42882,7 @@
false
false
false
+ 0
4765
@@ -38126,6 +42891,7 @@
false
false
false
+ 0
4766
@@ -38134,6 +42900,7 @@
false
false
false
+ 0
4767
@@ -38142,6 +42909,7 @@
false
false
false
+ 0
4768
@@ -38150,6 +42918,7 @@
false
false
false
+ 0
4769
@@ -38158,6 +42927,7 @@
false
false
false
+ 0
4770
@@ -38166,6 +42936,7 @@
false
false
false
+ 0
4771
@@ -38174,6 +42945,7 @@
false
false
false
+ 0
4772
@@ -38182,6 +42954,7 @@
false
false
false
+ 0
4773
@@ -38190,6 +42963,7 @@
false
false
false
+ 0
4774
@@ -38198,6 +42972,7 @@
false
false
false
+ 0
4775
@@ -38206,6 +42981,7 @@
false
false
false
+ 0
4776
@@ -38214,6 +42990,7 @@
false
false
false
+ 0
4777
@@ -38222,6 +42999,7 @@
false
false
false
+ 0
4778
@@ -38230,6 +43008,7 @@
false
false
false
+ 0
4779
@@ -38238,6 +43017,7 @@
false
false
false
+ 0
4780
@@ -38246,6 +43026,7 @@
false
false
false
+ 0
4781
@@ -38254,6 +43035,7 @@
false
false
false
+ 0
4782
@@ -38262,6 +43044,7 @@
false
false
false
+ 0
4783
@@ -38270,6 +43053,7 @@
false
false
false
+ 0
4784
@@ -38278,6 +43062,7 @@
false
false
false
+ 0
4785
@@ -38286,6 +43071,7 @@
false
false
false
+ 0
4786
@@ -38294,6 +43080,7 @@
false
false
false
+ 0
4787
@@ -38302,6 +43089,7 @@
false
false
false
+ 0
4788
@@ -38310,6 +43098,7 @@
false
false
false
+ 0
4789
@@ -38318,6 +43107,7 @@
false
false
false
+ 0
4790
@@ -38326,6 +43116,7 @@
false
false
false
+ 0
4791
@@ -38334,6 +43125,7 @@
false
false
false
+ 0
4792
@@ -38342,6 +43134,7 @@
false
false
false
+ 0
4793
@@ -38350,6 +43143,7 @@
false
false
false
+ 0
4794
@@ -38358,6 +43152,7 @@
false
false
false
+ 0
4795
@@ -38366,6 +43161,7 @@
false
false
false
+ 0
4796
@@ -38374,6 +43170,7 @@
false
false
false
+ 0
4797
@@ -38382,6 +43179,7 @@
false
false
false
+ 0
4798
@@ -38390,6 +43188,7 @@
false
false
false
+ 0
4799
@@ -38398,6 +43197,7 @@
false
false
false
+ 0
4800
@@ -38406,6 +43206,7 @@
false
false
false
+ 0
4801
@@ -38414,6 +43215,7 @@
false
false
false
+ 0
4802
@@ -38422,6 +43224,7 @@
false
false
false
+ 0
4803
@@ -38430,6 +43233,7 @@
false
false
false
+ 0
4804
@@ -38438,6 +43242,7 @@
false
false
false
+ 0
4805
@@ -38446,6 +43251,7 @@
false
false
false
+ 0
4806
@@ -38454,6 +43260,7 @@
false
false
false
+ 0
4807
@@ -38462,6 +43269,7 @@
false
false
false
+ 0
4808
@@ -38470,6 +43278,7 @@
false
false
false
+ 0
4809
@@ -38478,6 +43287,7 @@
false
false
false
+ 0
4810
@@ -38486,6 +43296,7 @@
false
false
false
+ 0
4811
@@ -38494,6 +43305,7 @@
false
false
false
+ 0
4812
@@ -38502,6 +43314,7 @@
false
false
false
+ 0
4813
@@ -38510,6 +43323,7 @@
false
false
false
+ 0
4814
@@ -38518,6 +43332,7 @@
false
false
false
+ 0
4815
@@ -38526,6 +43341,7 @@
false
false
false
+ 0
4816
@@ -38534,6 +43350,7 @@
false
false
false
+ 0
4817
@@ -38542,6 +43359,7 @@
false
false
false
+ 0
4818
@@ -38550,6 +43368,7 @@
false
false
false
+ 0
4819
@@ -38558,6 +43377,7 @@
false
false
false
+ 0
4820
@@ -38566,6 +43386,7 @@
false
false
false
+ 0
4821
@@ -38574,6 +43395,7 @@
false
false
false
+ 0
4822
@@ -38582,6 +43404,7 @@
false
false
false
+ 0
4823
@@ -38590,6 +43413,7 @@
false
false
false
+ 0
4824
@@ -38598,6 +43422,7 @@
false
false
false
+ 0
4825
@@ -38606,6 +43431,7 @@
false
false
false
+ 0
4826
@@ -38614,6 +43440,7 @@
false
false
false
+ 0
4827
@@ -38622,6 +43449,7 @@
false
false
false
+ 0
4828
@@ -38630,6 +43458,7 @@
false
false
false
+ 0
4829
@@ -38638,6 +43467,7 @@
false
false
false
+ 0
4830
@@ -38646,6 +43476,7 @@
false
false
false
+ 0
4831
@@ -38654,6 +43485,7 @@
false
false
false
+ 0
4832
@@ -38662,6 +43494,7 @@
false
false
false
+ 0
4833
@@ -38670,6 +43503,7 @@
false
false
false
+ 0
4834
@@ -38678,6 +43512,7 @@
false
false
false
+ 0
4835
@@ -38686,6 +43521,7 @@
false
false
false
+ 0
4836
@@ -38694,6 +43530,7 @@
false
false
false
+ 0
4837
@@ -38702,6 +43539,7 @@
false
false
false
+ 0
4838
@@ -38710,6 +43548,7 @@
false
false
false
+ 0
4839
@@ -38718,6 +43557,7 @@
false
false
false
+ 0
4840
@@ -38726,6 +43566,7 @@
false
false
false
+ 0
4841
@@ -38734,6 +43575,7 @@
false
false
false
+ 0
4842
@@ -38742,6 +43584,7 @@
false
false
false
+ 0
4843
@@ -38750,6 +43593,7 @@
false
false
false
+ 0
4844
@@ -38758,6 +43602,7 @@
false
false
false
+ 0
4845
@@ -38766,6 +43611,7 @@
false
false
false
+ 0
4846
@@ -38774,6 +43620,7 @@
false
false
false
+ 0
4847
@@ -38782,6 +43629,7 @@
false
false
false
+ 0
4848
@@ -38790,6 +43638,7 @@
false
false
false
+ 0
4849
@@ -38798,6 +43647,7 @@
false
false
false
+ 0
4850
@@ -38806,6 +43656,7 @@
false
false
false
+ 0
4851
@@ -38814,6 +43665,7 @@
false
false
false
+ 0
4852
@@ -38822,6 +43674,7 @@
false
false
false
+ 0
4853
@@ -38830,6 +43683,7 @@
false
false
false
+ 0
4854
@@ -38838,6 +43692,7 @@
false
false
false
+ 0
4855
@@ -38846,6 +43701,7 @@
false
false
false
+ 0
4856
@@ -38854,6 +43710,7 @@
false
false
false
+ 0
4857
@@ -38862,6 +43719,7 @@
false
false
false
+ 0
4858
@@ -38870,6 +43728,7 @@
false
false
false
+ 0
4859
@@ -38878,6 +43737,7 @@
false
false
false
+ 0
4860
@@ -38886,6 +43746,7 @@
false
false
false
+ 0
4861
@@ -38894,6 +43755,7 @@
false
false
false
+ 0
4862
@@ -38902,6 +43764,7 @@
false
false
false
+ 0
4863
@@ -38910,6 +43773,7 @@
false
false
false
+ 0
4864
@@ -38918,6 +43782,7 @@
false
false
false
+ 0
4865
@@ -38926,6 +43791,7 @@
false
false
false
+ 0
4866
@@ -38934,6 +43800,7 @@
false
false
false
+ 0
4867
@@ -38942,6 +43809,7 @@
false
false
false
+ 0
4868
@@ -38950,6 +43818,7 @@
false
false
false
+ 0
4869
@@ -38958,6 +43827,7 @@
false
false
false
+ 0
4870
@@ -38966,6 +43836,7 @@
false
false
false
+ 0
4871
@@ -38974,6 +43845,7 @@
false
false
false
+ 0
4872
@@ -38982,6 +43854,7 @@
false
false
false
+ 0
4873
@@ -38990,6 +43863,7 @@
false
false
false
+ 0
4874
@@ -38998,6 +43872,7 @@
false
false
false
+ 0
4875
@@ -39006,6 +43881,7 @@
false
false
false
+ 0
4876
@@ -39014,6 +43890,7 @@
false
false
false
+ 0
4877
@@ -39022,6 +43899,7 @@
false
false
false
+ 0
4878
@@ -39030,6 +43908,7 @@
false
false
false
+ 0
4879
@@ -39038,6 +43917,7 @@
false
false
false
+ 0
4880
@@ -39046,6 +43926,7 @@
false
false
false
+ 0
4881
@@ -39054,6 +43935,7 @@
false
false
false
+ 0
4882
@@ -39062,6 +43944,7 @@
false
false
false
+ 0
4883
@@ -39070,6 +43953,7 @@
false
false
false
+ 0
4884
@@ -39078,6 +43962,7 @@
false
false
false
+ 0
4885
@@ -39086,6 +43971,7 @@
false
false
false
+ 0
4886
@@ -39094,6 +43980,7 @@
false
false
false
+ 0
4887
@@ -39102,6 +43989,7 @@
false
false
false
+ 0
4888
@@ -39110,6 +43998,7 @@
false
false
false
+ 0
4889
@@ -39118,6 +44007,7 @@
false
false
false
+ 0
4890
@@ -39126,6 +44016,7 @@
false
false
false
+ 0
4891
@@ -39134,6 +44025,7 @@
false
false
false
+ 0
4892
@@ -39142,6 +44034,7 @@
false
false
false
+ 0
4893
@@ -39150,6 +44043,7 @@
false
false
false
+ 0
4894
@@ -39158,6 +44052,7 @@
false
false
false
+ 0
4895
@@ -39166,6 +44061,7 @@
false
false
false
+ 0
4896
@@ -39174,6 +44070,7 @@
false
false
false
+ 0
4897
@@ -39182,6 +44079,7 @@
false
false
false
+ 0
4898
@@ -39190,6 +44088,7 @@
false
false
false
+ 0
4899
@@ -39198,6 +44097,7 @@
false
false
false
+ 0
4900
@@ -39206,6 +44106,7 @@
false
false
false
+ 0
4901
@@ -39214,6 +44115,7 @@
false
false
false
+ 0
4902
@@ -39222,6 +44124,7 @@
false
false
false
+ 0
4903
@@ -39230,6 +44133,7 @@
false
false
false
+ 0
4904
@@ -39238,6 +44142,7 @@
false
false
false
+ 0
4905
@@ -39246,6 +44151,7 @@
false
false
false
+ 0
4906
@@ -39254,6 +44160,7 @@
false
false
false
+ 0
4907
@@ -39262,6 +44169,7 @@
false
false
false
+ 0
4908
@@ -39270,6 +44178,7 @@
false
false
false
+ 0
4909
@@ -39278,6 +44187,7 @@
false
false
false
+ 0
4910
@@ -39286,6 +44196,7 @@
false
false
false
+ 0
4911
@@ -39294,6 +44205,7 @@
false
false
false
+ 0
4912
@@ -39302,6 +44214,7 @@
false
false
false
+ 0
4913
@@ -39310,6 +44223,7 @@
false
false
false
+ 0
4914
@@ -39318,6 +44232,7 @@
false
false
false
+ 0
4915
@@ -39326,6 +44241,7 @@
false
false
false
+ 0
4916
@@ -39334,6 +44250,7 @@
false
false
false
+ 0
4917
@@ -39342,6 +44259,7 @@
false
false
false
+ 0
4918
@@ -39350,6 +44268,7 @@
false
false
false
+ 0
4919
@@ -39358,6 +44277,7 @@
false
false
false
+ 0
4920
@@ -39366,6 +44286,7 @@
false
false
false
+ 0
4921
@@ -39374,6 +44295,7 @@
false
false
false
+ 0
4922
@@ -39382,6 +44304,7 @@
false
false
false
+ 0
4923
@@ -39390,6 +44313,7 @@
false
false
false
+ 0
4924
@@ -39398,6 +44322,7 @@
false
false
false
+ 0
4925
@@ -39406,6 +44331,7 @@
false
false
false
+ 0
4926
@@ -39414,6 +44340,7 @@
false
false
false
+ 0
4927
@@ -39422,6 +44349,7 @@
false
false
false
+ 0
4928
@@ -39430,6 +44358,7 @@
false
false
false
+ 0
4929
@@ -39438,6 +44367,7 @@
false
false
false
+ 0
4930
@@ -39446,6 +44376,7 @@
false
false
false
+ 0
4931
@@ -39454,6 +44385,7 @@
false
false
false
+ 0
4932
@@ -39462,6 +44394,7 @@
false
false
false
+ 0
4933
@@ -39470,6 +44403,7 @@
false
false
false
+ 0
4934
@@ -39478,6 +44412,7 @@
false
false
false
+ 0
4935
@@ -39486,6 +44421,7 @@
false
false
false
+ 0
4936
@@ -39494,6 +44430,7 @@
false
false
false
+ 0
4937
@@ -39502,6 +44439,7 @@
false
false
false
+ 0
4938
@@ -39510,6 +44448,7 @@
false
false
false
+ 0
4939
@@ -39518,6 +44457,7 @@
false
false
false
+ 0
4940
@@ -39526,6 +44466,7 @@
false
false
false
+ 0
4941
@@ -39534,6 +44475,7 @@
false
false
false
+ 0
4942
@@ -39542,6 +44484,7 @@
false
false
false
+ 0
4943
@@ -39550,6 +44493,7 @@
false
false
false
+ 0
4944
@@ -39558,6 +44502,7 @@
false
false
false
+ 0
4945
@@ -39566,6 +44511,7 @@
false
false
false
+ 0
4946
@@ -39574,6 +44520,7 @@
false
false
false
+ 0
4947
@@ -39582,6 +44529,7 @@
false
false
false
+ 0
4948
@@ -39590,6 +44538,7 @@
false
false
false
+ 0
4949
@@ -39598,6 +44547,7 @@
false
false
false
+ 0
4950
@@ -39606,6 +44556,7 @@
false
false
false
+ 0
4951
@@ -39614,6 +44565,7 @@
false
false
false
+ 0
4952
@@ -39622,6 +44574,7 @@
false
false
false
+ 0
4953
@@ -39630,6 +44583,7 @@
false
false
false
+ 0
4954
@@ -39638,6 +44592,7 @@
false
false
false
+ 0
4955
@@ -39646,6 +44601,7 @@
false
false
false
+ 0
4956
@@ -39654,6 +44610,7 @@
false
false
false
+ 0
4957
@@ -39662,6 +44619,7 @@
false
false
false
+ 0
4958
@@ -39670,6 +44628,7 @@
false
false
false
+ 0
4959
@@ -39678,6 +44637,7 @@
false
false
false
+ 0
4960
@@ -39686,6 +44646,7 @@
false
false
false
+ 0
4961
@@ -39694,6 +44655,7 @@
false
false
false
+ 0
4962
@@ -39702,6 +44664,7 @@
false
false
false
+ 0
4963
@@ -39710,6 +44673,7 @@
false
false
false
+ 0
4964
@@ -39718,6 +44682,7 @@
false
false
false
+ 0
4965
@@ -39726,6 +44691,7 @@
false
false
false
+ 0
4966
@@ -39734,6 +44700,7 @@
false
false
false
+ 0
4967
@@ -39742,6 +44709,7 @@
false
false
false
+ 0
4968
@@ -39750,6 +44718,7 @@
false
false
false
+ 0
4969
@@ -39758,6 +44727,7 @@
false
false
false
+ 0
4970
@@ -39766,6 +44736,7 @@
false
false
false
+ 0
4971
@@ -39774,6 +44745,7 @@
false
false
false
+ 0
4972
@@ -39782,6 +44754,7 @@
false
false
false
+ 0
4973
@@ -39790,6 +44763,7 @@
false
false
false
+ 0
4974
@@ -39798,6 +44772,7 @@
false
false
false
+ 0
4975
@@ -39806,6 +44781,7 @@
false
false
false
+ 0
4976
@@ -39814,6 +44790,7 @@
false
false
false
+ 0
4977
@@ -39822,6 +44799,7 @@
false
false
false
+ 0
4978
@@ -39830,6 +44808,7 @@
false
false
false
+ 0
4979
@@ -39838,6 +44817,7 @@
false
false
false
+ 0
4980
@@ -39846,6 +44826,7 @@
false
false
false
+ 0
4981
@@ -39854,6 +44835,7 @@
false
false
false
+ 0
4982
@@ -39862,6 +44844,7 @@
false
false
false
+ 0
4983
@@ -39870,6 +44853,7 @@
false
false
false
+ 0
4984
@@ -39878,6 +44862,7 @@
false
false
false
+ 0
4985
@@ -39886,6 +44871,7 @@
false
false
false
+ 0
4986
@@ -39894,6 +44880,7 @@
false
false
false
+ 0
4987
@@ -39902,6 +44889,7 @@
false
false
false
+ 0
4988
@@ -39910,6 +44898,7 @@
false
false
false
+ 0
4989
@@ -39918,6 +44907,7 @@
false
false
false
+ 0
4990
@@ -39926,6 +44916,7 @@
false
false
false
+ 0
4991
@@ -39934,6 +44925,7 @@
false
false
false
+ 0
4992
@@ -39942,6 +44934,7 @@
false
false
false
+ 0
4993
@@ -39950,6 +44943,7 @@
false
false
false
+ 0
4994
@@ -39958,6 +44952,7 @@
false
false
false
+ 0
4995
@@ -39966,6 +44961,7 @@
false
false
false
+ 0
4996
@@ -39974,6 +44970,7 @@
false
false
false
+ 0
4997
@@ -39982,6 +44979,7 @@
false
false
false
+ 0
4998
@@ -39990,6 +44988,7 @@
false
false
false
+ 0
4999
@@ -39998,6 +44997,7 @@
false
false
false
+ 0
5000
@@ -40006,6 +45006,7 @@
false
false
false
+ 0
5001
@@ -40014,6 +45015,7 @@
false
false
false
+ 0
5002
@@ -40022,6 +45024,7 @@
false
false
false
+ 0
5003
@@ -40030,6 +45033,7 @@
false
false
false
+ 0
5004
@@ -40038,6 +45042,7 @@
false
false
false
+ 0
5005
@@ -40046,6 +45051,7 @@
false
false
false
+ 0
5006
@@ -40054,6 +45060,7 @@
false
false
false
+ 0
5007
@@ -40062,6 +45069,7 @@
false
false
false
+ 0
5008
@@ -40070,6 +45078,7 @@
false
false
false
+ 0
5009
@@ -40078,6 +45087,7 @@
false
false
false
+ 0
5010
@@ -40086,6 +45096,7 @@
false
false
false
+ 0
5011
@@ -40094,6 +45105,7 @@
false
false
false
+ 0
5012
@@ -40102,6 +45114,7 @@
false
false
false
+ 0
5013
@@ -40110,6 +45123,7 @@
false
false
false
+ 0
5014
@@ -40118,6 +45132,7 @@
false
false
false
+ 0
5015
@@ -40126,6 +45141,7 @@
false
false
false
+ 0
5016
@@ -40134,6 +45150,7 @@
false
false
false
+ 0
5017
@@ -40142,6 +45159,7 @@
false
false
false
+ 0
5018
@@ -40150,6 +45168,7 @@
false
false
false
+ 0
5019
@@ -40158,6 +45177,7 @@
false
false
false
+ 0
5020
@@ -40166,6 +45186,7 @@
false
false
false
+ 0
5021
@@ -40174,6 +45195,7 @@
false
false
false
+ 0
5022
@@ -40182,6 +45204,7 @@
false
false
false
+ 0
5023
@@ -40190,6 +45213,7 @@
false
false
false
+ 0
5024
@@ -40198,6 +45222,7 @@
false
false
false
+ 0
5025
@@ -40206,6 +45231,7 @@
false
false
false
+ 0
5026
@@ -40214,6 +45240,7 @@
false
false
false
+ 0
5027
@@ -40222,6 +45249,7 @@
false
false
false
+ 0
5028
@@ -40230,6 +45258,7 @@
false
false
false
+ 0
5029
@@ -40238,6 +45267,7 @@
false
false
false
+ 0
5030
@@ -40246,6 +45276,7 @@
false
false
false
+ 0
5031
@@ -40254,6 +45285,7 @@
false
false
false
+ 0
5032
@@ -40262,6 +45294,7 @@
false
false
false
+ 0
5033
@@ -40270,6 +45303,7 @@
false
false
false
+ 0
5034
@@ -40278,6 +45312,7 @@
false
false
false
+ 0
5035
@@ -40286,6 +45321,7 @@
false
false
false
+ 0
5036
@@ -40294,6 +45330,7 @@
false
false
false
+ 0
5037
@@ -40302,6 +45339,7 @@
false
false
false
+ 0
5038
@@ -40310,6 +45348,7 @@
false
false
false
+ 0
5039
@@ -40318,6 +45357,7 @@
false
false
false
+ 0
5040
@@ -40326,6 +45366,7 @@
false
false
false
+ 0
5041
@@ -40334,6 +45375,7 @@
false
false
false
+ 0
5042
@@ -40342,6 +45384,7 @@
false
false
false
+ 0
5043
@@ -40350,6 +45393,7 @@
false
false
false
+ 0
5044
@@ -40358,6 +45402,7 @@
false
false
false
+ 0
5045
@@ -40366,6 +45411,7 @@
false
false
false
+ 0
5046
@@ -40374,6 +45420,7 @@
false
false
false
+ 0
5047
@@ -40382,6 +45429,7 @@
false
false
false
+ 0
5048
@@ -40390,6 +45438,7 @@
false
false
false
+ 0
5049
@@ -40398,6 +45447,7 @@
false
false
false
+ 0
5050
@@ -40406,6 +45456,7 @@
false
false
false
+ 0
5051
@@ -40414,6 +45465,7 @@
false
false
false
+ 0
5052
@@ -40422,6 +45474,7 @@
false
false
false
+ 0
5053
@@ -40430,6 +45483,7 @@
false
false
false
+ 0
5054
@@ -40438,6 +45492,7 @@
false
false
false
+ 0
5055
@@ -40446,6 +45501,7 @@
false
false
false
+ 0
5056
@@ -40454,6 +45510,7 @@
false
false
false
+ 0
5057
@@ -40462,6 +45519,7 @@
false
false
false
+ 0
5058
@@ -40470,6 +45528,7 @@
false
false
false
+ 0
5059
@@ -40478,6 +45537,7 @@
false
false
false
+ 0
5060
@@ -40486,6 +45546,7 @@
false
false
false
+ 0
5061
@@ -40494,6 +45555,7 @@
false
false
false
+ 0
5062
@@ -40502,6 +45564,7 @@
false
false
false
+ 0
5063
@@ -40510,6 +45573,7 @@
false
false
false
+ 0
5064
@@ -40518,6 +45582,7 @@
false
false
false
+ 0
5065
@@ -40526,6 +45591,7 @@
false
false
false
+ 0
5066
@@ -40534,6 +45600,7 @@
false
false
false
+ 0
5067
@@ -40542,6 +45609,7 @@
false
false
false
+ 0
5068
@@ -40550,6 +45618,7 @@
false
false
false
+ 0
5069
@@ -40558,6 +45627,7 @@
false
false
false
+ 0
5070
@@ -40566,6 +45636,7 @@
false
false
false
+ 0
5071
@@ -40574,6 +45645,7 @@
false
false
false
+ 0
5072
@@ -40582,6 +45654,7 @@
false
false
false
+ 0
5073
@@ -40590,6 +45663,7 @@
false
false
false
+ 0
5074
@@ -40598,6 +45672,7 @@
false
false
false
+ 0
5075
@@ -40606,6 +45681,7 @@
false
false
false
+ 0
5076
@@ -40614,6 +45690,7 @@
false
false
false
+ 0
5077
@@ -40622,6 +45699,7 @@
false
false
false
+ 0
5078
@@ -40630,6 +45708,7 @@
false
false
false
+ 0
5079
@@ -40638,6 +45717,7 @@
false
false
false
+ 0
5080
@@ -40646,6 +45726,7 @@
false
false
false
+ 0
5081
@@ -40654,6 +45735,7 @@
false
false
false
+ 0
5082
@@ -40662,6 +45744,7 @@
false
false
false
+ 0
5083
@@ -40670,6 +45753,7 @@
false
false
false
+ 0
5084
@@ -40678,6 +45762,7 @@
false
false
false
+ 0
5085
@@ -40686,6 +45771,7 @@
false
false
false
+ 0
5086
@@ -40694,6 +45780,7 @@
false
false
false
+ 0
5087
@@ -40702,6 +45789,7 @@
false
false
false
+ 0
5088
@@ -40710,6 +45798,7 @@
false
false
false
+ 0
5089
@@ -40718,6 +45807,7 @@
false
false
false
+ 0
5090
@@ -40726,6 +45816,7 @@
false
false
false
+ 0
5091
@@ -40734,6 +45825,7 @@
false
false
false
+ 0
5092
@@ -40742,6 +45834,7 @@
false
false
false
+ 0
5093
@@ -40750,6 +45843,7 @@
false
false
false
+ 0
5094
@@ -40758,6 +45852,7 @@
false
false
false
+ 0
5095
@@ -40766,6 +45861,7 @@
false
false
false
+ 0
5096
@@ -40774,6 +45870,7 @@
false
false
false
+ 0
5097
@@ -40782,6 +45879,7 @@
false
false
false
+ 0
5098
@@ -40790,6 +45888,7 @@
false
false
false
+ 0
5099
@@ -40798,6 +45897,7 @@
false
false
false
+ 0
5100
@@ -40806,6 +45906,7 @@
false
false
false
+ 0
5101
@@ -40814,6 +45915,7 @@
false
false
false
+ 0
5102
@@ -40822,6 +45924,7 @@
false
false
false
+ 0
5103
@@ -40830,6 +45933,7 @@
false
false
false
+ 0
5104
@@ -40838,6 +45942,7 @@
false
false
false
+ 0
5105
@@ -40846,6 +45951,7 @@
false
false
false
+ 0
5106
@@ -40854,6 +45960,7 @@
false
false
false
+ 0
5107
@@ -40862,6 +45969,7 @@
false
false
false
+ 0
5108
@@ -40870,6 +45978,7 @@
false
false
false
+ 0
5109
@@ -40878,6 +45987,7 @@
false
false
false
+ 0
5110
@@ -40886,6 +45996,7 @@
false
false
false
+ 0
5111
@@ -40894,6 +46005,7 @@
false
false
false
+ 0
5112
@@ -40902,6 +46014,7 @@
false
false
false
+ 0
5113
@@ -40910,6 +46023,7 @@
false
false
false
+ 0
5114
@@ -40918,6 +46032,7 @@
false
false
false
+ 0
5115
@@ -40926,6 +46041,7 @@
false
false
false
+ 0
5116
@@ -40934,6 +46050,7 @@
false
false
false
+ 0
5117
@@ -40942,6 +46059,7 @@
false
false
false
+ 0
5118
@@ -40950,6 +46068,7 @@
false
false
false
+ 0
5119
@@ -40958,6 +46077,7 @@
false
false
false
+ 0
5120
@@ -40966,6 +46086,7 @@
false
false
false
+ 0
5121
@@ -40974,6 +46095,7 @@
false
false
false
+ 0
5122
@@ -40982,6 +46104,7 @@
false
false
false
+ 0
5123
@@ -40990,6 +46113,7 @@
false
false
false
+ 0
5124
@@ -40998,6 +46122,7 @@
false
false
false
+ 0
5125
@@ -41006,6 +46131,7 @@
false
false
false
+ 0
5126
@@ -41014,6 +46140,7 @@
false
false
false
+ 0
5127
@@ -41022,6 +46149,7 @@
false
false
false
+ 0
5128
@@ -41030,6 +46158,7 @@
false
false
false
+ 0
5129
@@ -41038,6 +46167,7 @@
false
false
false
+ 0
5130
@@ -41046,6 +46176,7 @@
false
false
false
+ 0
5131
@@ -41054,6 +46185,7 @@
false
false
false
+ 0
5132
@@ -41062,6 +46194,7 @@
false
false
false
+ 0
5133
@@ -41070,6 +46203,7 @@
false
false
false
+ 0
5134
@@ -41078,6 +46212,7 @@
false
false
false
+ 0
5135
@@ -41086,6 +46221,7 @@
false
false
false
+ 0
5136
@@ -41094,6 +46230,7 @@
false
false
false
+ 0
5137
@@ -41102,6 +46239,7 @@
false
false
false
+ 0
5138
@@ -41110,6 +46248,7 @@
false
false
false
+ 0
5139
@@ -41118,6 +46257,7 @@
false
false
false
+ 0
5140
@@ -41126,6 +46266,7 @@
false
false
false
+ 0
5141
@@ -41134,6 +46275,7 @@
false
false
false
+ 0
5142
@@ -41142,6 +46284,7 @@
false
false
false
+ 0
5143
@@ -41150,6 +46293,7 @@
false
false
false
+ 0
5144
@@ -41158,6 +46302,7 @@
false
false
false
+ 0
5145
@@ -41166,6 +46311,7 @@
false
false
false
+ 0
5146
@@ -41174,6 +46320,7 @@
false
false
false
+ 0
5147
@@ -41182,6 +46329,7 @@
false
false
false
+ 0
5148
@@ -41190,6 +46338,7 @@
false
false
false
+ 0
5149
@@ -41198,6 +46347,7 @@
false
false
false
+ 0
5150
@@ -41206,6 +46356,7 @@
false
false
false
+ 0
5151
@@ -41214,6 +46365,7 @@
false
false
false
+ 0
5152
@@ -41222,6 +46374,7 @@
false
false
false
+ 0
5153
@@ -41230,6 +46383,7 @@
false
false
false
+ 0
5154
@@ -41238,6 +46392,7 @@
false
false
false
+ 0
5155
@@ -41246,6 +46401,7 @@
false
false
false
+ 0
5156
@@ -41254,6 +46410,7 @@
false
false
false
+ 0
5157
@@ -41262,6 +46419,7 @@
false
false
false
+ 0
5158
@@ -41270,6 +46428,7 @@
false
false
false
+ 0
5159
@@ -41278,6 +46437,7 @@
false
false
false
+ 0
5160
@@ -41286,6 +46446,7 @@
false
false
false
+ 0
5161
@@ -41294,6 +46455,7 @@
false
false
false
+ 0
5162
@@ -41302,6 +46464,7 @@
false
false
false
+ 0
5163
@@ -41310,6 +46473,7 @@
false
false
false
+ 0
5164
@@ -41318,6 +46482,7 @@
false
false
false
+ 0
5165
@@ -41326,6 +46491,7 @@
false
false
false
+ 0
5166
@@ -41334,6 +46500,7 @@
false
false
false
+ 0
5167
@@ -41342,6 +46509,7 @@
false
false
false
+ 0
5168
@@ -41350,6 +46518,7 @@
false
false
false
+ 0
5169
@@ -41358,6 +46527,7 @@
false
false
false
+ 0
5170
@@ -41366,6 +46536,7 @@
false
false
false
+ 0
5171
@@ -41374,6 +46545,7 @@
false
false
false
+ 0
5172
@@ -41382,6 +46554,7 @@
false
false
false
+ 0
5173
@@ -41390,6 +46563,7 @@
false
false
false
+ 0
5174
@@ -41398,6 +46572,7 @@
false
false
false
+ 0
5175
@@ -41406,6 +46581,7 @@
false
false
false
+ 0
5176
@@ -41414,6 +46590,7 @@
false
false
false
+ 0
5177
@@ -41422,6 +46599,7 @@
false
false
false
+ 0
5178
@@ -41430,6 +46608,7 @@
false
false
false
+ 0
5179
@@ -41438,6 +46617,7 @@
false
false
false
+ 0
5180
@@ -41446,6 +46626,7 @@
false
false
false
+ 0
5181
@@ -41454,6 +46635,7 @@
false
false
false
+ 0
5182
@@ -41462,6 +46644,7 @@
false
false
false
+ 0
5183
@@ -41470,6 +46653,7 @@
false
false
false
+ 0
5184
@@ -41478,6 +46662,7 @@
false
false
false
+ 0
5185
@@ -41486,6 +46671,7 @@
false
false
false
+ 0
5186
@@ -41494,6 +46680,7 @@
false
false
false
+ 0
5187
@@ -41502,6 +46689,7 @@
false
false
false
+ 0
5188
@@ -41510,6 +46698,7 @@
false
false
false
+ 0
5189
@@ -41518,6 +46707,7 @@
false
false
false
+ 0
5190
@@ -41526,6 +46716,7 @@
false
false
false
+ 0
5191
@@ -41534,6 +46725,7 @@
false
false
false
+ 0
5192
@@ -41542,6 +46734,7 @@
false
false
false
+ 0
5193
@@ -41550,6 +46743,7 @@
false
false
false
+ 0
5194
@@ -41558,6 +46752,7 @@
false
false
false
+ 0
5195
@@ -41566,6 +46761,7 @@
false
false
false
+ 0
5196
@@ -41574,6 +46770,7 @@
false
false
false
+ 0
5197
@@ -41582,6 +46779,7 @@
false
false
false
+ 0
5198
@@ -41590,6 +46788,7 @@
false
false
false
+ 0
5199
@@ -41598,6 +46797,7 @@
false
false
false
+ 0
5200
@@ -41606,6 +46806,7 @@
false
false
false
+ 0
5201
@@ -41614,6 +46815,7 @@
false
false
false
+ 0
5202
@@ -41622,6 +46824,7 @@
false
false
false
+ 0
5203
@@ -41630,6 +46833,7 @@
false
false
false
+ 0
5204
@@ -41638,6 +46842,7 @@
false
false
false
+ 0
5205
@@ -41646,6 +46851,7 @@
false
false
false
+ 0
5206
@@ -41654,6 +46860,7 @@
false
false
false
+ 0
5207
@@ -41662,6 +46869,7 @@
false
false
false
+ 0
5208
@@ -41670,6 +46878,7 @@
false
false
false
+ 0
5209
@@ -41678,6 +46887,7 @@
false
false
false
+ 0
5210
@@ -41686,6 +46896,7 @@
false
false
false
+ 0
5211
@@ -41694,6 +46905,7 @@
false
false
false
+ 0
5212
@@ -41702,6 +46914,7 @@
false
false
false
+ 0
5213
@@ -41710,6 +46923,7 @@
false
false
false
+ 0
5214
@@ -41718,6 +46932,7 @@
false
false
false
+ 0
5215
@@ -41726,6 +46941,7 @@
false
false
false
+ 0
5216
@@ -41734,6 +46950,7 @@
false
false
false
+ 0
5217
@@ -41742,6 +46959,7 @@
false
false
false
+ 0
5218
@@ -41750,6 +46968,7 @@
false
false
false
+ 0
5219
@@ -41758,6 +46977,7 @@
false
false
false
+ 0
5220
@@ -41766,6 +46986,7 @@
false
false
false
+ 0
5221
@@ -41774,6 +46995,7 @@
false
false
false
+ 0
5222
@@ -41782,6 +47004,7 @@
false
false
false
+ 0
5223
@@ -41790,6 +47013,7 @@
false
false
false
+ 0
5224
@@ -41798,6 +47022,7 @@
false
false
false
+ 0
5225
@@ -41806,6 +47031,7 @@
false
false
false
+ 0
5226
@@ -41814,6 +47040,7 @@
false
false
false
+ 0
5227
@@ -41822,6 +47049,7 @@
false
false
false
+ 0
5228
@@ -41830,6 +47058,7 @@
false
false
false
+ 0
5229
@@ -41838,6 +47067,7 @@
false
false
false
+ 0
5230
@@ -41846,6 +47076,7 @@
false
false
false
+ 0
5231
@@ -41854,6 +47085,7 @@
false
false
false
+ 0
5232
@@ -41862,6 +47094,7 @@
false
false
false
+ 0
5233
@@ -41870,6 +47103,7 @@
false
false
false
+ 0
5234
@@ -41878,6 +47112,7 @@
false
false
false
+ 0
5235
@@ -41886,6 +47121,7 @@
false
false
false
+ 0
5236
@@ -41894,6 +47130,7 @@
false
false
false
+ 0
5237
@@ -41902,6 +47139,7 @@
false
false
false
+ 0
5238
@@ -41910,6 +47148,7 @@
false
false
false
+ 0
5239
@@ -41918,6 +47157,7 @@
false
false
false
+ 0
5240
@@ -41926,6 +47166,7 @@
false
false
false
+ 0
5241
@@ -41934,6 +47175,7 @@
false
false
false
+ 0
5242
@@ -41942,6 +47184,7 @@
false
false
false
+ 0
5243
@@ -41950,6 +47193,7 @@
false
false
false
+ 0
5244
@@ -41958,6 +47202,7 @@
false
false
false
+ 0
5245
@@ -41966,6 +47211,7 @@
false
false
false
+ 0
5246
@@ -41974,6 +47220,7 @@
false
false
false
+ 0
5247
@@ -41982,6 +47229,7 @@
false
false
false
+ 0
5248
@@ -41990,6 +47238,7 @@
false
false
false
+ 0
5249
@@ -41998,6 +47247,7 @@
false
false
false
+ 0
5250
@@ -42006,6 +47256,7 @@
false
false
false
+ 0
5251
@@ -42014,6 +47265,7 @@
false
false
false
+ 0
5252
@@ -42022,6 +47274,7 @@
false
false
false
+ 0
5253
@@ -42030,6 +47283,7 @@
false
false
false
+ 0
5254
@@ -42038,6 +47292,7 @@
false
false
false
+ 0
5255
@@ -42046,6 +47301,7 @@
false
false
false
+ 0
5256
@@ -42054,6 +47310,7 @@
false
false
false
+ 0
5257
@@ -42062,6 +47319,7 @@
false
false
false
+ 0
5258
@@ -42070,6 +47328,7 @@
false
false
false
+ 0
5259
@@ -42078,6 +47337,7 @@
false
false
false
+ 0
5260
@@ -42086,6 +47346,7 @@
false
false
false
+ 0
5261
@@ -42094,6 +47355,7 @@
false
false
false
+ 0
5262
@@ -42102,6 +47364,7 @@
false
false
false
+ 0
5263
@@ -42110,6 +47373,7 @@
false
false
false
+ 0
5264
@@ -42118,6 +47382,7 @@
false
false
false
+ 0
5265
@@ -42126,6 +47391,7 @@
false
false
false
+ 0
5266
@@ -42134,6 +47400,7 @@
false
false
false
+ 0
5267
@@ -42142,6 +47409,7 @@
false
false
false
+ 0
5268
@@ -42150,6 +47418,7 @@
false
false
false
+ 0
5269
@@ -42158,6 +47427,7 @@
false
false
false
+ 0
5270
@@ -42166,6 +47436,7 @@
false
false
false
+ 0
5271
@@ -42174,6 +47445,7 @@
false
false
false
+ 0
5272
@@ -42182,6 +47454,7 @@
false
false
false
+ 0
5273
@@ -42190,6 +47463,7 @@
false
false
false
+ 0
5274
@@ -42198,6 +47472,7 @@
false
false
false
+ 0
5275
@@ -42206,6 +47481,7 @@
false
false
false
+ 0
5276
@@ -42214,6 +47490,7 @@
false
false
false
+ 0
5277
@@ -42222,6 +47499,7 @@
false
false
false
+ 0
5278
@@ -42230,6 +47508,7 @@
false
false
false
+ 0
5279
@@ -42238,6 +47517,7 @@
false
false
false
+ 0
5280
@@ -42246,6 +47526,7 @@
false
false
false
+ 0
5281
@@ -42254,6 +47535,7 @@
false
false
false
+ 0
5282
@@ -42262,6 +47544,7 @@
false
false
false
+ 0
5283
@@ -42270,6 +47553,7 @@
false
false
false
+ 0
5284
@@ -42278,6 +47562,7 @@
false
false
false
+ 0
5285
@@ -42286,6 +47571,7 @@
false
false
false
+ 0
5286
@@ -42294,6 +47580,7 @@
false
false
false
+ 0
5287
@@ -42302,6 +47589,7 @@
false
false
false
+ 0
5288
@@ -42310,6 +47598,7 @@
false
false
false
+ 0
5289
@@ -42318,6 +47607,7 @@
false
false
false
+ 0
5290
@@ -42326,6 +47616,7 @@
false
false
false
+ 0
5291
@@ -42334,6 +47625,7 @@
false
false
false
+ 0
5292
@@ -42342,6 +47634,7 @@
false
false
false
+ 0
5293
@@ -42350,6 +47643,7 @@
false
false
false
+ 0
5294
@@ -42358,6 +47652,7 @@
false
false
false
+ 0
5295
@@ -42366,6 +47661,7 @@
false
false
false
+ 0
5296
@@ -42374,6 +47670,7 @@
false
false
false
+ 0
5297
@@ -42382,6 +47679,7 @@
false
false
false
+ 0
5298
@@ -42390,6 +47688,7 @@
false
false
false
+ 0
5299
@@ -42398,6 +47697,7 @@
false
false
false
+ 0
5300
@@ -42406,6 +47706,7 @@
false
false
false
+ 0
5301
@@ -42414,6 +47715,7 @@
false
false
false
+ 0
5302
@@ -42422,6 +47724,7 @@
false
false
false
+ 0
5303
@@ -42430,6 +47733,7 @@
false
false
false
+ 0
5304
@@ -42438,6 +47742,7 @@
false
false
false
+ 0
5305
@@ -42446,6 +47751,7 @@
false
false
false
+ 0
5306
@@ -42454,6 +47760,7 @@
false
false
false
+ 0
5307
@@ -42462,6 +47769,7 @@
false
false
false
+ 0
5308
@@ -42470,6 +47778,7 @@
false
false
false
+ 0
5309
@@ -42478,6 +47787,7 @@
false
false
false
+ 0
5310
@@ -42486,6 +47796,7 @@
false
false
false
+ 0
5311
@@ -42494,6 +47805,7 @@
false
false
false
+ 0
5312
@@ -42502,6 +47814,7 @@
false
false
false
+ 0
5313
@@ -42510,6 +47823,7 @@
false
false
false
+ 0
5314
@@ -42518,6 +47832,7 @@
false
false
false
+ 0
5315
@@ -42526,6 +47841,7 @@
false
false
false
+ 0
5316
@@ -42534,6 +47850,7 @@
false
false
false
+ 0
5317
@@ -42542,6 +47859,7 @@
false
false
false
+ 0
5318
@@ -42550,6 +47868,7 @@
false
false
false
+ 0
5319
@@ -42558,6 +47877,7 @@
false
false
false
+ 0
5320
@@ -42566,6 +47886,7 @@
false
false
false
+ 0
5321
@@ -42574,6 +47895,7 @@
false
false
false
+ 0
5322
@@ -42582,6 +47904,7 @@
false
false
false
+ 0
5323
@@ -42590,6 +47913,7 @@
false
false
false
+ 0
5324
@@ -42598,6 +47922,7 @@
false
false
false
+ 0
5325
@@ -42606,6 +47931,7 @@
false
false
false
+ 0
5326
@@ -42614,6 +47940,7 @@
false
false
false
+ 0
5327
@@ -42622,6 +47949,7 @@
false
false
false
+ 0
5328
@@ -42630,6 +47958,7 @@
false
false
false
+ 0
5329
@@ -42638,6 +47967,7 @@
false
false
false
+ 0
5330
@@ -42646,6 +47976,7 @@
false
false
false
+ 0
5331
@@ -42654,6 +47985,7 @@
false
false
false
+ 0
5332
@@ -42662,6 +47994,7 @@
false
false
false
+ 0
5333
@@ -42670,6 +48003,7 @@
false
false
false
+ 0
5334
@@ -42678,6 +48012,7 @@
false
false
false
+ 0
5335
@@ -42686,6 +48021,7 @@
false
false
false
+ 0
5336
@@ -42694,6 +48030,7 @@
false
false
false
+ 0
5337
@@ -42702,6 +48039,7 @@
false
false
false
+ 0
5338
@@ -42710,6 +48048,7 @@
false
false
false
+ 0
5339
@@ -42718,6 +48057,7 @@
false
false
false
+ 0
5340
@@ -42726,6 +48066,7 @@
false
false
false
+ 0
5341
@@ -42734,6 +48075,7 @@
false
false
false
+ 0
5342
@@ -42742,6 +48084,7 @@
false
false
false
+ 0
5343
@@ -42750,6 +48093,7 @@
false
false
false
+ 0
5344
@@ -42758,6 +48102,7 @@
false
false
false
+ 0
5345
@@ -42766,6 +48111,7 @@
false
false
false
+ 0
5346
@@ -42774,6 +48120,7 @@
false
false
false
+ 0
5347
@@ -42782,6 +48129,7 @@
false
false
false
+ 0
5348
@@ -42790,6 +48138,7 @@
false
false
false
+ 0
5349
@@ -42798,6 +48147,7 @@
false
false
false
+ 0
5350
@@ -42806,6 +48156,7 @@
false
false
false
+ 0
5351
@@ -42814,6 +48165,7 @@
false
false
false
+ 0
5352
@@ -42822,6 +48174,7 @@
false
false
false
+ 0
5353
@@ -42830,6 +48183,7 @@
false
false
false
+ 0
5354
@@ -42838,6 +48192,7 @@
false
false
false
+ 0
5355
@@ -42846,6 +48201,7 @@
false
false
false
+ 0
5356
@@ -42854,6 +48210,7 @@
false
false
false
+ 0
5357
@@ -42862,6 +48219,7 @@
false
false
false
+ 0
5358
@@ -42870,6 +48228,7 @@
false
false
false
+ 0
5359
@@ -42878,6 +48237,7 @@
false
false
false
+ 0
5360
@@ -42886,6 +48246,7 @@
false
false
false
+ 0
5361
@@ -42894,6 +48255,7 @@
false
false
false
+ 0
5362
@@ -42902,6 +48264,7 @@
false
false
false
+ 0
5363
@@ -42910,6 +48273,7 @@
false
false
false
+ 0
5364
@@ -42918,6 +48282,7 @@
false
false
false
+ 0
5365
@@ -42926,6 +48291,7 @@
false
false
false
+ 0
5366
@@ -42934,6 +48300,7 @@
false
false
false
+ 0
5367
@@ -42942,6 +48309,7 @@
false
false
false
+ 0
5368
@@ -42950,6 +48318,7 @@
false
false
false
+ 0
5369
@@ -42958,6 +48327,7 @@
false
false
false
+ 0
5370
@@ -42966,6 +48336,7 @@
false
false
false
+ 0
5371
@@ -42974,6 +48345,7 @@
false
false
false
+ 0
5372
@@ -42982,6 +48354,7 @@
false
false
false
+ 0
5373
@@ -42990,6 +48363,7 @@
false
false
false
+ 0
5374
@@ -42998,6 +48372,7 @@
false
false
false
+ 0
5375
@@ -43006,6 +48381,7 @@
false
false
false
+ 0
5376
@@ -43014,6 +48390,7 @@
false
false
false
+ 0
5377
@@ -43022,6 +48399,7 @@
false
false
false
+ 0
5378
@@ -43030,6 +48408,7 @@
false
false
false
+ 0
5379
@@ -43038,6 +48417,7 @@
false
false
false
+ 0
5380
@@ -43046,6 +48426,7 @@
false
false
false
+ 0
5381
@@ -43054,6 +48435,7 @@
false
false
false
+ 0
5382
@@ -43062,6 +48444,7 @@
false
false
false
+ 0
5383
@@ -43070,6 +48453,7 @@
false
false
false
+ 0
5384
@@ -43078,6 +48462,7 @@
false
false
false
+ 0
5385
@@ -43086,6 +48471,7 @@
false
false
false
+ 0
5386
@@ -43094,6 +48480,7 @@
false
false
false
+ 0
5387
@@ -43102,6 +48489,7 @@
false
false
false
+ 0
5388
@@ -43110,6 +48498,7 @@
false
false
false
+ 0
5389
@@ -43118,6 +48507,7 @@
false
false
false
+ 0
5390
@@ -43126,6 +48516,7 @@
false
false
false
+ 0
5391
@@ -43134,6 +48525,7 @@
false
false
false
+ 0
5392
@@ -43142,6 +48534,7 @@
false
false
false
+ 0
5393
@@ -43150,6 +48543,7 @@
false
false
false
+ 0
5394
@@ -43158,6 +48552,7 @@
false
false
false
+ 0
5395
@@ -43166,6 +48561,7 @@
false
false
false
+ 0
5396
@@ -43174,6 +48570,7 @@
false
false
false
+ 0
5397
@@ -43182,6 +48579,7 @@
false
false
false
+ 0
5398
@@ -43190,6 +48588,7 @@
false
false
false
+ 0
5399
@@ -43198,6 +48597,7 @@
false
false
false
+ 0
5400
@@ -43206,6 +48606,7 @@
false
false
false
+ 0
5401
@@ -43214,6 +48615,7 @@
false
false
false
+ 0
5402
@@ -43222,6 +48624,7 @@
false
false
false
+ 0
5403
@@ -43230,6 +48633,7 @@
false
false
false
+ 0
5404
@@ -43238,6 +48642,7 @@
false
false
false
+ 0
5405
@@ -43246,6 +48651,7 @@
false
false
false
+ 0
5406
@@ -43254,6 +48660,7 @@
false
false
false
+ 0
5407
@@ -43262,6 +48669,7 @@
false
false
false
+ 0
5408
@@ -43270,6 +48678,7 @@
false
false
false
+ 0
5409
@@ -43278,6 +48687,7 @@
false
false
false
+ 0
5410
@@ -43286,6 +48696,7 @@
false
false
false
+ 0
5411
@@ -43294,6 +48705,7 @@
false
false
false
+ 0
5412
@@ -43302,6 +48714,7 @@
false
false
false
+ 0
5413
@@ -43310,6 +48723,7 @@
false
false
false
+ 0
5414
@@ -43318,6 +48732,7 @@
false
false
false
+ 0
5415
@@ -43326,6 +48741,7 @@
false
false
false
+ 0
5416
@@ -43334,6 +48750,7 @@
false
false
false
+ 0
5417
@@ -43342,6 +48759,7 @@
false
false
false
+ 0
5418
@@ -43350,6 +48768,7 @@
false
false
false
+ 0
5419
@@ -43358,6 +48777,7 @@
false
false
false
+ 0
5420
@@ -43366,6 +48786,7 @@
false
false
false
+ 0
5421
@@ -43374,6 +48795,7 @@
false
false
false
+ 0
5422
@@ -43382,6 +48804,7 @@
false
false
false
+ 0
5423
@@ -43390,6 +48813,7 @@
false
false
false
+ 0
5424
@@ -43398,6 +48822,7 @@
false
false
false
+ 0
5425
@@ -43406,6 +48831,7 @@
false
false
false
+ 0
5426
@@ -43414,6 +48840,7 @@
false
false
false
+ 0
5427
@@ -43422,6 +48849,7 @@
false
false
false
+ 0
5428
@@ -43430,6 +48858,7 @@
false
false
false
+ 0
5429
@@ -43438,6 +48867,7 @@
false
false
false
+ 0
5430
@@ -43446,6 +48876,7 @@
false
false
false
+ 0
5431
@@ -43454,6 +48885,7 @@
false
false
false
+ 0
5432
@@ -43462,6 +48894,7 @@
false
false
false
+ 0
5433
@@ -43470,6 +48903,7 @@
false
false
false
+ 0
5434
@@ -43478,6 +48912,7 @@
false
false
false
+ 0
5435
@@ -43486,6 +48921,7 @@
false
false
false
+ 0
5436
@@ -43494,6 +48930,7 @@
false
false
false
+ 0
5437
@@ -43502,6 +48939,7 @@
false
false
false
+ 0
5438
@@ -43510,6 +48948,7 @@
false
false
false
+ 0
5439
@@ -43518,6 +48957,7 @@
false
false
false
+ 0
5440
@@ -43526,6 +48966,7 @@
false
false
false
+ 0
5441
@@ -43534,6 +48975,7 @@
false
false
false
+ 0
5442
@@ -43542,6 +48984,7 @@
false
false
false
+ 0
5443
@@ -43550,6 +48993,7 @@
false
false
false
+ 0
5444
@@ -43558,6 +49002,7 @@
false
false
false
+ 0
5445
@@ -43566,6 +49011,7 @@
false
false
false
+ 0
5446
@@ -43574,6 +49020,7 @@
false
false
false
+ 0
5447
@@ -43582,6 +49029,7 @@
false
false
false
+ 0
5448
@@ -43590,6 +49038,7 @@
false
false
false
+ 0
5449
@@ -43598,6 +49047,7 @@
false
false
false
+ 0
5450
@@ -43606,6 +49056,7 @@
false
false
false
+ 0
5451
@@ -43614,6 +49065,7 @@
false
false
false
+ 0
5452
@@ -43622,6 +49074,7 @@
false
false
false
+ 0
5453
@@ -43630,6 +49083,7 @@
false
false
false
+ 0
5454
@@ -43638,6 +49092,7 @@
false
false
false
+ 0
5455
@@ -43646,6 +49101,7 @@
false
false
false
+ 0
5456
@@ -43654,6 +49110,7 @@
false
false
false
+ 0
5457
@@ -43662,6 +49119,7 @@
false
false
false
+ 0
5458
@@ -43670,6 +49128,7 @@
false
false
false
+ 0
5459
@@ -43678,6 +49137,7 @@
false
false
false
+ 0
5460
@@ -43686,6 +49146,7 @@
false
false
false
+ 0
5461
@@ -43694,6 +49155,7 @@
false
false
false
+ 0
5462
@@ -43702,6 +49164,7 @@
false
false
false
+ 0
5463
@@ -43710,6 +49173,7 @@
false
false
false
+ 0
5464
@@ -43718,6 +49182,7 @@
false
false
false
+ 0
5465
@@ -43726,6 +49191,7 @@
false
false
false
+ 0
5466
@@ -43734,6 +49200,7 @@
false
false
false
+ 0
5467
@@ -43742,6 +49209,7 @@
false
false
false
+ 0
5468
@@ -43750,6 +49218,7 @@
false
false
false
+ 0
5469
@@ -43758,6 +49227,7 @@
false
false
false
+ 0
5470
@@ -43766,6 +49236,7 @@
false
false
false
+ 0
5471
@@ -43774,6 +49245,7 @@
false
false
false
+ 0
5472
@@ -43782,6 +49254,7 @@
false
false
false
+ 0
5473
@@ -43790,6 +49263,7 @@
false
false
false
+ 0
5474
@@ -43798,6 +49272,7 @@
false
false
false
+ 0
5475
@@ -43806,6 +49281,7 @@
false
false
false
+ 0
5476
@@ -43814,6 +49290,7 @@
false
false
false
+ 0
5477
@@ -43822,6 +49299,7 @@
false
false
false
+ 0
5478
@@ -43830,6 +49308,7 @@
false
false
false
+ 0
5479
@@ -43838,6 +49317,7 @@
false
false
false
+ 0
5480
@@ -43846,6 +49326,7 @@
false
false
false
+ 0
5481
@@ -43854,6 +49335,7 @@
false
false
false
+ 0
5482
@@ -43862,6 +49344,7 @@
false
false
false
+ 0
5483
@@ -43870,6 +49353,7 @@
false
false
false
+ 0
5484
@@ -43878,6 +49362,7 @@
false
false
false
+ 0
5485
@@ -43886,6 +49371,7 @@
false
false
false
+ 0
5486
@@ -43894,6 +49380,7 @@
false
false
false
+ 0
5487
@@ -43902,6 +49389,7 @@
false
false
false
+ 0
5488
@@ -43910,6 +49398,7 @@
false
false
false
+ 0
5489
@@ -43918,6 +49407,7 @@
false
false
false
+ 0
5490
@@ -43926,6 +49416,7 @@
false
false
false
+ 0
5491
@@ -43934,6 +49425,7 @@
false
false
false
+ 0
5492
@@ -43942,6 +49434,7 @@
false
false
false
+ 0
5493
@@ -43950,6 +49443,7 @@
false
false
false
+ 0
5494
@@ -43958,6 +49452,7 @@
false
false
false
+ 0
5495
@@ -43966,6 +49461,7 @@
false
false
false
+ 0
5496
@@ -43974,6 +49470,7 @@
false
false
false
+ 0
5497
@@ -43982,6 +49479,7 @@
false
false
false
+ 0
5498
@@ -43990,6 +49488,7 @@
false
false
false
+ 0
5499
@@ -43998,6 +49497,7 @@
false
false
false
+ 0
5500
@@ -44006,6 +49506,7 @@
false
false
false
+ 0
5501
@@ -44014,6 +49515,7 @@
false
false
false
+ 0
5502
@@ -44022,6 +49524,7 @@
false
false
false
+ 0
5503
@@ -44030,6 +49533,7 @@
false
false
false
+ 0
5504
@@ -44038,6 +49542,7 @@
false
false
false
+ 0
5505
@@ -44046,6 +49551,7 @@
false
false
false
+ 0
5506
@@ -44054,6 +49560,7 @@
false
false
false
+ 0
5507
@@ -44062,6 +49569,7 @@
false
false
false
+ 0
5508
@@ -44070,6 +49578,7 @@
false
false
false
+ 0
5509
@@ -44078,6 +49587,7 @@
false
false
false
+ 0
5510
@@ -44086,6 +49596,7 @@
false
false
false
+ 0
5511
@@ -44094,6 +49605,7 @@
false
false
false
+ 0
5512
@@ -44102,6 +49614,7 @@
false
false
false
+ 0
5513
@@ -44110,6 +49623,7 @@
false
false
false
+ 0
5514
@@ -44118,6 +49632,7 @@
false
false
false
+ 0
5515
@@ -44126,6 +49641,7 @@
false
false
false
+ 0
5516
@@ -44134,6 +49650,7 @@
false
false
false
+ 0
5517
@@ -44142,6 +49659,7 @@
false
false
false
+ 0
5518
@@ -44150,6 +49668,7 @@
false
false
false
+ 0
5519
@@ -44158,6 +49677,7 @@
false
false
false
+ 0
5520
@@ -44166,6 +49686,7 @@
false
false
false
+ 0
5521
@@ -44174,6 +49695,7 @@
false
false
false
+ 0
5522
@@ -44182,6 +49704,7 @@
false
false
false
+ 0
5523
@@ -44190,6 +49713,7 @@
false
false
false
+ 0
5524
@@ -44198,6 +49722,7 @@
false
false
false
+ 0
5525
@@ -44206,6 +49731,7 @@
false
false
false
+ 0
5526
@@ -44214,6 +49740,7 @@
false
false
false
+ 0
5527
@@ -44222,6 +49749,7 @@
false
false
false
+ 0
5528
@@ -44230,6 +49758,7 @@
false
false
false
+ 0
5529
@@ -44238,6 +49767,7 @@
false
false
false
+ 0
5530
@@ -44246,6 +49776,7 @@
false
false
false
+ 0
5531
@@ -44254,6 +49785,7 @@
false
false
false
+ 0
5532
@@ -44262,6 +49794,7 @@
false
false
false
+ 0
5533
@@ -44270,6 +49803,7 @@
false
false
false
+ 0
5534
@@ -44278,6 +49812,7 @@
false
false
false
+ 0
5535
@@ -44286,6 +49821,7 @@
false
false
false
+ 0
5536
@@ -44294,6 +49830,7 @@
false
false
false
+ 0
5537
@@ -44302,6 +49839,7 @@
false
false
false
+ 0
5538
@@ -44310,6 +49848,7 @@
false
false
false
+ 0
5539
@@ -44318,6 +49857,7 @@
false
false
false
+ 0
5540
@@ -44326,6 +49866,7 @@
false
false
false
+ 0
5541
@@ -44334,6 +49875,7 @@
false
false
false
+ 0
5542
@@ -44342,6 +49884,7 @@
false
false
false
+ 0
5543
@@ -44350,6 +49893,7 @@
false
false
false
+ 0
5544
@@ -44358,6 +49902,7 @@
false
false
false
+ 0
5545
@@ -44366,6 +49911,7 @@
false
false
false
+ 0
5546
@@ -44374,6 +49920,7 @@
false
false
false
+ 0
5547
@@ -44382,6 +49929,7 @@
false
false
false
+ 0
5548
@@ -44390,6 +49938,7 @@
false
false
false
+ 0
5549
@@ -44398,6 +49947,7 @@
false
false
false
+ 0
5550
@@ -44406,6 +49956,7 @@
false
false
false
+ 0
5551
@@ -44414,6 +49965,7 @@
false
false
false
+ 0
5552
@@ -44422,6 +49974,7 @@
false
false
false
+ 0
5553
@@ -44430,6 +49983,7 @@
false
false
false
+ 0
5554
@@ -44438,6 +49992,7 @@
false
false
false
+ 0
5555
@@ -44446,6 +50001,7 @@
false
false
false
+ 0
5556
@@ -44454,6 +50010,7 @@
false
false
false
+ 0
5557
@@ -44462,6 +50019,7 @@
false
false
false
+ 0
5558
@@ -44470,6 +50028,7 @@
false
false
false
+ 0
5559
@@ -44478,6 +50037,7 @@
false
false
false
+ 0
5560
@@ -44486,6 +50046,7 @@
false
false
false
+ 0
5561
@@ -44494,6 +50055,7 @@
false
false
false
+ 0
5562
@@ -44502,6 +50064,7 @@
false
false
false
+ 0
5563
@@ -44510,6 +50073,7 @@
false
false
false
+ 0
5564
@@ -44518,6 +50082,7 @@
false
false
false
+ 0
5565
@@ -44526,6 +50091,7 @@
false
false
false
+ 0
5566
@@ -44534,6 +50100,7 @@
false
false
false
+ 0
5567
@@ -44542,6 +50109,7 @@
false
false
false
+ 0
5568
@@ -44550,6 +50118,7 @@
false
false
false
+ 0
5569
@@ -44558,6 +50127,7 @@
false
false
false
+ 0
5570
@@ -44566,6 +50136,7 @@
false
false
false
+ 0
5571
@@ -44574,6 +50145,7 @@
false
false
false
+ 0
5572
@@ -44582,6 +50154,7 @@
false
false
false
+ 0
5573
@@ -44590,6 +50163,7 @@
false
false
false
+ 0
5574
@@ -44598,6 +50172,7 @@
false
false
false
+ 0
5575
@@ -44606,6 +50181,7 @@
false
false
false
+ 0
5576
@@ -44614,6 +50190,7 @@
false
false
false
+ 0
5577
@@ -44622,6 +50199,7 @@
false
false
false
+ 0
5578
@@ -44630,6 +50208,7 @@
false
false
false
+ 0
5579
@@ -44638,6 +50217,7 @@
false
false
false
+ 0
5580
@@ -44646,6 +50226,7 @@
false
false
false
+ 0
5581
@@ -44654,6 +50235,7 @@
false
false
false
+ 0
5582
@@ -44662,6 +50244,7 @@
false
false
false
+ 0
5583
@@ -44670,6 +50253,7 @@
false
false
false
+ 0
5584
@@ -44678,6 +50262,7 @@
false
false
false
+ 0
5585
@@ -44686,6 +50271,7 @@
false
false
false
+ 0
5586
@@ -44694,6 +50280,7 @@
false
false
false
+ 0
5587
@@ -44702,6 +50289,7 @@
false
false
false
+ 0
5588
@@ -44710,6 +50298,7 @@
false
false
false
+ 0
5589
@@ -44718,6 +50307,7 @@
false
false
false
+ 0
5590
@@ -44726,6 +50316,7 @@
false
false
false
+ 0
5591
@@ -44734,6 +50325,7 @@
false
false
false
+ 0
5592
@@ -44742,6 +50334,7 @@
false
false
false
+ 0
5593
@@ -44750,6 +50343,7 @@
false
false
false
+ 0
5594
@@ -44758,6 +50352,7 @@
false
false
false
+ 0
5595
@@ -44766,6 +50361,7 @@
false
false
false
+ 0
5596
@@ -44774,6 +50370,7 @@
false
false
false
+ 0
5597
@@ -44782,6 +50379,7 @@
false
false
false
+ 0
5598
@@ -44790,6 +50388,7 @@
false
false
false
+ 0
5599
@@ -44798,6 +50397,7 @@
false
false
false
+ 0
5600
@@ -44806,6 +50406,7 @@
false
false
false
+ 0
5601
@@ -44814,6 +50415,7 @@
false
false
false
+ 0
5602
@@ -44822,6 +50424,7 @@
false
false
false
+ 0
5603
@@ -44830,6 +50433,7 @@
false
false
false
+ 0
5604
@@ -44838,6 +50442,7 @@
false
false
false
+ 0
5605
@@ -44846,6 +50451,7 @@
false
false
false
+ 0
5606
@@ -44854,6 +50460,7 @@
false
false
false
+ 0
5607
@@ -44862,6 +50469,7 @@
false
false
false
+ 0
5608
@@ -44870,6 +50478,7 @@
false
false
false
+ 0
5609
@@ -44878,6 +50487,7 @@
false
false
false
+ 0
5610
@@ -44886,6 +50496,7 @@
false
false
false
+ 0
5611
@@ -44894,6 +50505,7 @@
false
false
false
+ 0
5612
@@ -44902,6 +50514,7 @@
false
false
false
+ 0
5613
@@ -44910,6 +50523,7 @@
false
false
false
+ 0
5614
@@ -44918,6 +50532,7 @@
false
false
false
+ 0
5615
@@ -44926,6 +50541,7 @@
false
false
false
+ 0
5616
@@ -44934,6 +50550,7 @@
false
false
false
+ 0
5617
@@ -44942,6 +50559,7 @@
false
false
false
+ 0
5618
@@ -44950,6 +50568,7 @@
false
false
false
+ 0
5619
@@ -44958,6 +50577,7 @@
false
false
false
+ 0
5620
@@ -44966,6 +50586,7 @@
false
false
false
+ 0
5621
@@ -44974,6 +50595,7 @@
false
false
false
+ 0
5622
@@ -44982,6 +50604,7 @@
false
false
false
+ 0
5623
@@ -44990,6 +50613,7 @@
false
false
false
+ 0
5624
@@ -44998,6 +50622,7 @@
false
false
false
+ 0
5625
@@ -45006,6 +50631,7 @@
false
false
false
+ 0
5626
@@ -45014,6 +50640,7 @@
false
false
false
+ 0
5627
@@ -45022,6 +50649,7 @@
false
false
false
+ 0
5628
@@ -45030,6 +50658,7 @@
false
false
false
+ 0
5629
@@ -45038,6 +50667,7 @@
false
false
false
+ 0
5630
@@ -45046,6 +50676,7 @@
false
false
false
+ 0
5631
@@ -45054,6 +50685,7 @@
false
false
false
+ 0
5632
@@ -45062,6 +50694,7 @@
false
false
false
+ 0
5633
@@ -45070,6 +50703,7 @@
false
false
false
+ 0
5634
@@ -45078,6 +50712,7 @@
false
false
false
+ 0
5635
@@ -45086,6 +50721,7 @@
false
false
false
+ 0
5636
@@ -45094,6 +50730,7 @@
false
false
false
+ 0
5637
@@ -45102,6 +50739,7 @@
false
false
false
+ 0
5638
@@ -45110,6 +50748,7 @@
false
false
false
+ 0
5639
@@ -45118,6 +50757,7 @@
false
false
false
+ 0
5640
@@ -45126,6 +50766,7 @@
false
false
false
+ 0
5641
@@ -45134,6 +50775,7 @@
false
false
false
+ 0
5642
@@ -45142,6 +50784,7 @@
false
false
false
+ 0
5643
@@ -45150,6 +50793,7 @@
false
false
false
+ 0
5644
@@ -45158,6 +50802,7 @@
false
false
false
+ 0
5645
@@ -45166,6 +50811,7 @@
false
false
false
+ 0
5646
@@ -45174,6 +50820,7 @@
false
false
false
+ 0
5647
@@ -45182,6 +50829,7 @@
false
false
false
+ 0
5648
@@ -45190,6 +50838,7 @@
false
false
false
+ 0
5649
@@ -45198,6 +50847,7 @@
false
false
false
+ 0
5650
@@ -45206,6 +50856,7 @@
false
false
false
+ 0
5651
@@ -45214,6 +50865,7 @@
false
false
false
+ 0
5652
@@ -45222,6 +50874,7 @@
false
false
false
+ 0
5653
@@ -45230,6 +50883,7 @@
false
false
false
+ 0
5654
@@ -45238,6 +50892,7 @@
false
false
false
+ 0
5655
@@ -45246,6 +50901,7 @@
false
false
false
+ 0
5656
@@ -45254,6 +50910,7 @@
false
false
false
+ 0
5657
@@ -45262,6 +50919,7 @@
false
false
false
+ 0
5658
@@ -45270,6 +50928,7 @@
false
false
false
+ 0
5659
@@ -45278,6 +50937,7 @@
false
false
false
+ 0
5660
@@ -45286,6 +50946,7 @@
false
false
false
+ 0
5661
@@ -45294,6 +50955,7 @@
false
false
false
+ 0
5662
@@ -45302,6 +50964,7 @@
false
false
false
+ 0
5663
@@ -45310,6 +50973,7 @@
false
false
false
+ 0
5664
@@ -45318,6 +50982,7 @@
false
false
false
+ 0
5665
@@ -45326,6 +50991,7 @@
false
false
false
+ 0
5666
@@ -45334,6 +51000,7 @@
false
false
false
+ 0
5667
@@ -45342,6 +51009,7 @@
false
false
false
+ 0
5668
@@ -45350,6 +51018,7 @@
false
false
false
+ 0
5669
@@ -45358,6 +51027,7 @@
false
false
false
+ 0
5670
@@ -45366,6 +51036,7 @@
false
false
false
+ 0
5671
@@ -45374,6 +51045,7 @@
false
false
false
+ 0
5672
@@ -45382,6 +51054,7 @@
false
false
false
+ 0
5673
@@ -45390,6 +51063,7 @@
false
false
false
+ 0
5674
@@ -45398,6 +51072,7 @@
false
false
false
+ 0
5675
@@ -45406,6 +51081,7 @@
false
false
false
+ 0
5676
@@ -45414,6 +51090,7 @@
false
false
false
+ 0
5677
@@ -45422,6 +51099,7 @@
false
false
false
+ 0
5678
@@ -45430,6 +51108,7 @@
false
false
false
+ 0
5679
@@ -45438,6 +51117,7 @@
false
false
false
+ 0
5680
@@ -45446,6 +51126,7 @@
false
false
false
+ 0
5681
@@ -45454,6 +51135,7 @@
false
false
false
+ 0
5682
@@ -45462,6 +51144,7 @@
false
false
false
+ 0
5683
@@ -45470,6 +51153,7 @@
false
false
false
+ 0
5684
@@ -45478,6 +51162,7 @@
false
false
false
+ 0
5685
@@ -45486,6 +51171,7 @@
false
false
false
+ 0
5686
@@ -45494,6 +51180,7 @@
false
false
false
+ 0
5687
@@ -45502,6 +51189,7 @@
false
false
false
+ 0
5688
@@ -45510,6 +51198,7 @@
false
false
false
+ 0
5689
@@ -45518,6 +51207,7 @@
false
false
false
+ 0
5690
@@ -45526,6 +51216,7 @@
false
false
false
+ 0
5691
@@ -45534,6 +51225,7 @@
false
false
false
+ 0
5692
@@ -45542,6 +51234,7 @@
false
false
false
+ 0
5693
@@ -45550,6 +51243,7 @@
false
false
false
+ 0
5694
@@ -45558,6 +51252,7 @@
false
false
false
+ 0
5695
@@ -45566,6 +51261,7 @@
false
false
false
+ 0
5696
@@ -45574,6 +51270,7 @@
false
false
false
+ 0
5697
@@ -45582,6 +51279,7 @@
false
false
false
+ 0
5698
@@ -45590,6 +51288,7 @@
false
false
false
+ 0
5699
@@ -45598,6 +51297,7 @@
false
false
false
+ 0
5700
@@ -45606,6 +51306,7 @@
false
false
false
+ 0
5701
@@ -45614,6 +51315,7 @@
false
false
false
+ 0
5702
@@ -45622,6 +51324,7 @@
false
false
false
+ 0
5703
@@ -45630,6 +51333,7 @@
false
false
false
+ 0
5704
@@ -45638,6 +51342,7 @@
false
false
false
+ 0
5705
@@ -45646,6 +51351,7 @@
false
false
false
+ 0
5706
@@ -45654,6 +51360,7 @@
false
false
false
+ 0
5707
@@ -45662,6 +51369,7 @@
false
false
false
+ 0
5708
@@ -45670,6 +51378,7 @@
false
false
false
+ 0
5709
@@ -45678,6 +51387,7 @@
false
false
false
+ 0
5710
@@ -45686,6 +51396,7 @@
false
false
false
+ 0
5711
@@ -45694,6 +51405,7 @@
false
false
false
+ 0
5712
@@ -45702,6 +51414,7 @@
false
false
false
+ 0
5713
@@ -45710,6 +51423,7 @@
false
false
false
+ 0
5714
@@ -45718,6 +51432,7 @@
false
false
false
+ 0
5715
@@ -45726,6 +51441,7 @@
false
false
false
+ 0
5716
@@ -45734,6 +51450,7 @@
false
false
false
+ 0
5717
@@ -45742,6 +51459,7 @@
false
false
false
+ 0
5718
@@ -45750,6 +51468,7 @@
false
false
false
+ 0
5719
@@ -45758,6 +51477,7 @@
false
false
false
+ 0
5720
@@ -45766,6 +51486,7 @@
false
false
false
+ 0
5721
@@ -45774,6 +51495,7 @@
false
false
false
+ 0
5722
@@ -45782,6 +51504,7 @@
false
false
false
+ 0
5723
@@ -45790,6 +51513,7 @@
false
false
false
+ 0
5724
@@ -45798,6 +51522,7 @@
false
false
false
+ 0
5725
@@ -45806,6 +51531,7 @@
false
false
false
+ 0
5726
@@ -45814,6 +51540,7 @@
false
false
false
+ 0
5727
@@ -45822,6 +51549,7 @@
false
false
false
+ 0
5728
@@ -45830,6 +51558,7 @@
false
false
false
+ 0
5729
@@ -45838,6 +51567,7 @@
false
false
false
+ 0
5730
@@ -45846,6 +51576,7 @@
false
false
false
+ 0
5731
@@ -45854,6 +51585,7 @@
false
false
false
+ 0
5732
@@ -45862,6 +51594,7 @@
false
false
false
+ 0
5733
@@ -45870,6 +51603,7 @@
false
false
false
+ 0
5734
@@ -45878,6 +51612,7 @@
false
false
false
+ 0
5735
@@ -45886,6 +51621,7 @@
false
false
false
+ 0
5736
@@ -45894,6 +51630,7 @@
false
false
false
+ 0
5737
@@ -45902,6 +51639,7 @@
false
false
false
+ 0
5738
@@ -45910,6 +51648,7 @@
false
false
false
+ 0
5739
@@ -45918,6 +51657,7 @@
false
false
false
+ 0
5740
@@ -45926,6 +51666,7 @@
false
false
false
+ 0
5741
@@ -45934,6 +51675,7 @@
false
false
false
+ 0
5742
@@ -45942,6 +51684,7 @@
false
false
false
+ 0
5743
@@ -45950,6 +51693,7 @@
false
false
false
+ 0
5744
@@ -45958,6 +51702,7 @@
false
false
false
+ 0
5745
@@ -45966,6 +51711,7 @@
false
false
false
+ 0
5746
@@ -45974,6 +51720,7 @@
false
false
false
+ 0
5747
@@ -45982,6 +51729,7 @@
false
false
false
+ 0
5748
@@ -45990,6 +51738,7 @@
false
false
false
+ 0
5749
@@ -45998,6 +51747,7 @@
false
false
false
+ 0
5750
@@ -46006,6 +51756,7 @@
false
false
false
+ 0
5751
@@ -46014,6 +51765,7 @@
false
false
false
+ 0
5752
@@ -46022,6 +51774,7 @@
false
false
false
+ 0
5753
@@ -46030,6 +51783,7 @@
false
false
false
+ 0
5754
@@ -46038,6 +51792,7 @@
false
false
false
+ 0
5755
@@ -46046,6 +51801,7 @@
false
false
false
+ 0
5756
@@ -46054,6 +51810,7 @@
false
false
false
+ 0
5757
@@ -46062,6 +51819,7 @@
false
false
false
+ 0
5758
@@ -46070,6 +51828,7 @@
false
false
false
+ 0
5759
@@ -46078,6 +51837,7 @@
false
false
false
+ 0
5760
@@ -46086,6 +51846,7 @@
false
false
false
+ 0
5761
@@ -46094,6 +51855,7 @@
false
false
false
+ 0
5762
@@ -46102,6 +51864,7 @@
false
false
false
+ 0
5763
@@ -46110,6 +51873,7 @@
false
false
false
+ 0
5764
@@ -46118,6 +51882,7 @@
false
false
false
+ 0
5765
@@ -46126,6 +51891,7 @@
false
false
false
+ 0
5766
@@ -46134,6 +51900,7 @@
false
false
false
+ 0
5767
@@ -46142,6 +51909,7 @@
false
false
false
+ 0
5768
@@ -46150,6 +51918,7 @@
false
false
false
+ 0
5769
@@ -46158,6 +51927,7 @@
false
false
false
+ 0
5770
@@ -46166,6 +51936,7 @@
false
false
false
+ 0
5771
@@ -46174,6 +51945,7 @@
false
false
false
+ 0
5772
@@ -46182,6 +51954,7 @@
false
false
false
+ 0
5773
@@ -46190,6 +51963,7 @@
false
false
false
+ 0
5774
@@ -46198,6 +51972,7 @@
false
false
false
+ 0
5775
@@ -46206,6 +51981,7 @@
false
false
false
+ 0
5776
@@ -46214,6 +51990,7 @@
false
false
false
+ 0
5777
@@ -46222,6 +51999,7 @@
false
false
false
+ 0
5778
@@ -46230,6 +52008,7 @@
false
false
false
+ 0
5779
@@ -46238,6 +52017,7 @@
false
false
false
+ 0
5780
@@ -46246,6 +52026,7 @@
false
false
false
+ 0
5781
@@ -46254,6 +52035,7 @@
false
false
false
+ 0
5782
@@ -46262,6 +52044,7 @@
false
false
false
+ 0
5783
@@ -46270,6 +52053,7 @@
false
false
false
+ 0
5784
@@ -46278,6 +52062,7 @@
false
false
false
+ 0
5785
@@ -46286,6 +52071,7 @@
false
false
false
+ 0
5786
@@ -46294,6 +52080,7 @@
false
false
false
+ 0
5787
@@ -46302,6 +52089,7 @@
false
false
false
+ 0
5788
@@ -46310,6 +52098,7 @@
false
false
false
+ 0
5789
@@ -46318,6 +52107,7 @@
false
false
false
+ 0
5790
@@ -46326,6 +52116,7 @@
false
false
false
+ 0
5791
@@ -46334,6 +52125,7 @@
false
false
false
+ 0
5792
@@ -46342,6 +52134,7 @@
false
false
false
+ 0
5793
@@ -46350,6 +52143,7 @@
false
false
false
+ 0
5794
@@ -46358,6 +52152,7 @@
false
false
false
+ 0
5795
@@ -46366,6 +52161,7 @@
false
false
false
+ 0
5796
@@ -46374,6 +52170,7 @@
false
false
false
+ 0
5797
@@ -46382,6 +52179,7 @@
false
false
false
+ 0
5798
@@ -46390,6 +52188,7 @@
false
false
false
+ 0
5799
@@ -46398,6 +52197,7 @@
false
false
false
+ 0
5800
@@ -46406,6 +52206,7 @@
false
false
false
+ 0
5801
@@ -46414,6 +52215,7 @@
false
false
false
+ 0
5802
@@ -46422,6 +52224,7 @@
false
false
false
+ 0
5803
@@ -46430,6 +52233,7 @@
false
false
false
+ 0
5804
@@ -46438,6 +52242,7 @@
false
false
false
+ 0
5805
@@ -46446,6 +52251,7 @@
false
false
false
+ 0
5806
@@ -46454,6 +52260,7 @@
false
false
false
+ 0
5807
@@ -46462,6 +52269,7 @@
false
false
false
+ 0
5808
@@ -46470,6 +52278,7 @@
false
false
false
+ 0
5809
@@ -46478,6 +52287,7 @@
false
false
false
+ 0
5810
@@ -46486,6 +52296,7 @@
false
false
false
+ 0
5811
@@ -46494,6 +52305,7 @@
false
false
false
+ 0
5812
@@ -46502,6 +52314,7 @@
false
false
false
+ 0
5813
@@ -46510,6 +52323,7 @@
false
false
false
+ 0
5814
@@ -46518,6 +52332,7 @@
false
false
false
+ 0
5815
@@ -46526,6 +52341,7 @@
false
false
false
+ 0
5816
@@ -46534,6 +52350,7 @@
false
false
false
+ 0
5817
@@ -46542,6 +52359,7 @@
false
false
false
+ 0
5818
@@ -46550,6 +52368,7 @@
false
false
false
+ 0
5819
@@ -46558,6 +52377,7 @@
false
false
false
+ 0
5820
@@ -46566,6 +52386,7 @@
false
false
false
+ 0
5821
@@ -46574,6 +52395,7 @@
false
false
false
+ 0
5822
@@ -46582,6 +52404,7 @@
false
false
false
+ 0
5823
@@ -46590,6 +52413,7 @@
false
false
false
+ 0
5824
@@ -46598,6 +52422,7 @@
false
false
false
+ 0
5825
@@ -46606,6 +52431,7 @@
false
false
false
+ 0
5826
@@ -46614,6 +52440,7 @@
false
false
false
+ 0
5827
@@ -46622,6 +52449,7 @@
false
false
false
+ 0
5828
@@ -46630,6 +52458,7 @@
false
false
false
+ 0
5829
@@ -46638,6 +52467,7 @@
false
false
false
+ 0
5830
@@ -46646,6 +52476,7 @@
false
false
false
+ 0
5831
@@ -46654,6 +52485,7 @@
false
false
false
+ 0
5832
@@ -46662,6 +52494,7 @@
false
false
false
+ 0
5833
@@ -46670,6 +52503,7 @@
false
false
false
+ 0
5834
@@ -46678,6 +52512,7 @@
false
false
false
+ 0
5835
@@ -46686,6 +52521,7 @@
false
false
false
+ 0
5836
@@ -46694,6 +52530,7 @@
false
false
false
+ 0
5837
@@ -46702,6 +52539,7 @@
false
false
false
+ 0
5838
@@ -46710,6 +52548,7 @@
false
false
false
+ 0
5839
@@ -46718,6 +52557,7 @@
false
false
false
+ 0
5840
@@ -46726,6 +52566,7 @@
false
false
false
+ 0
5841
@@ -46734,6 +52575,7 @@
false
false
false
+ 0
5842
@@ -46742,6 +52584,7 @@
false
false
false
+ 0
5843
@@ -46750,6 +52593,7 @@
false
false
false
+ 0
5844
@@ -46758,6 +52602,7 @@
false
false
false
+ 0
5845
@@ -46766,6 +52611,7 @@
false
false
false
+ 0
5846
@@ -46774,6 +52620,7 @@
false
false
false
+ 0
5847
@@ -46782,6 +52629,7 @@
false
false
false
+ 0
5848
@@ -46790,6 +52638,7 @@
false
false
false
+ 0
5849
@@ -46798,6 +52647,7 @@
false
false
false
+ 0
5850
@@ -46806,6 +52656,7 @@
false
false
false
+ 0
5851
@@ -46814,6 +52665,7 @@
false
false
false
+ 0
5852
@@ -46822,6 +52674,7 @@
false
false
false
+ 0
5853
@@ -46830,6 +52683,7 @@
false
false
false
+ 0
5854
@@ -46838,6 +52692,7 @@
false
false
false
+ 0
5855
@@ -46846,6 +52701,7 @@
false
false
false
+ 0
5856
@@ -46854,6 +52710,7 @@
false
false
false
+ 0
5857
@@ -46862,6 +52719,7 @@
false
false
false
+ 0
5858
@@ -46870,6 +52728,7 @@
false
false
false
+ 0
5859
@@ -46878,6 +52737,7 @@
false
false
false
+ 0
5860
@@ -46886,6 +52746,7 @@
false
false
false
+ 0
5861
@@ -46894,6 +52755,7 @@
false
false
false
+ 0
5862
@@ -46902,6 +52764,7 @@
false
false
false
+ 0
5863
@@ -46910,6 +52773,7 @@
false
false
false
+ 0
5864
@@ -46918,6 +52782,7 @@
false
false
false
+ 0
5865
@@ -46926,6 +52791,7 @@
false
false
false
+ 0
5866
@@ -46934,6 +52800,7 @@
false
false
false
+ 0
5867
@@ -46942,6 +52809,7 @@
false
false
false
+ 0
5868
@@ -46950,6 +52818,7 @@
false
false
false
+ 0
5869
@@ -46958,6 +52827,7 @@
false
false
false
+ 0
5870
@@ -46966,6 +52836,7 @@
false
false
false
+ 0
5871
@@ -46974,6 +52845,7 @@
false
false
false
+ 0
5872
@@ -46982,6 +52854,7 @@
false
false
false
+ 0
5873
@@ -46990,6 +52863,7 @@
false
false
false
+ 0
5874
@@ -46998,6 +52872,7 @@
false
false
false
+ 0
5875
@@ -47006,6 +52881,7 @@
false
false
false
+ 0
5876
@@ -47014,6 +52890,7 @@
false
false
false
+ 0
5877
@@ -47022,6 +52899,7 @@
false
false
false
+ 0
5878
@@ -47030,6 +52908,7 @@
false
false
false
+ 0
5879
@@ -47038,6 +52917,7 @@
false
false
false
+ 0
5880
@@ -47046,6 +52926,7 @@
false
false
false
+ 0
5881
@@ -47054,6 +52935,7 @@
false
false
false
+ 0
5882
@@ -47062,6 +52944,7 @@
false
false
false
+ 0
5883
@@ -47070,6 +52953,7 @@
false
false
false
+ 0
5884
@@ -47078,6 +52962,7 @@
false
false
false
+ 0
5885
@@ -47086,6 +52971,7 @@
false
false
false
+ 0
5886
@@ -47094,6 +52980,7 @@
false
false
false
+ 0
5887
@@ -47102,6 +52989,7 @@
false
false
false
+ 0
5888
@@ -47110,6 +52998,7 @@
false
false
false
+ 0
5889
@@ -47118,6 +53007,7 @@
false
false
false
+ 0
5890
@@ -47126,6 +53016,7 @@
false
false
false
+ 0
5891
@@ -47134,6 +53025,7 @@
false
false
false
+ 0
5892
@@ -47142,6 +53034,7 @@
false
false
false
+ 0
5893
@@ -47150,6 +53043,7 @@
false
false
false
+ 0
5894
@@ -47158,6 +53052,7 @@
false
false
false
+ 0
5895
@@ -47166,6 +53061,7 @@
false
false
false
+ 0
5896
@@ -47174,6 +53070,7 @@
false
false
false
+ 0
5897
@@ -47182,6 +53079,7 @@
false
false
false
+ 0
5898
@@ -47190,6 +53088,7 @@
false
false
false
+ 0
5899
@@ -47198,6 +53097,7 @@
false
false
false
+ 0
5900
@@ -47206,6 +53106,7 @@
false
false
false
+ 0
5901
@@ -47214,6 +53115,7 @@
false
false
false
+ 0
5902
@@ -47222,6 +53124,7 @@
false
false
false
+ 0
5903
@@ -47230,6 +53133,7 @@
false
false
false
+ 0
5904
@@ -47238,6 +53142,7 @@
false
false
false
+ 0
5905
@@ -47246,6 +53151,7 @@
false
false
false
+ 0
5906
@@ -47254,6 +53160,7 @@
false
false
false
+ 0
5907
@@ -47262,6 +53169,7 @@
false
false
false
+ 0
5908
@@ -47270,6 +53178,7 @@
false
false
false
+ 0
5909
@@ -47278,6 +53187,7 @@
false
false
false
+ 0
5910
@@ -47286,6 +53196,7 @@
false
false
false
+ 0
5911
@@ -47294,6 +53205,7 @@
false
false
false
+ 0
5912
@@ -47302,6 +53214,7 @@
false
false
false
+ 0
5913
@@ -47310,6 +53223,7 @@
false
false
false
+ 0
5914
@@ -47318,6 +53232,7 @@
false
false
false
+ 0
5915
@@ -47326,6 +53241,7 @@
false
false
false
+ 0
5916
@@ -47334,6 +53250,7 @@
false
false
false
+ 0
5917
@@ -47342,6 +53259,7 @@
false
false
false
+ 0
5918
@@ -47350,6 +53268,7 @@
false
false
false
+ 0
5919
@@ -47358,6 +53277,7 @@
false
false
false
+ 0
5920
@@ -47366,6 +53286,7 @@
false
false
false
+ 0
5921
@@ -47374,6 +53295,7 @@
false
false
false
+ 0
5922
@@ -47382,6 +53304,7 @@
false
false
false
+ 0
5923
@@ -47390,6 +53313,7 @@
false
false
false
+ 0
5924
@@ -47398,6 +53322,7 @@
false
false
false
+ 0
5925
@@ -47406,6 +53331,7 @@
false
false
false
+ 0
5926
@@ -47414,6 +53340,7 @@
false
false
false
+ 0
5927
@@ -47422,6 +53349,7 @@
false
false
false
+ 0
5928
@@ -47430,6 +53358,7 @@
false
false
false
+ 0
5929
@@ -47438,6 +53367,7 @@
false
false
false
+ 0
5930
@@ -47446,6 +53376,7 @@
false
false
false
+ 0
5931
@@ -47454,6 +53385,7 @@
false
false
false
+ 0
5932
@@ -47462,6 +53394,7 @@
false
false
false
+ 0
5933
@@ -47470,6 +53403,7 @@
false
false
false
+ 0
5934
@@ -47478,6 +53412,7 @@
false
false
false
+ 0
5935
@@ -47486,6 +53421,7 @@
false
false
false
+ 0
5936
@@ -47494,6 +53430,7 @@
false
false
false
+ 0
5937
@@ -47502,6 +53439,7 @@
false
false
false
+ 0
5938
@@ -47510,6 +53448,7 @@
false
false
false
+ 0
5939
@@ -47518,6 +53457,7 @@
false
false
false
+ 0
5940
@@ -47526,6 +53466,7 @@
false
false
false
+ 0
5941
@@ -47534,6 +53475,7 @@
false
false
false
+ 0
5942
@@ -47542,6 +53484,7 @@
false
false
false
+ 0
5943
@@ -47550,6 +53493,7 @@
false
false
false
+ 0
5944
@@ -47558,6 +53502,7 @@
false
false
false
+ 0
5945
@@ -47566,6 +53511,7 @@
false
false
false
+ 0
5946
@@ -47574,6 +53520,7 @@
false
false
false
+ 0
5947
@@ -47582,6 +53529,7 @@
false
false
false
+ 0
5948
@@ -47590,6 +53538,7 @@
false
false
false
+ 0
5949
@@ -47598,6 +53547,7 @@
false
false
false
+ 0
5950
@@ -47606,6 +53556,7 @@
false
false
false
+ 0
5951
@@ -47614,6 +53565,7 @@
false
false
false
+ 0
5952
@@ -47622,6 +53574,7 @@
false
false
false
+ 0
5953
@@ -47630,6 +53583,7 @@
false
false
false
+ 0
5954
@@ -47638,6 +53592,7 @@
false
false
false
+ 0
5955
@@ -47646,6 +53601,7 @@
false
false
false
+ 0
5956
@@ -47654,6 +53610,7 @@
false
false
false
+ 0
5957
@@ -47662,6 +53619,7 @@
false
false
false
+ 0
5958
@@ -47670,6 +53628,7 @@
false
false
false
+ 0
5959
@@ -47678,6 +53637,7 @@
false
false
false
+ 0
5960
@@ -47686,6 +53646,7 @@
false
false
false
+ 0
5961
@@ -47694,6 +53655,7 @@
false
false
false
+ 0
5962
@@ -47702,6 +53664,7 @@
false
false
false
+ 0
5963
@@ -47710,6 +53673,7 @@
false
false
false
+ 0
5964
@@ -47718,6 +53682,7 @@
false
false
false
+ 0
5965
@@ -47726,6 +53691,7 @@
false
false
false
+ 0
5966
@@ -47734,6 +53700,7 @@
false
false
false
+ 0
5967
@@ -47742,6 +53709,7 @@
false
false
false
+ 0
5968
@@ -47750,6 +53718,7 @@
false
false
false
+ 0
5969
@@ -47758,6 +53727,7 @@
false
false
false
+ 0
5970
@@ -47766,6 +53736,7 @@
false
false
false
+ 0
5971
@@ -47774,6 +53745,7 @@
false
false
false
+ 0
5972
@@ -47782,6 +53754,7 @@
false
false
false
+ 0
5973
@@ -47790,6 +53763,7 @@
false
false
false
+ 0
5974
@@ -47798,6 +53772,7 @@
false
false
false
+ 0
5975
@@ -47806,6 +53781,7 @@
false
false
false
+ 0
5976
@@ -47814,6 +53790,7 @@
false
false
false
+ 0
5977
@@ -47822,6 +53799,7 @@
false
false
false
+ 0
5978
@@ -47830,6 +53808,7 @@
false
false
false
+ 0
5979
@@ -47838,6 +53817,7 @@
false
false
false
+ 0
5980
@@ -47846,6 +53826,7 @@
false
false
false
+ 0
5981
@@ -47854,6 +53835,7 @@
false
false
false
+ 0
5982
@@ -47862,6 +53844,7 @@
false
false
false
+ 0
5983
@@ -47870,6 +53853,7 @@
false
false
false
+ 0
5984
@@ -47878,6 +53862,7 @@
false
false
false
+ 0
5985
@@ -47886,6 +53871,7 @@
false
false
false
+ 0
5986
@@ -47894,6 +53880,7 @@
false
false
false
+ 0
5987
@@ -47902,6 +53889,7 @@
false
false
false
+ 0
5988
@@ -47910,6 +53898,7 @@
false
false
false
+ 0
5989
@@ -47918,6 +53907,7 @@
false
false
false
+ 0
5990
@@ -47926,6 +53916,7 @@
false
false
false
+ 0
5991
@@ -47934,6 +53925,7 @@
false
false
false
+ 0
5992
@@ -47942,6 +53934,7 @@
false
false
false
+ 0
5993
@@ -47950,6 +53943,7 @@
false
false
false
+ 0
5994
@@ -47958,6 +53952,7 @@
false
false
false
+ 0
5995
@@ -47966,6 +53961,7 @@
false
false
false
+ 0
5996
@@ -47974,6 +53970,7 @@
false
false
false
+ 0
5997
@@ -47982,6 +53979,7 @@
false
false
false
+ 0
5998
@@ -47990,6 +53988,7 @@
false
false
false
+ 0
5999
@@ -47998,6 +53997,7 @@
false
false
false
+ 0
6000
@@ -48006,6 +54006,7 @@
false
false
false
+ 0
6001
@@ -48014,6 +54015,7 @@
false
false
false
+ 0
6002
@@ -48022,6 +54024,7 @@
false
false
false
+ 0
6003
@@ -48030,6 +54033,7 @@
false
false
false
+ 0
6004
@@ -48038,6 +54042,7 @@
false
false
false
+ 0
6005
@@ -48046,6 +54051,7 @@
false
false
false
+ 0
6006
@@ -48054,6 +54060,7 @@
false
false
false
+ 0
6007
@@ -48062,6 +54069,7 @@
false
false
false
+ 0
6008
@@ -48070,6 +54078,7 @@
false
false
false
+ 0
6009
@@ -48078,6 +54087,7 @@
false
false
false
+ 0
6010
@@ -48086,6 +54096,7 @@
false
false
false
+ 0
6011
@@ -48094,6 +54105,7 @@
false
false
false
+ 0
6012
@@ -48102,6 +54114,7 @@
false
false
false
+ 0
6013
@@ -48110,6 +54123,7 @@
false
false
false
+ 0
6014
@@ -48118,6 +54132,7 @@
false
false
false
+ 0
6015
@@ -48126,6 +54141,7 @@
false
false
false
+ 0
6016
@@ -48134,6 +54150,7 @@
false
false
false
+ 0
6017
@@ -48142,6 +54159,7 @@
false
false
false
+ 0
6018
@@ -48150,6 +54168,7 @@
false
false
false
+ 0
6019
@@ -48158,6 +54177,7 @@
false
false
false
+ 0
6020
@@ -48166,6 +54186,7 @@
false
false
false
+ 0
6021
@@ -48174,6 +54195,7 @@
false
false
false
+ 0
6022
@@ -48182,6 +54204,7 @@
false
false
false
+ 0
6023
@@ -48190,6 +54213,7 @@
false
false
false
+ 0
6024
@@ -48198,6 +54222,7 @@
false
false
false
+ 0
6025
@@ -48206,6 +54231,7 @@
false
false
false
+ 0
6026
@@ -48214,6 +54240,7 @@
false
false
false
+ 0
6027
@@ -48222,6 +54249,7 @@
false
false
false
+ 0
6028
@@ -48230,6 +54258,7 @@
false
false
false
+ 0
6029
@@ -48238,6 +54267,7 @@
false
false
false
+ 0
6030
@@ -48246,6 +54276,7 @@
false
false
false
+ 0
6031
@@ -48254,6 +54285,7 @@
false
false
false
+ 0
6032
@@ -48262,6 +54294,7 @@
false
false
false
+ 0
6033
@@ -48270,6 +54303,7 @@
false
false
false
+ 0
6034
@@ -48278,6 +54312,7 @@
false
false
false
+ 0
6035
@@ -48286,6 +54321,7 @@
false
false
false
+ 0
6036
@@ -48294,6 +54330,7 @@
false
false
false
+ 0
6037
@@ -48302,6 +54339,7 @@
false
false
false
+ 0
6038
@@ -48310,6 +54348,7 @@
false
false
false
+ 0
6039
@@ -48318,6 +54357,7 @@
false
false
false
+ 0
6040
@@ -48326,6 +54366,7 @@
false
false
false
+ 0
6041
@@ -48334,6 +54375,7 @@
false
false
false
+ 0
6042
@@ -48342,6 +54384,7 @@
false
false
false
+ 0
6043
@@ -48350,6 +54393,7 @@
false
false
false
+ 0
6044
@@ -48358,6 +54402,7 @@
false
false
false
+ 0
6045
@@ -48366,6 +54411,7 @@
false
false
false
+ 0
6046
@@ -48374,6 +54420,7 @@
false
false
false
+ 0
6047
@@ -48382,6 +54429,7 @@
false
false
false
+ 0
6048
@@ -48390,6 +54438,7 @@
false
false
false
+ 0
6049
@@ -48398,6 +54447,7 @@
false
false
false
+ 0
6050
@@ -48406,6 +54456,7 @@
false
false
false
+ 0
6051
@@ -48414,6 +54465,7 @@
false
false
false
+ 0
6052
@@ -48422,6 +54474,7 @@
false
false
false
+ 0
6053
@@ -48430,6 +54483,7 @@
false
false
false
+ 0
6054
@@ -48438,6 +54492,7 @@
false
false
false
+ 0
6055
@@ -48446,6 +54501,7 @@
false
false
false
+ 0
6056
@@ -48454,6 +54510,7 @@
false
false
false
+ 0
6057
@@ -48462,6 +54519,7 @@
false
false
false
+ 0
6058
@@ -48470,6 +54528,7 @@
false
false
false
+ 0
6059
@@ -48478,6 +54537,7 @@
false
false
false
+ 0
6060
@@ -48486,6 +54546,7 @@
false
false
false
+ 0
6061
@@ -48494,6 +54555,7 @@
false
false
false
+ 0
6062
@@ -48502,6 +54564,7 @@
false
false
false
+ 0
6063
@@ -48510,6 +54573,7 @@
false
false
false
+ 0
6064
@@ -48518,6 +54582,7 @@
false
false
false
+ 0
6065
@@ -48526,6 +54591,7 @@
false
false
false
+ 0
6066
@@ -48534,6 +54600,7 @@
false
false
false
+ 0
6067
@@ -48542,6 +54609,7 @@
false
false
false
+ 0
6068
@@ -48550,6 +54618,7 @@
false
false
false
+ 0
6069
@@ -48558,6 +54627,7 @@
false
false
false
+ 0
6070
@@ -48566,6 +54636,7 @@
false
false
false
+ 0
6071
@@ -48574,6 +54645,7 @@
false
false
false
+ 0
6072
@@ -48582,6 +54654,7 @@
false
false
false
+ 0
6073
@@ -48590,6 +54663,7 @@
false
false
false
+ 0
6074
@@ -48598,6 +54672,7 @@
false
false
false
+ 0
6075
@@ -48606,6 +54681,7 @@
false
false
false
+ 0
6076
@@ -48614,6 +54690,7 @@
false
false
false
+ 0
6077
@@ -48622,6 +54699,7 @@
false
false
false
+ 0
6078
@@ -48630,6 +54708,7 @@
false
false
false
+ 0
6079
@@ -48638,6 +54717,7 @@
false
false
false
+ 0
6080
@@ -48646,6 +54726,7 @@
false
false
false
+ 0
6081
@@ -48654,6 +54735,7 @@
false
false
false
+ 0
6082
@@ -48662,6 +54744,7 @@
false
false
false
+ 0
6083
@@ -48670,6 +54753,7 @@
false
false
false
+ 0
6084
@@ -48678,6 +54762,7 @@
false
false
false
+ 0
6085
@@ -48686,6 +54771,7 @@
false
false
false
+ 0
6086
@@ -48694,6 +54780,7 @@
false
false
false
+ 0
6087
@@ -48702,6 +54789,7 @@
false
false
false
+ 0
6088
@@ -48710,6 +54798,7 @@
false
false
false
+ 0
6089
@@ -48718,6 +54807,7 @@
false
false
false
+ 0
6090
@@ -48726,6 +54816,7 @@
false
false
false
+ 0
6091
@@ -48734,6 +54825,7 @@
false
false
false
+ 0
6092
@@ -48742,6 +54834,7 @@
false
false
false
+ 0
6093
@@ -48750,6 +54843,7 @@
false
false
false
+ 0
6094
@@ -48758,6 +54852,7 @@
false
false
false
+ 0
6095
@@ -48766,6 +54861,7 @@
false
false
false
+ 0
6096
@@ -48774,6 +54870,7 @@
false
false
false
+ 0
6097
@@ -48782,6 +54879,7 @@
false
false
false
+ 0
6098
@@ -48790,6 +54888,7 @@
false
false
false
+ 0
6099
@@ -48798,6 +54897,7 @@
false
false
false
+ 0
6100
@@ -48806,6 +54906,7 @@
false
false
false
+ 0
6101
@@ -48814,6 +54915,7 @@
false
false
false
+ 0
6102
@@ -48822,6 +54924,7 @@
false
false
false
+ 0
6103
@@ -48830,6 +54933,7 @@
false
false
false
+ 0
6104
@@ -48838,6 +54942,7 @@
false
false
false
+ 0
6105
@@ -48846,6 +54951,7 @@
false
false
false
+ 0
6106
@@ -48854,6 +54960,7 @@
false
false
false
+ 0
6107
@@ -48862,6 +54969,7 @@
false
false
false
+ 0
6108
@@ -48870,6 +54978,7 @@
false
false
false
+ 0
6109
@@ -48878,6 +54987,7 @@
false
false
false
+ 0
6110
@@ -48886,6 +54996,7 @@
false
false
false
+ 0
6111
@@ -48894,6 +55005,7 @@
false
false
false
+ 0
6112
@@ -48902,6 +55014,7 @@
false
false
false
+ 0
6113
@@ -48910,6 +55023,7 @@
false
false
false
+ 0
6114
@@ -48918,6 +55032,7 @@
false
false
false
+ 0
6115
@@ -48926,6 +55041,7 @@
false
false
false
+ 0
6116
@@ -48934,6 +55050,7 @@
false
false
false
+ 0
6117
@@ -48942,6 +55059,7 @@
false
false
false
+ 0
6118
@@ -48950,6 +55068,7 @@
false
false
false
+ 0
6119
@@ -48958,6 +55077,7 @@
false
false
false
+ 0
6120
@@ -48966,6 +55086,7 @@
false
false
false
+ 0
6121
@@ -48974,6 +55095,7 @@
false
false
false
+ 0
6122
@@ -48982,6 +55104,7 @@
false
false
false
+ 0
6123
@@ -48990,6 +55113,7 @@
false
false
false
+ 0
6124
@@ -48998,6 +55122,7 @@
false
false
false
+ 0
6125
@@ -49006,6 +55131,7 @@
false
false
false
+ 0
6126
@@ -49014,6 +55140,7 @@
false
false
false
+ 0
6127
@@ -49022,6 +55149,7 @@
false
false
false
+ 0
6128
@@ -49030,6 +55158,7 @@
false
false
false
+ 0
6129
@@ -49038,6 +55167,7 @@
false
false
false
+ 0
6130
@@ -49046,6 +55176,7 @@
false
false
false
+ 0
6131
@@ -49054,6 +55185,7 @@
false
false
false
+ 0
6132
@@ -49062,6 +55194,7 @@
false
false
false
+ 0
6133
@@ -49070,6 +55203,7 @@
false
false
false
+ 0
6134
@@ -49078,6 +55212,7 @@
false
false
false
+ 0
6135
@@ -49086,6 +55221,7 @@
false
false
false
+ 0
6136
@@ -49094,6 +55230,7 @@
false
false
false
+ 0
6137
@@ -49102,6 +55239,7 @@
false
false
false
+ 0
6138
@@ -49110,6 +55248,7 @@
false
false
false
+ 0
6139
@@ -49118,6 +55257,7 @@
false
false
false
+ 0
6140
@@ -49126,6 +55266,7 @@
false
false
false
+ 0
6141
@@ -49134,6 +55275,7 @@
false
false
false
+ 0
6142
@@ -49142,6 +55284,7 @@
false
false
false
+ 0
6143
@@ -49150,6 +55293,7 @@
false
false
false
+ 0
6144
@@ -49158,6 +55302,7 @@
false
false
false
+ 0
6145
@@ -49166,6 +55311,7 @@
false
false
false
+ 0
6146
@@ -49174,6 +55320,7 @@
false
false
false
+ 0
6147
@@ -49182,6 +55329,7 @@
false
false
false
+ 0
6148
@@ -49190,6 +55338,7 @@
false
false
false
+ 0
6149
@@ -49198,6 +55347,7 @@
false
false
false
+ 0
6150
@@ -49206,6 +55356,7 @@
false
false
false
+ 0
6151
@@ -49214,6 +55365,7 @@
false
false
false
+ 0
6152
@@ -49222,6 +55374,7 @@
false
false
false
+ 0
6153
@@ -49230,6 +55383,7 @@
false
false
false
+ 0
6154
@@ -49238,6 +55392,7 @@
false
false
false
+ 0
6155
@@ -49246,6 +55401,7 @@
false
false
false
+ 0
6156
@@ -49254,6 +55410,7 @@
false
false
false
+ 0
6157
@@ -49262,6 +55419,7 @@
false
false
false
+ 0
6158
@@ -49270,6 +55428,7 @@
false
false
false
+ 0
6159
@@ -49278,6 +55437,7 @@
false
false
false
+ 0
6160
@@ -49286,6 +55446,7 @@
false
false
false
+ 0
6161
@@ -49294,6 +55455,7 @@
false
false
false
+ 0
6162
@@ -49302,6 +55464,7 @@
false
false
false
+ 0
6163
@@ -49310,6 +55473,7 @@
false
false
false
+ 0
6164
@@ -49318,6 +55482,7 @@
false
false
false
+ 0
6165
@@ -49326,6 +55491,7 @@
false
false
false
+ 0
6166
@@ -49334,6 +55500,7 @@
false
false
false
+ 0
6167
@@ -49342,6 +55509,7 @@
false
false
false
+ 0
6168
@@ -49350,6 +55518,7 @@
false
false
false
+ 0
6169
@@ -49358,6 +55527,7 @@
false
false
false
+ 0
6170
@@ -49366,6 +55536,7 @@
false
false
false
+ 0
6171
@@ -49374,6 +55545,7 @@
false
false
false
+ 0
6172
@@ -49382,6 +55554,7 @@
false
false
false
+ 0
6173
@@ -49390,6 +55563,7 @@
false
false
false
+ 0
6174
@@ -49398,6 +55572,7 @@
false
false
false
+ 0
6175
@@ -49406,6 +55581,7 @@
false
false
false
+ 0
6176
@@ -49414,6 +55590,7 @@
false
false
false
+ 0
6177
@@ -49422,6 +55599,7 @@
false
false
false
+ 0
6178
@@ -49430,6 +55608,7 @@
false
false
false
+ 0
6179
@@ -49438,6 +55617,7 @@
false
false
false
+ 0
6180
@@ -49446,6 +55626,7 @@
false
false
false
+ 0
6181
@@ -49454,6 +55635,7 @@
false
false
false
+ 0
6182
@@ -49462,6 +55644,7 @@
false
false
false
+ 0
6183
@@ -49470,6 +55653,7 @@
false
false
false
+ 0
6184
@@ -49478,6 +55662,7 @@
false
false
false
+ 0
6185
@@ -49486,6 +55671,7 @@
false
false
false
+ 0
6186
@@ -49494,6 +55680,7 @@
false
false
false
+ 0
6187
@@ -49502,6 +55689,7 @@
false
false
false
+ 0
6188
@@ -49510,6 +55698,7 @@
false
false
false
+ 0
6189
@@ -49518,6 +55707,7 @@
false
false
false
+ 0
6190
@@ -49526,6 +55716,7 @@
false
false
false
+ 0
6191
@@ -49534,6 +55725,7 @@
false
false
false
+ 0
6192
@@ -49542,6 +55734,7 @@
false
false
false
+ 0
6193
@@ -49550,6 +55743,7 @@
false
false
false
+ 0
6194
@@ -49558,6 +55752,7 @@
false
false
false
+ 0
6195
@@ -49566,6 +55761,7 @@
false
false
false
+ 0
6196
@@ -49574,6 +55770,7 @@
false
false
false
+ 0
6197
@@ -49582,6 +55779,7 @@
false
false
false
+ 0
6198
@@ -49590,6 +55788,7 @@
false
false
false
+ 0
6199
@@ -49598,6 +55797,7 @@
false
false
false
+ 0
6200
@@ -49606,6 +55806,7 @@
false
false
false
+ 0
6201
@@ -49614,6 +55815,7 @@
false
false
false
+ 0
6202
@@ -49622,6 +55824,7 @@
false
false
false
+ 0
6203
@@ -49630,6 +55833,7 @@
false
false
false
+ 0
6204
@@ -49638,6 +55842,7 @@
false
false
false
+ 0
6205
@@ -49646,6 +55851,7 @@
false
false
false
+ 0
6206
@@ -49654,6 +55860,7 @@
false
false
false
+ 0
6207
@@ -49662,6 +55869,7 @@
false
false
false
+ 0
6208
@@ -49670,6 +55878,7 @@
false
false
false
+ 0
6209
@@ -49678,6 +55887,7 @@
false
false
false
+ 0
6210
@@ -49686,6 +55896,7 @@
false
false
false
+ 0
6211
@@ -49694,6 +55905,7 @@
false
false
false
+ 0
6212
@@ -49702,6 +55914,7 @@
false
false
false
+ 0
6213
@@ -49710,6 +55923,7 @@
false
false
false
+ 0
6214
@@ -49718,6 +55932,7 @@
false
false
false
+ 0
6215
@@ -49726,6 +55941,7 @@
false
false
false
+ 0
6216
@@ -49734,6 +55950,7 @@
false
false
false
+ 0
6217
@@ -49742,6 +55959,7 @@
false
false
false
+ 0
6218
@@ -49750,6 +55968,7 @@
false
false
false
+ 0
6219
@@ -49758,6 +55977,7 @@
false
false
false
+ 0
6220
@@ -49766,6 +55986,7 @@
false
false
false
+ 0
6221
@@ -49774,6 +55995,7 @@
false
false
false
+ 0
6222
@@ -49782,6 +56004,7 @@
false
false
false
+ 0
6223
@@ -49790,6 +56013,7 @@
false
false
false
+ 0
6224
@@ -49798,6 +56022,7 @@
false
false
false
+ 0
6225
@@ -49806,6 +56031,7 @@
false
false
false
+ 0
6226
@@ -49814,6 +56040,7 @@
false
false
false
+ 0
6227
@@ -49822,6 +56049,7 @@
false
false
false
+ 0
6228
@@ -49830,6 +56058,7 @@
false
false
false
+ 0
6229
@@ -49838,6 +56067,7 @@
false
false
false
+ 0
6230
@@ -49846,6 +56076,7 @@
false
false
false
+ 0
6231
@@ -49854,6 +56085,7 @@
false
false
false
+ 0
6232
@@ -49862,6 +56094,7 @@
false
false
false
+ 0
6233
@@ -49870,6 +56103,7 @@
false
false
false
+ 0
6234
@@ -49878,6 +56112,7 @@
false
false
false
+ 0
6235
@@ -49886,6 +56121,7 @@
false
false
false
+ 0
6236
@@ -49894,6 +56130,7 @@
false
false
false
+ 0
6237
@@ -49902,6 +56139,7 @@
false
false
false
+ 0
6238
@@ -49910,6 +56148,7 @@
false
false
false
+ 0
6239
@@ -49918,6 +56157,7 @@
false
false
false
+ 0
6240
@@ -49926,6 +56166,7 @@
false
false
false
+ 0
6241
@@ -49934,6 +56175,7 @@
false
false
false
+ 0
6242
@@ -49942,6 +56184,7 @@
false
false
false
+ 0
6243
@@ -49950,6 +56193,7 @@
false
false
false
+ 0
6244
@@ -49958,6 +56202,7 @@
false
false
false
+ 0
6245
@@ -49966,6 +56211,7 @@
false
false
false
+ 0
6246
@@ -49974,6 +56220,7 @@
false
false
false
+ 0
6247
@@ -49982,6 +56229,7 @@
false
false
false
+ 0
6248
@@ -49990,6 +56238,7 @@
false
false
false
+ 0
6249
@@ -49998,6 +56247,7 @@
false
false
false
+ 0
6250
@@ -50006,6 +56256,7 @@
false
false
false
+ 0
6251
@@ -50014,6 +56265,7 @@
false
false
false
+ 0
6252
@@ -50022,6 +56274,7 @@
false
false
false
+ 0
6253
@@ -50030,6 +56283,7 @@
false
false
false
+ 0
6254
@@ -50038,6 +56292,7 @@
false
false
false
+ 0
6255
@@ -50046,6 +56301,7 @@
false
false
false
+ 0
6256
@@ -50054,6 +56310,7 @@
false
false
false
+ 0
6257
@@ -50062,6 +56319,7 @@
false
false
false
+ 0
6258
@@ -50070,6 +56328,7 @@
false
false
false
+ 0
6259
@@ -50078,6 +56337,7 @@
false
false
false
+ 0
6260
@@ -50086,6 +56346,7 @@
false
false
false
+ 0
6261
@@ -50094,6 +56355,7 @@
false
false
false
+ 0
6262
@@ -50102,6 +56364,7 @@
false
false
false
+ 0
6263
@@ -50110,6 +56373,7 @@
false
false
false
+ 0
6264
@@ -50118,6 +56382,7 @@
false
false
false
+ 0
6265
@@ -50126,6 +56391,7 @@
false
false
false
+ 0
6266
@@ -50134,6 +56400,7 @@
false
false
false
+ 0
6267
@@ -50142,6 +56409,7 @@
false
false
false
+ 0
6268
@@ -50150,6 +56418,7 @@
false
false
false
+ 0
6269
@@ -50158,6 +56427,7 @@
false
false
false
+ 0
6270
@@ -50166,6 +56436,7 @@
false
false
false
+ 0
6271
@@ -50174,6 +56445,7 @@
false
false
false
+ 0
6272
@@ -50182,6 +56454,7 @@
false
false
false
+ 0
6273
@@ -50190,6 +56463,7 @@
false
false
false
+ 0
6274
@@ -50198,6 +56472,7 @@
false
false
false
+ 0
6275
@@ -50206,6 +56481,7 @@
false
false
false
+ 0
6276
@@ -50214,6 +56490,7 @@
false
false
false
+ 0
6277
@@ -50222,6 +56499,7 @@
false
false
false
+ 0
6278
@@ -50230,6 +56508,7 @@
false
false
false
+ 0
6279
@@ -50238,6 +56517,7 @@
false
false
false
+ 0
6280
@@ -50246,6 +56526,7 @@
false
false
false
+ 0
6281
@@ -50254,6 +56535,7 @@
false
false
false
+ 0
6282
@@ -50262,6 +56544,7 @@
false
false
false
+ 0
6283
@@ -50270,6 +56553,7 @@
false
false
false
+ 0
6284
@@ -50278,6 +56562,7 @@
false
false
false
+ 0
6285
@@ -50286,6 +56571,7 @@
false
false
false
+ 0
6286
@@ -50294,6 +56580,7 @@
false
false
false
+ 0
6287
@@ -50302,6 +56589,7 @@
false
false
false
+ 0
6288
@@ -50310,6 +56598,7 @@
false
false
false
+ 0
6289
@@ -50318,6 +56607,7 @@
false
false
false
+ 0
6290
@@ -50326,6 +56616,7 @@
false
false
false
+ 0
6291
@@ -50334,6 +56625,7 @@
false
false
false
+ 0
6292
@@ -50342,6 +56634,7 @@
false
false
false
+ 0
6293
@@ -50350,6 +56643,7 @@
false
false
false
+ 0
6294
@@ -50358,6 +56652,7 @@
false
false
false
+ 0
6295
@@ -50366,6 +56661,7 @@
false
false
false
+ 0
6296
@@ -50374,6 +56670,7 @@
false
false
false
+ 0
6297
@@ -50382,6 +56679,7 @@
false
false
false
+ 0
6298
@@ -50390,6 +56688,7 @@
false
false
false
+ 0
6299
@@ -50398,6 +56697,7 @@
false
false
false
+ 0
6300
@@ -50406,6 +56706,7 @@
false
false
false
+ 0
6301
@@ -50414,6 +56715,7 @@
false
false
false
+ 0
6302
@@ -50422,6 +56724,7 @@
false
false
false
+ 0
6303
@@ -50430,6 +56733,7 @@
false
false
false
+ 0
6304
@@ -50438,6 +56742,7 @@
false
false
false
+ 0
6305
@@ -50446,6 +56751,7 @@
false
false
false
+ 0
6306
@@ -50454,6 +56760,7 @@
false
false
false
+ 0
6307
@@ -50462,6 +56769,7 @@
false
false
false
+ 0
6308
@@ -50470,6 +56778,7 @@
false
false
false
+ 0
6309
@@ -50478,6 +56787,7 @@
false
false
false
+ 0
6310
@@ -50486,6 +56796,7 @@
false
false
false
+ 0
6311
@@ -50494,6 +56805,7 @@
false
false
false
+ 0
6312
@@ -50502,6 +56814,7 @@
false
false
false
+ 0
6313
@@ -50510,6 +56823,7 @@
false
false
false
+ 0
6314
@@ -50518,6 +56832,7 @@
false
false
false
+ 0
6315
@@ -50526,6 +56841,7 @@
false
false
false
+ 0
6316
@@ -50534,6 +56850,7 @@
false
false
false
+ 0
6317
@@ -50542,6 +56859,7 @@
false
false
false
+ 0
6318
@@ -50550,6 +56868,7 @@
false
false
false
+ 0
6319
@@ -50558,6 +56877,7 @@
false
false
false
+ 0
6320
@@ -50566,6 +56886,7 @@
false
false
false
+ 0
6321
@@ -50574,6 +56895,7 @@
false
false
false
+ 0
6322
@@ -50582,6 +56904,7 @@
false
false
false
+ 0
6323
@@ -50590,6 +56913,7 @@
false
false
false
+ 0
6324
@@ -50598,6 +56922,7 @@
false
false
false
+ 0
6325
@@ -50606,6 +56931,7 @@
false
false
false
+ 0
6326
@@ -50614,6 +56940,7 @@
false
false
false
+ 0
6327
@@ -50622,6 +56949,7 @@
false
false
false
+ 0
6328
@@ -50630,6 +56958,7 @@
false
false
false
+ 0
6329
@@ -50638,6 +56967,7 @@
false
false
false
+ 0
6330
@@ -50646,6 +56976,7 @@
false
false
false
+ 0
6331
@@ -50654,6 +56985,7 @@
false
false
false
+ 0
6332
@@ -50662,6 +56994,7 @@
false
false
false
+ 0
6333
@@ -50670,6 +57003,7 @@
false
false
false
+ 0
6334
@@ -50678,6 +57012,7 @@
false
false
false
+ 0
6335
@@ -50686,6 +57021,7 @@
false
false
false
+ 0
6336
@@ -50694,6 +57030,7 @@
false
false
false
+ 0
6337
@@ -50702,6 +57039,7 @@
false
false
false
+ 0
6338
@@ -50710,6 +57048,7 @@
false
false
false
+ 0
6339
@@ -50718,6 +57057,7 @@
false
false
false
+ 0
6340
@@ -50726,6 +57066,7 @@
false
false
false
+ 0
6341
@@ -50734,6 +57075,7 @@
false
false
false
+ 0
6342
@@ -50742,6 +57084,7 @@
false
false
false
+ 0
6343
@@ -50750,6 +57093,7 @@
false
false
false
+ 0
6344
@@ -50758,6 +57102,7 @@
false
false
false
+ 0
6345
@@ -50766,6 +57111,7 @@
false
false
false
+ 0
6346
@@ -50774,6 +57120,7 @@
false
false
false
+ 0
6347
@@ -50782,6 +57129,7 @@
false
false
false
+ 0
6348
@@ -50790,6 +57138,7 @@
false
false
false
+ 0
6349
@@ -50798,6 +57147,7 @@
false
false
false
+ 0
6350
@@ -50806,6 +57156,7 @@
false
false
false
+ 0
6351
@@ -50814,6 +57165,7 @@
false
false
false
+ 0
6352
@@ -50822,6 +57174,7 @@
false
false
false
+ 0
6353
@@ -50830,6 +57183,7 @@
false
false
false
+ 0
6354
@@ -50838,6 +57192,7 @@
false
false
false
+ 0
6355
@@ -50846,6 +57201,7 @@
false
false
false
+ 0
6356
@@ -50854,6 +57210,7 @@
false
false
false
+ 0
6357
@@ -50862,6 +57219,7 @@
false
false
false
+ 0
6358
@@ -50870,6 +57228,7 @@
false
false
false
+ 0
6359
@@ -50878,6 +57237,7 @@
false
false
false
+ 0
6360
@@ -50886,6 +57246,7 @@
false
false
false
+ 0
6361
@@ -50894,6 +57255,7 @@
false
false
false
+ 0
6362
@@ -50902,6 +57264,7 @@
false
false
false
+ 0
6363
@@ -50910,6 +57273,7 @@
false
false
false
+ 0
6364
@@ -50918,6 +57282,7 @@
false
false
false
+ 0
6365
@@ -50926,6 +57291,7 @@
false
false
false
+ 0
6366
@@ -50934,6 +57300,7 @@
false
false
false
+ 0
6367
@@ -50942,6 +57309,7 @@
false
false
false
+ 0
6368
@@ -50950,6 +57318,7 @@
false
false
false
+ 0
6369
@@ -50958,6 +57327,7 @@
false
false
false
+ 0
6370
@@ -50966,6 +57336,7 @@
false
false
false
+ 0
6371
@@ -50974,6 +57345,7 @@
false
false
false
+ 0
6372
@@ -50982,6 +57354,7 @@
false
false
false
+ 0
6373
@@ -50990,6 +57363,7 @@
false
false
false
+ 0
6374
@@ -50998,6 +57372,7 @@
false
false
false
+ 0
6375
@@ -51006,6 +57381,7 @@
false
false
false
+ 0
6376
@@ -51014,6 +57390,7 @@
false
false
false
+ 0
6377
@@ -51022,6 +57399,7 @@
false
false
false
+ 0
6378
@@ -51030,6 +57408,7 @@
false
false
false
+ 0
6379
@@ -51038,6 +57417,7 @@
false
false
false
+ 0
6380
@@ -51046,6 +57426,7 @@
false
false
false
+ 0
6381
@@ -51054,6 +57435,7 @@
false
false
false
+ 0
6382
@@ -51062,6 +57444,7 @@
false
false
false
+ 0
6383
@@ -51070,6 +57453,7 @@
false
false
false
+ 0
6384
@@ -51078,6 +57462,7 @@
false
false
false
+ 0
6385
@@ -51086,6 +57471,7 @@
false
false
false
+ 0
6386
@@ -51094,6 +57480,7 @@
false
false
false
+ 0
6387
@@ -51102,6 +57489,7 @@
false
false
false
+ 0
6388
@@ -51110,6 +57498,7 @@
false
false
false
+ 0
6389
@@ -51118,6 +57507,7 @@
false
false
false
+ 0
6390
@@ -51126,6 +57516,7 @@
false
false
false
+ 0
6391
@@ -51134,6 +57525,7 @@
false
false
false
+ 0
6392
@@ -51142,6 +57534,7 @@
false
false
false
+ 0
6393
@@ -51150,6 +57543,7 @@
false
false
false
+ 0
6394
@@ -51158,6 +57552,7 @@
false
false
false
+ 0
6395
@@ -51166,6 +57561,7 @@
false
false
false
+ 0
6396
@@ -51174,6 +57570,7 @@
false
false
false
+ 0
6397
@@ -51182,6 +57579,7 @@
false
false
false
+ 0
6398
@@ -51190,6 +57588,7 @@
false
false
false
+ 0
6399
@@ -51198,6 +57597,7 @@
false
false
false
+ 0
6400
@@ -51206,6 +57606,7 @@
false
false
false
+ 0
6401
@@ -51214,6 +57615,7 @@
false
false
false
+ 0
6402
@@ -51222,6 +57624,7 @@
false
false
false
+ 0
6403
@@ -51230,6 +57633,7 @@
false
false
false
+ 0
6404
@@ -51238,6 +57642,7 @@
false
false
false
+ 0
6405
@@ -51246,6 +57651,7 @@
false
false
false
+ 0
6406
@@ -51254,6 +57660,7 @@
false
false
false
+ 0
6407
@@ -51262,6 +57669,7 @@
false
false
false
+ 0
6408
@@ -51270,6 +57678,7 @@
false
false
false
+ 0
6409
@@ -51278,6 +57687,7 @@
false
false
false
+ 0
6410
@@ -51286,6 +57696,7 @@
false
false
false
+ 0
6411
@@ -51294,6 +57705,7 @@
false
false
false
+ 0
6412
@@ -51302,6 +57714,7 @@
false
false
false
+ 0
6413
@@ -51310,6 +57723,7 @@
false
false
false
+ 0
6414
@@ -51318,6 +57732,7 @@
false
false
false
+ 0
6415
@@ -51326,6 +57741,7 @@
false
false
false
+ 0
6416
@@ -51334,6 +57750,7 @@
false
false
false
+ 0
6417
@@ -51342,6 +57759,7 @@
false
false
false
+ 0
6418
@@ -51350,6 +57768,7 @@
false
false
false
+ 0
6419
@@ -51358,6 +57777,7 @@
false
false
false
+ 0
6420
@@ -51366,6 +57786,7 @@
false
false
false
+ 0
6421
@@ -51374,6 +57795,7 @@
false
false
false
+ 0
6422
@@ -51382,6 +57804,7 @@
false
false
false
+ 0
6423
@@ -51390,6 +57813,7 @@
false
false
false
+ 0
6424
@@ -51398,6 +57822,7 @@
false
false
false
+ 0
6425
@@ -51406,6 +57831,7 @@
false
false
false
+ 0
6426
@@ -51414,6 +57840,7 @@
false
false
false
+ 0
6427
@@ -51422,6 +57849,7 @@
false
false
false
+ 0
6428
@@ -51430,6 +57858,7 @@
false
false
false
+ 0
6429
@@ -51438,6 +57867,7 @@
false
false
false
+ 0
6430
@@ -51446,6 +57876,7 @@
false
false
false
+ 0
6431
@@ -51454,6 +57885,7 @@
false
false
false
+ 0
6432
@@ -51462,6 +57894,7 @@
false
false
false
+ 0
6433
@@ -51470,6 +57903,7 @@
false
false
false
+ 0
6434
@@ -51478,6 +57912,7 @@
false
false
false
+ 0
6435
@@ -51486,6 +57921,7 @@
false
false
false
+ 0
6436
@@ -51494,6 +57930,7 @@
false
false
false
+ 0
6437
@@ -51502,6 +57939,7 @@
false
false
false
+ 0
6438
@@ -51510,6 +57948,7 @@
false
false
false
+ 0
6439
@@ -51518,6 +57957,7 @@
false
false
false
+ 0
6440
@@ -51526,6 +57966,7 @@
false
false
false
+ 0
6441
@@ -51534,6 +57975,7 @@
false
false
false
+ 0
6442
@@ -51542,6 +57984,7 @@
false
false
false
+ 0
6443
@@ -51550,6 +57993,7 @@
false
false
false
+ 0
6444
@@ -51558,6 +58002,7 @@
false
false
false
+ 0
6445
@@ -51566,6 +58011,7 @@
false
false
false
+ 0
6446
@@ -51574,6 +58020,7 @@
false
false
false
+ 0
6447
@@ -51582,6 +58029,7 @@
false
false
false
+ 0
6448
@@ -51590,6 +58038,7 @@
false
false
false
+ 0
6449
@@ -51598,6 +58047,7 @@
false
false
false
+ 0
6450
@@ -51606,6 +58056,7 @@
false
false
false
+ 0
6451
@@ -51614,6 +58065,7 @@
false
false
false
+ 0
6452
@@ -51622,6 +58074,7 @@
false
false
false
+ 0
6453
@@ -51630,6 +58083,7 @@
false
false
false
+ 0
6454
@@ -51638,6 +58092,7 @@
false
false
false
+ 0
6455
@@ -51646,6 +58101,7 @@
false
false
false
+ 0
6456
@@ -51654,6 +58110,7 @@
false
false
false
+ 0
6457
@@ -51662,6 +58119,7 @@
false
false
false
+ 0
6458
@@ -51670,6 +58128,7 @@
false
false
false
+ 0
6459
@@ -51678,6 +58137,7 @@
false
false
false
+ 0
6460
@@ -51686,6 +58146,7 @@
false
false
false
+ 0
6461
@@ -51694,6 +58155,7 @@
false
false
false
+ 0
6462
@@ -51702,6 +58164,7 @@
false
false
false
+ 0
6463
@@ -51710,6 +58173,7 @@
false
false
false
+ 0
6464
@@ -51718,6 +58182,7 @@
false
false
false
+ 0
6465
@@ -51726,6 +58191,7 @@
false
false
false
+ 0
6466
@@ -51734,6 +58200,7 @@
false
false
false
+ 0
6467
@@ -51742,6 +58209,7 @@
false
false
false
+ 0
6468
@@ -51750,6 +58218,7 @@
false
false
false
+ 0
6469
@@ -51758,6 +58227,7 @@
false
false
false
+ 0
6470
@@ -51766,6 +58236,7 @@
false
false
false
+ 0
6471
@@ -51774,6 +58245,7 @@
false
false
false
+ 0
6472
@@ -51782,6 +58254,7 @@
false
false
false
+ 0
6473
@@ -51790,6 +58263,7 @@
false
false
false
+ 0
6474
@@ -51798,6 +58272,7 @@
false
false
false
+ 0
6475
@@ -51806,6 +58281,7 @@
false
false
false
+ 0
6476
@@ -51814,6 +58290,7 @@
false
false
false
+ 0
6477
@@ -51822,6 +58299,7 @@
false
false
false
+ 0
6478
@@ -51830,6 +58308,7 @@
false
false
false
+ 0
6479
@@ -51838,6 +58317,7 @@
false
false
false
+ 0
6480
@@ -51846,6 +58326,7 @@
false
false
false
+ 0
6481
@@ -51854,6 +58335,7 @@
false
false
false
+ 0
6482
@@ -51862,6 +58344,7 @@
false
false
false
+ 0
6483
@@ -51870,6 +58353,7 @@
false
false
false
+ 0
6484
@@ -51878,6 +58362,7 @@
false
false
false
+ 0
6485
@@ -51886,6 +58371,7 @@
false
false
false
+ 0
6486
@@ -51894,6 +58380,7 @@
false
false
false
+ 0
6487
@@ -51902,6 +58389,7 @@
false
false
false
+ 0
6488
@@ -51910,6 +58398,7 @@
false
false
false
+ 0
6489
@@ -51918,6 +58407,7 @@
false
false
false
+ 0
6490
@@ -51926,6 +58416,7 @@
false
false
false
+ 0
6491
@@ -51934,6 +58425,7 @@
false
false
false
+ 0
6492
@@ -51942,6 +58434,7 @@
false
false
false
+ 0
6493
@@ -51950,6 +58443,7 @@
false
false
false
+ 0
6494
@@ -51958,6 +58452,7 @@
false
false
false
+ 0
6495
@@ -51966,6 +58461,7 @@
false
false
false
+ 0
6496
@@ -51974,6 +58470,7 @@
false
false
false
+ 0
6497
@@ -51982,6 +58479,7 @@
false
false
false
+ 0
6498
@@ -51990,6 +58488,7 @@
false
false
false
+ 0
6499
@@ -51998,6 +58497,7 @@
false
false
false
+ 0
6500
@@ -52006,6 +58506,7 @@
false
false
false
+ 0
6501
@@ -52014,6 +58515,7 @@
false
false
false
+ 0
6502
@@ -52022,6 +58524,7 @@
false
false
false
+ 0
6503
@@ -52030,6 +58533,7 @@
false
false
false
+ 0
6504
@@ -52038,6 +58542,7 @@
false
false
false
+ 0
6505
@@ -52046,6 +58551,7 @@
false
false
false
+ 0
6506
@@ -52054,6 +58560,7 @@
false
false
false
+ 0
6507
@@ -52062,6 +58569,7 @@
false
false
false
+ 0
6508
@@ -52070,6 +58578,7 @@
false
false
false
+ 0
6509
@@ -52078,6 +58587,7 @@
false
false
false
+ 0
6510
@@ -52086,6 +58596,7 @@
false
false
false
+ 0
6511
@@ -52094,6 +58605,7 @@
false
false
false
+ 0
6512
@@ -52102,6 +58614,7 @@
false
false
false
+ 0
6513
@@ -52110,6 +58623,7 @@
false
false
false
+ 0
6514
@@ -52118,6 +58632,7 @@
false
false
false
+ 0
6515
@@ -52126,6 +58641,7 @@
false
false
false
+ 0
6516
@@ -52134,6 +58650,7 @@
false
false
false
+ 0
6517
@@ -52142,6 +58659,7 @@
false
false
false
+ 0
6518
@@ -52150,6 +58668,7 @@
false
false
false
+ 0
6519
@@ -52158,6 +58677,7 @@
false
false
false
+ 0
6520
@@ -52166,6 +58686,7 @@
false
false
false
+ 0
6521
@@ -52174,6 +58695,7 @@
false
false
false
+ 0
6522
@@ -52182,6 +58704,7 @@
false
false
false
+ 0
6523
@@ -52190,6 +58713,7 @@
false
false
false
+ 0
6524
@@ -52198,6 +58722,7 @@
false
false
false
+ 0
6525
@@ -52206,6 +58731,7 @@
false
false
false
+ 0
6526
@@ -52214,6 +58740,7 @@
false
false
false
+ 0
6527
@@ -52222,6 +58749,7 @@
false
false
false
+ 0
6528
@@ -52230,6 +58758,7 @@
false
false
false
+ 0
6529
@@ -52238,6 +58767,7 @@
false
false
false
+ 0
6530
@@ -52246,6 +58776,7 @@
false
false
false
+ 0
6531
@@ -52254,6 +58785,7 @@
false
false
false
+ 0
6532
@@ -52262,6 +58794,7 @@
false
false
false
+ 0
6533
@@ -52270,6 +58803,7 @@
false
false
false
+ 0
6534
@@ -52278,6 +58812,7 @@
false
false
false
+ 0
6535
@@ -52286,6 +58821,7 @@
false
false
false
+ 0
6536
@@ -52294,6 +58830,7 @@
false
false
false
+ 0
6537
@@ -52302,6 +58839,7 @@
false
false
false
+ 0
6538
@@ -52310,6 +58848,7 @@
false
false
false
+ 0
6539
@@ -52318,6 +58857,7 @@
false
false
false
+ 0
6540
@@ -52326,6 +58866,7 @@
false
false
false
+ 0
6541
@@ -52334,6 +58875,7 @@
false
false
false
+ 0
6542
@@ -52342,6 +58884,7 @@
false
false
false
+ 0
6543
@@ -52350,6 +58893,7 @@
false
false
false
+ 0
6544
@@ -52358,6 +58902,7 @@
false
false
false
+ 0
6545
@@ -52366,6 +58911,7 @@
false
false
false
+ 0
6546
@@ -52374,6 +58920,7 @@
false
false
false
+ 0
6547
@@ -52382,6 +58929,7 @@
false
false
false
+ 0
6548
@@ -52390,6 +58938,7 @@
false
false
false
+ 0
6549
@@ -52398,6 +58947,7 @@
false
false
false
+ 0
6550
@@ -52406,6 +58956,7 @@
false
false
false
+ 0
6551
@@ -52414,6 +58965,7 @@
false
false
false
+ 0
6552
@@ -52422,6 +58974,7 @@
false
false
false
+ 0
6553
@@ -52430,6 +58983,7 @@
false
false
false
+ 0
6554
@@ -52438,6 +58992,7 @@
false
false
false
+ 0
6555
@@ -52446,6 +59001,7 @@
false
false
false
+ 0
6556
@@ -52454,6 +59010,7 @@
false
false
false
+ 0
6557
@@ -52462,6 +59019,7 @@
false
false
false
+ 0
6558
@@ -52470,6 +59028,7 @@
false
false
false
+ 0
6559
@@ -52478,6 +59037,7 @@
false
false
false
+ 0
6560
@@ -52486,6 +59046,7 @@
false
false
false
+ 0
6561
@@ -52494,6 +59055,7 @@
false
false
false
+ 0
6562
@@ -52502,6 +59064,7 @@
false
false
false
+ 0
6563
@@ -52510,6 +59073,7 @@
false
false
false
+ 0
6564
@@ -52518,6 +59082,7 @@
false
false
false
+ 0
6565
@@ -52526,6 +59091,7 @@
false
false
false
+ 0
6566
@@ -52534,6 +59100,7 @@
false
false
false
+ 0
6567
@@ -52542,6 +59109,7 @@
false
false
false
+ 0
6568
@@ -52550,6 +59118,7 @@
false
false
false
+ 0
6569
@@ -52558,6 +59127,7 @@
false
false
false
+ 0
6570
@@ -52566,6 +59136,7 @@
false
false
false
+ 0
6571
@@ -52574,6 +59145,7 @@
false
false
false
+ 0
6572
@@ -52582,6 +59154,7 @@
false
false
false
+ 0
6573
@@ -52590,6 +59163,7 @@
false
false
false
+ 0
6574
@@ -52598,6 +59172,7 @@
false
false
false
+ 0
6575
@@ -52606,6 +59181,7 @@
false
false
false
+ 0
6576
@@ -52614,6 +59190,7 @@
false
false
false
+ 0
6577
@@ -52622,6 +59199,7 @@
false
false
false
+ 0
6578
@@ -52630,6 +59208,7 @@
false
false
false
+ 0
6579
@@ -52638,6 +59217,7 @@
false
false
false
+ 0
6580
@@ -52646,6 +59226,7 @@
false
false
false
+ 0
6581
@@ -52654,6 +59235,7 @@
false
false
false
+ 0
6582
@@ -52662,6 +59244,7 @@
false
false
false
+ 0
6583
@@ -52670,6 +59253,7 @@
false
false
false
+ 0
6584
@@ -52678,6 +59262,7 @@
false
false
false
+ 0
6585
@@ -52686,6 +59271,7 @@
false
false
false
+ 0
6586
@@ -52694,6 +59280,7 @@
false
false
false
+ 0
6587
@@ -52702,6 +59289,7 @@
false
false
false
+ 0
6588
@@ -52710,6 +59298,7 @@
false
false
false
+ 0
6589
@@ -52718,6 +59307,7 @@
false
false
false
+ 0
6590
@@ -52726,6 +59316,7 @@
false
false
false
+ 0
6591
@@ -52734,6 +59325,7 @@
false
false
false
+ 0
6592
@@ -52742,6 +59334,7 @@
false
false
false
+ 0
6593
@@ -52750,6 +59343,7 @@
false
false
false
+ 0
6594
@@ -52758,6 +59352,7 @@
false
false
false
+ 0
6595
@@ -52766,6 +59361,7 @@
false
false
false
+ 0
6596
@@ -52774,6 +59370,7 @@
false
false
false
+ 0
6597
@@ -52782,6 +59379,7 @@
false
false
false
+ 0
6598
@@ -52790,6 +59388,7 @@
false
false
false
+ 0
6599
@@ -52798,6 +59397,7 @@
false
false
false
+ 0
6600
@@ -52806,6 +59406,7 @@
false
false
false
+ 0
6601
@@ -52814,6 +59415,7 @@
false
false
false
+ 0
6602
@@ -52822,6 +59424,7 @@
false
false
false
+ 0
6603
@@ -52830,6 +59433,7 @@
false
false
false
+ 0
6604
@@ -52838,6 +59442,7 @@
false
false
false
+ 0
6605
@@ -52846,6 +59451,7 @@
false
false
false
+ 0
6606
@@ -52854,6 +59460,7 @@
false
false
false
+ 0
6607
@@ -52862,6 +59469,7 @@
false
false
false
+ 0
6608
@@ -52870,6 +59478,7 @@
false
false
false
+ 0
6609
@@ -52878,6 +59487,7 @@
false
false
false
+ 0
6610
@@ -52886,6 +59496,7 @@
false
false
false
+ 0
6611
@@ -52894,6 +59505,7 @@
false
false
false
+ 0
6612
@@ -52902,6 +59514,7 @@
false
false
false
+ 0
6613
@@ -52910,6 +59523,7 @@
false
false
false
+ 0
6614
@@ -52918,6 +59532,7 @@
false
false
false
+ 0
6615
@@ -52926,6 +59541,7 @@
false
false
false
+ 0
6616
@@ -52934,6 +59550,7 @@
false
false
false
+ 0
6617
@@ -52942,6 +59559,7 @@
false
false
false
+ 0
6618
@@ -52950,6 +59568,7 @@
false
false
false
+ 0
6619
@@ -52958,6 +59577,7 @@
false
false
false
+ 0
6620
@@ -52966,6 +59586,7 @@
false
false
false
+ 0
6621
@@ -52974,6 +59595,7 @@
false
false
false
+ 0
6622
@@ -52982,6 +59604,7 @@
false
false
false
+ 0
6623
@@ -52990,6 +59613,7 @@
false
false
false
+ 0
6624
@@ -52998,6 +59622,7 @@
false
false
false
+ 0
6625
@@ -53006,6 +59631,7 @@
false
false
false
+ 0
6626
@@ -53014,6 +59640,7 @@
false
false
false
+ 0
6627
@@ -53022,6 +59649,7 @@
false
false
false
+ 0
6628
@@ -53030,6 +59658,7 @@
false
false
false
+ 0
6629
@@ -53038,6 +59667,7 @@
false
false
false
+ 0
6630
@@ -53046,6 +59676,7 @@
false
false
false
+ 0
6631
@@ -53054,6 +59685,7 @@
false
false
false
+ 0
6632
@@ -53062,6 +59694,7 @@
false
false
false
+ 0
6633
@@ -53070,6 +59703,7 @@
false
false
false
+ 0
6634
@@ -53078,6 +59712,7 @@
false
false
false
+ 0
6635
@@ -53086,6 +59721,7 @@
false
false
false
+ 0
6636
@@ -53094,6 +59730,7 @@
false
false
false
+ 0
6637
@@ -53102,6 +59739,7 @@
false
false
false
+ 0
6638
@@ -53110,6 +59748,7 @@
false
false
false
+ 0
6639
@@ -53118,6 +59757,7 @@
false
false
false
+ 0
6640
@@ -53126,6 +59766,7 @@
false
false
false
+ 0
6641
@@ -53134,6 +59775,7 @@
false
false
false
+ 0
6642
@@ -53142,6 +59784,7 @@
false
false
false
+ 0
6643
@@ -53150,6 +59793,7 @@
false
false
false
+ 0
6644
@@ -53158,6 +59802,7 @@
false
false
false
+ 0
6645
@@ -53166,6 +59811,7 @@
false
false
false
+ 0
6646
@@ -53174,6 +59820,7 @@
false
false
false
+ 0
6647
@@ -53182,6 +59829,7 @@
false
false
false
+ 0
6648
@@ -53190,6 +59838,7 @@
false
false
false
+ 0
6649
@@ -53198,6 +59847,7 @@
false
false
false
+ 0
6650
@@ -53206,6 +59856,7 @@
false
false
false
+ 0
6651
@@ -53214,6 +59865,7 @@
false
false
false
+ 0
6652
@@ -53222,6 +59874,7 @@
false
false
false
+ 0
6653
@@ -53230,6 +59883,7 @@
false
false
false
+ 0
6654
@@ -53238,6 +59892,7 @@
false
false
false
+ 0
6655
@@ -53246,6 +59901,7 @@
false
false
false
+ 0
6656
@@ -53254,6 +59910,7 @@
false
false
false
+ 0
6657
@@ -53262,6 +59919,7 @@
false
false
false
+ 0
6658
@@ -53270,6 +59928,7 @@
false
false
false
+ 0
6659
@@ -53278,6 +59937,7 @@
false
false
false
+ 0
6660
@@ -53286,6 +59946,7 @@
false
false
false
+ 0
6661
@@ -53294,6 +59955,7 @@
false
false
false
+ 0
6662
@@ -53302,6 +59964,7 @@
false
false
false
+ 0
6663
@@ -53310,6 +59973,7 @@
false
false
false
+ 0
6664
@@ -53318,6 +59982,7 @@
false
false
false
+ 0
6665
@@ -53326,6 +59991,7 @@
false
false
false
+ 0
6666
@@ -53334,6 +60000,7 @@
false
false
false
+ 0
6667
@@ -53342,6 +60009,7 @@
false
false
false
+ 0
6668
@@ -53350,6 +60018,7 @@
false
false
false
+ 0
6669
@@ -53358,6 +60027,7 @@
false
false
false
+ 0
6670
@@ -53366,6 +60036,7 @@
false
false
false
+ 0
6671
@@ -53374,6 +60045,7 @@
false
false
false
+ 0
6672
@@ -53382,6 +60054,7 @@
false
false
false
+ 0
6673
@@ -53390,6 +60063,7 @@
false
false
false
+ 0
6674
@@ -53398,6 +60072,7 @@
false
false
false
+ 0
6675
@@ -53406,6 +60081,7 @@
false
false
false
+ 0
6676
@@ -53414,6 +60090,7 @@
false
false
false
+ 0
6677
@@ -53422,6 +60099,7 @@
false
false
false
+ 0
6678
@@ -53430,6 +60108,7 @@
false
false
false
+ 0
6679
@@ -53438,6 +60117,7 @@
false
false
false
+ 0
6680
@@ -53446,6 +60126,7 @@
false
false
false
+ 0
6681
@@ -53454,6 +60135,7 @@
false
false
false
+ 0
6682
@@ -53462,6 +60144,7 @@
false
false
false
+ 0
6683
@@ -53470,6 +60153,7 @@
false
false
false
+ 0
6684
@@ -53478,6 +60162,7 @@
false
false
false
+ 0
6685
@@ -53486,6 +60171,7 @@
false
false
false
+ 0
6686
@@ -53494,6 +60180,7 @@
false
false
false
+ 0
6687
@@ -53502,6 +60189,7 @@
false
false
false
+ 0
6688
@@ -53510,6 +60198,7 @@
false
false
false
+ 0
6689
@@ -53518,6 +60207,7 @@
false
false
false
+ 0
6690
@@ -53526,6 +60216,7 @@
false
false
false
+ 0
6691
@@ -53534,6 +60225,7 @@
false
false
false
+ 0
6692
@@ -53542,6 +60234,7 @@
false
false
false
+ 0
6693
@@ -53550,6 +60243,7 @@
false
false
false
+ 0
6694
@@ -53558,6 +60252,7 @@
false
false
false
+ 0
6695
@@ -53566,6 +60261,7 @@
false
false
false
+ 0
6696
@@ -53574,6 +60270,7 @@
false
false
false
+ 0
6697
@@ -53582,6 +60279,7 @@
false
false
false
+ 0
6698
@@ -53590,6 +60288,7 @@
false
false
false
+ 0
6699
@@ -53598,6 +60297,7 @@
false
false
false
+ 0
6700
@@ -53606,6 +60306,7 @@
false
false
false
+ 0
6701
@@ -53614,6 +60315,7 @@
false
false
false
+ 0
6702
@@ -53622,6 +60324,7 @@
false
false
false
+ 0
6703
@@ -53630,6 +60333,7 @@
false
false
false
+ 0
6704
@@ -53638,6 +60342,7 @@
false
false
false
+ 0
6705
@@ -53646,6 +60351,7 @@
false
false
false
+ 0
6706
@@ -53654,6 +60360,7 @@
false
false
false
+ 0
6707
@@ -53662,6 +60369,7 @@
false
false
false
+ 0
6708
@@ -53670,6 +60378,7 @@
false
false
false
+ 0
6709
@@ -53678,6 +60387,7 @@
false
false
false
+ 0
6710
@@ -53686,6 +60396,7 @@
false
false
false
+ 0
6711
@@ -53694,6 +60405,7 @@
false
false
false
+ 0
6712
@@ -53702,6 +60414,7 @@
false
false
false
+ 0
6713
@@ -53710,6 +60423,7 @@
false
false
false
+ 0
6714
@@ -53718,6 +60432,7 @@
false
false
false
+ 0
6715
@@ -53726,6 +60441,7 @@
false
false
false
+ 0
6716
@@ -53734,6 +60450,7 @@
false
false
false
+ 0
6717
@@ -53742,6 +60459,7 @@
false
false
false
+ 0
6718
@@ -53750,6 +60468,7 @@
false
false
false
+ 0
6719
@@ -53758,6 +60477,7 @@
false
false
false
+ 0
6720
@@ -53766,6 +60486,7 @@
false
false
false
+ 0
6721
@@ -53774,6 +60495,7 @@
false
false
false
+ 0
6722
@@ -53782,6 +60504,7 @@
false
false
false
+ 0
6723
@@ -53790,6 +60513,7 @@
false
false
false
+ 0
6724
@@ -53798,6 +60522,7 @@
false
false
false
+ 0
6725
@@ -53806,6 +60531,7 @@
false
false
false
+ 0
6726
@@ -53814,6 +60540,7 @@
false
false
false
+ 0
6727
@@ -53822,6 +60549,7 @@
false
false
false
+ 0
6728
@@ -53830,6 +60558,7 @@
false
false
false
+ 0
6729
@@ -53838,6 +60567,7 @@
false
false
false
+ 0
6730
@@ -53846,6 +60576,7 @@
false
false
false
+ 0
6731
@@ -53854,6 +60585,7 @@
false
false
false
+ 0
6732
@@ -53862,6 +60594,7 @@
false
false
false
+ 0
6733
@@ -53870,6 +60603,7 @@
false
false
false
+ 0
6734
@@ -53878,6 +60612,7 @@
false
false
false
+ 0
6735
@@ -53886,6 +60621,7 @@
false
false
false
+ 0
6736
@@ -53894,6 +60630,7 @@
false
false
false
+ 0
6737
@@ -53902,6 +60639,7 @@
false
false
false
+ 0
6738
@@ -53910,6 +60648,7 @@
false
false
false
+ 0
6739
@@ -53918,6 +60657,7 @@
false
false
false
+ 0
6740
@@ -53926,6 +60666,7 @@
false
false
false
+ 0
6741
@@ -53934,6 +60675,7 @@
false
false
false
+ 0
6742
@@ -53942,6 +60684,7 @@
false
false
false
+ 0
6743
@@ -53950,6 +60693,7 @@
false
false
false
+ 0
6744
@@ -53958,6 +60702,7 @@
false
false
false
+ 0
6745
@@ -53966,6 +60711,7 @@
false
false
false
+ 0
6746
@@ -53974,6 +60720,7 @@
false
false
false
+ 0
6747
@@ -53982,6 +60729,7 @@
false
false
false
+ 0
6748
@@ -53990,6 +60738,7 @@
false
false
false
+ 0
6749
@@ -53998,6 +60747,7 @@
false
false
false
+ 0
6750
@@ -54006,6 +60756,7 @@
false
false
false
+ 0
6751
@@ -54014,6 +60765,7 @@
false
false
false
+ 0
6752
@@ -54022,6 +60774,7 @@
false
false
false
+ 0
6753
@@ -54030,6 +60783,7 @@
false
false
false
+ 0
6754
@@ -54038,6 +60792,7 @@
false
false
false
+ 0
6755
@@ -54046,6 +60801,7 @@
false
false
false
+ 0
6756
@@ -54054,6 +60810,7 @@
false
false
false
+ 0
6757
@@ -54062,6 +60819,7 @@
false
false
false
+ 0
6758
@@ -54070,6 +60828,7 @@
false
false
false
+ 0
6759
@@ -54078,6 +60837,7 @@
false
false
false
+ 0
6760
@@ -54086,6 +60846,7 @@
false
false
false
+ 0
6761
@@ -54094,6 +60855,7 @@
false
false
false
+ 0
6762
@@ -54102,6 +60864,7 @@
false
false
false
+ 0
6763
@@ -54110,6 +60873,7 @@
false
false
false
+ 0
6764
@@ -54118,6 +60882,7 @@
false
false
false
+ 0
6765
@@ -54126,6 +60891,7 @@
false
false
false
+ 0
6766
@@ -54134,6 +60900,7 @@
false
false
false
+ 0
6767
@@ -54142,6 +60909,7 @@
false
false
false
+ 0
6768
@@ -54150,6 +60918,7 @@
false
false
false
+ 0
6769
@@ -54158,6 +60927,7 @@
false
false
false
+ 0
6770
@@ -54166,6 +60936,7 @@
false
false
false
+ 0
6771
@@ -54174,6 +60945,7 @@
false
false
false
+ 0
6772
@@ -54182,6 +60954,7 @@
false
false
false
+ 0
6773
@@ -54190,6 +60963,7 @@
false
false
false
+ 0
6774
@@ -54198,6 +60972,7 @@
false
false
false
+ 0
6775
@@ -54206,6 +60981,7 @@
false
false
false
+ 0
6776
@@ -54214,6 +60990,7 @@
false
false
false
+ 0
6777
@@ -54222,6 +60999,7 @@
false
false
false
+ 0
6778
@@ -54230,6 +61008,7 @@
false
false
false
+ 0
6779
@@ -54238,6 +61017,7 @@
false
false
false
+ 0
6780
@@ -54246,6 +61026,7 @@
false
false
false
+ 0
6781
@@ -54254,6 +61035,7 @@
false
false
false
+ 0
6782
@@ -54262,6 +61044,7 @@
false
false
false
+ 0
6783
@@ -54270,6 +61053,7 @@
false
false
false
+ 0
6784
@@ -54278,6 +61062,7 @@
false
false
false
+ 0
6785
@@ -54286,6 +61071,7 @@
false
false
false
+ 0
6786
@@ -54294,6 +61080,7 @@
false
false
false
+ 0
6787
@@ -54302,6 +61089,7 @@
false
false
false
+ 0
6788
@@ -54310,6 +61098,7 @@
false
false
false
+ 0
6789
@@ -54318,6 +61107,7 @@
false
false
false
+ 0
6790
@@ -54326,6 +61116,7 @@
false
false
false
+ 0
6791
@@ -54334,6 +61125,7 @@
false
false
false
+ 0
6792
@@ -54342,6 +61134,7 @@
false
false
false
+ 0
6793
@@ -54350,6 +61143,7 @@
false
false
false
+ 0
6794
@@ -54358,6 +61152,7 @@
false
false
false
+ 0
6795
@@ -54366,6 +61161,7 @@
false
false
false
+ 0
6796
@@ -54374,6 +61170,7 @@
false
false
false
+ 0
6797
@@ -54382,6 +61179,7 @@
false
false
false
+ 0
6798
@@ -54390,6 +61188,7 @@
false
false
false
+ 0
6799
@@ -54398,6 +61197,7 @@
false
false
false
+ 0
6800
@@ -54406,6 +61206,7 @@
false
false
false
+ 0
6801
@@ -54414,6 +61215,7 @@
false
false
false
+ 0
6802
@@ -54422,6 +61224,7 @@
false
false
false
+ 0
6803
@@ -54430,6 +61233,7 @@
false
false
false
+ 0
6804
@@ -54438,6 +61242,7 @@
false
false
false
+ 0
6805
@@ -54446,6 +61251,7 @@
false
false
false
+ 0
6806
@@ -54454,6 +61260,7 @@
false
false
false
+ 0
6807
@@ -54462,6 +61269,7 @@
false
false
false
+ 0
6808
@@ -54470,6 +61278,7 @@
false
false
false
+ 0
6809
@@ -54478,6 +61287,7 @@
false
false
false
+ 0
6810
@@ -54486,6 +61296,7 @@
false
false
false
+ 0
6811
@@ -54494,6 +61305,7 @@
false
false
false
+ 0
6812
@@ -54502,6 +61314,7 @@
false
false
false
+ 0
6813
@@ -54510,6 +61323,7 @@
false
false
false
+ 0
6814
@@ -54518,6 +61332,7 @@
false
false
false
+ 0
6815
@@ -54526,6 +61341,7 @@
false
false
false
+ 0
6816
@@ -54534,6 +61350,7 @@
false
false
false
+ 0
6817
@@ -54542,6 +61359,7 @@
false
false
false
+ 0
6818
@@ -54550,6 +61368,7 @@
false
false
false
+ 0
6819
@@ -54558,6 +61377,7 @@
false
false
false
+ 0
6820
@@ -54566,6 +61386,7 @@
false
false
false
+ 0
6821
@@ -54574,6 +61395,7 @@
false
false
false
+ 0
6822
@@ -54582,6 +61404,7 @@
false
false
false
+ 0
6823
@@ -54590,6 +61413,7 @@
false
false
false
+ 0
6824
@@ -54598,6 +61422,7 @@
false
false
false
+ 0
6825
@@ -54606,6 +61431,7 @@
false
false
false
+ 0
6826
@@ -54614,6 +61440,7 @@
false
false
false
+ 0
6827
@@ -54622,6 +61449,7 @@
false
false
false
+ 0
6828
@@ -54630,6 +61458,7 @@
false
false
false
+ 0
6829
@@ -54638,6 +61467,7 @@
false
false
false
+ 0
6830
@@ -54646,6 +61476,7 @@
false
false
false
+ 0
6831
@@ -54654,6 +61485,7 @@
false
false
false
+ 0
6832
@@ -54662,6 +61494,7 @@
false
false
false
+ 0
6833
@@ -54670,6 +61503,7 @@
false
false
false
+ 0
6834
@@ -54678,6 +61512,7 @@
false
false
false
+ 0
6835
@@ -54686,6 +61521,7 @@
false
false
false
+ 0
6836
@@ -54694,6 +61530,7 @@
false
false
false
+ 0
6837
@@ -54702,6 +61539,7 @@
false
false
false
+ 0
6838
@@ -54710,6 +61548,7 @@
false
false
false
+ 0
6839
@@ -54718,6 +61557,7 @@
false
false
false
+ 0
6840
@@ -54726,6 +61566,7 @@
false
false
false
+ 0
6841
@@ -54734,6 +61575,7 @@
false
false
false
+ 0
6842
@@ -54742,6 +61584,7 @@
false
false
false
+ 0
6843
@@ -54750,6 +61593,7 @@
false
false
false
+ 0
6844
@@ -54758,6 +61602,7 @@
false
false
false
+ 0
6845
@@ -54766,6 +61611,7 @@
false
false
false
+ 0
6846
@@ -54774,6 +61620,7 @@
false
false
false
+ 0
6847
@@ -54782,6 +61629,7 @@
false
false
false
+ 0
6848
@@ -54790,6 +61638,7 @@
false
false
false
+ 0
6849
@@ -54798,6 +61647,7 @@
false
false
false
+ 0
6850
@@ -54806,6 +61656,7 @@
false
false
false
+ 0
6851
@@ -54814,6 +61665,7 @@
false
false
false
+ 0
6852
@@ -54822,6 +61674,7 @@
false
false
false
+ 0
6853
@@ -54830,6 +61683,7 @@
false
false
false
+ 0
6854
@@ -54838,6 +61692,7 @@
false
false
false
+ 0
6855
@@ -54846,6 +61701,7 @@
false
false
false
+ 0
6856
@@ -54854,6 +61710,7 @@
false
false
false
+ 0
6857
@@ -54862,6 +61719,7 @@
false
false
false
+ 0
6858
@@ -54870,6 +61728,7 @@
false
false
false
+ 0
6859
@@ -54878,6 +61737,7 @@
false
false
false
+ 0
6860
@@ -54886,6 +61746,7 @@
false
false
false
+ 0
6861
@@ -54894,6 +61755,7 @@
false
false
false
+ 0
6862
@@ -54902,6 +61764,7 @@
false
false
false
+ 0
6863
@@ -54910,6 +61773,7 @@
false
false
false
+ 0
6864
@@ -54918,6 +61782,7 @@
false
false
false
+ 0
6865
@@ -54926,6 +61791,7 @@
false
false
false
+ 0
6866
@@ -54934,6 +61800,7 @@
false
false
false
+ 0
6867
@@ -54942,6 +61809,7 @@
false
false
false
+ 0
6868
@@ -54950,6 +61818,7 @@
false
false
false
+ 0
6869
@@ -54958,6 +61827,7 @@
false
false
false
+ 0
6870
@@ -54966,6 +61836,7 @@
false
false
false
+ 0
6871
@@ -54974,6 +61845,7 @@
false
false
false
+ 0
6872
@@ -54982,6 +61854,7 @@
false
false
false
+ 0
6873
@@ -54990,6 +61863,7 @@
false
false
false
+ 0
6874
@@ -54998,6 +61872,7 @@
false
false
false
+ 0
6875
@@ -55006,6 +61881,7 @@
false
false
false
+ 0
6876
@@ -55014,6 +61890,7 @@
false
false
false
+ 0
6877
@@ -55022,6 +61899,7 @@
false
false
false
+ 0
6878
@@ -55030,6 +61908,7 @@
false
false
false
+ 0
6879
@@ -55038,6 +61917,7 @@
false
false
false
+ 0
6880
@@ -55046,6 +61926,7 @@
false
false
false
+ 0
6881
@@ -55054,6 +61935,7 @@
false
false
false
+ 0
6882
@@ -55062,6 +61944,7 @@
false
false
false
+ 0
6883
@@ -55070,6 +61953,7 @@
false
false
false
+ 0
6884
@@ -55078,6 +61962,7 @@
false
false
false
+ 0
6885
@@ -55086,6 +61971,7 @@
false
false
false
+ 0
6886
@@ -55094,6 +61980,7 @@
false
false
false
+ 0
6887
@@ -55102,6 +61989,7 @@
false
false
false
+ 0
6888
@@ -55110,6 +61998,7 @@
false
false
false
+ 0
6889
@@ -55118,6 +62007,7 @@
false
false
false
+ 0
6890
@@ -55126,6 +62016,7 @@
false
false
false
+ 0
6891
@@ -55134,6 +62025,7 @@
false
false
false
+ 0
6892
@@ -55142,6 +62034,7 @@
false
false
false
+ 0
6893
@@ -55150,6 +62043,7 @@
false
false
false
+ 0
6894
@@ -55158,6 +62052,7 @@
false
false
false
+ 0
6895
@@ -55166,6 +62061,7 @@
false
false
false
+ 0
6896
@@ -55174,6 +62070,7 @@
false
false
false
+ 0
6897
@@ -55182,6 +62079,7 @@
false
false
false
+ 0
6898
@@ -55190,6 +62088,7 @@
false
false
false
+ 0
6899
@@ -55198,6 +62097,7 @@
false
false
false
+ 0
6900
@@ -55206,6 +62106,7 @@
false
false
false
+ 0
6901
@@ -55214,6 +62115,7 @@
false
false
false
+ 0
6902
@@ -55222,6 +62124,7 @@
false
false
false
+ 0
6903
@@ -55230,6 +62133,7 @@
false
false
false
+ 0
6904
@@ -55238,6 +62142,7 @@
false
false
false
+ 0
6905
@@ -55246,6 +62151,7 @@
false
false
false
+ 0
6906
@@ -55254,6 +62160,7 @@
false
false
false
+ 0
6907
@@ -55262,6 +62169,7 @@
false
false
false
+ 0
6908
@@ -55270,6 +62178,7 @@
false
false
false
+ 0
6909
@@ -55278,6 +62187,7 @@
false
false
false
+ 0
6910
@@ -55286,6 +62196,7 @@
false
false
false
+ 0
6911
@@ -55294,6 +62205,7 @@
false
false
false
+ 0
6912
@@ -55302,6 +62214,7 @@
false
false
false
+ 0
6913
@@ -55310,6 +62223,7 @@
false
false
false
+ 0
6914
@@ -55318,6 +62232,7 @@
false
false
false
+ 0
6915
@@ -55326,6 +62241,7 @@
false
false
false
+ 0
6916
@@ -55334,6 +62250,7 @@
false
false
false
+ 0
6917
@@ -55342,6 +62259,7 @@
false
false
false
+ 0
6918
@@ -55350,6 +62268,7 @@
false
false
false
+ 0
6919
@@ -55358,6 +62277,7 @@
false
false
false
+ 0
6920
@@ -55366,6 +62286,7 @@
false
false
false
+ 0
6921
@@ -55374,6 +62295,7 @@
false
false
false
+ 0
6922
@@ -55382,6 +62304,7 @@
false
false
false
+ 0
6923
@@ -55390,6 +62313,7 @@
false
false
false
+ 0
6924
@@ -55398,6 +62322,7 @@
false
false
false
+ 0
6925
@@ -55406,6 +62331,7 @@
false
false
false
+ 0
6926
@@ -55414,6 +62340,7 @@
false
false
false
+ 0
6927
@@ -55422,6 +62349,7 @@
false
false
false
+ 0
6928
@@ -55430,6 +62358,7 @@
false
false
false
+ 0
6929
@@ -55438,6 +62367,7 @@
false
false
false
+ 0
6930
@@ -55446,6 +62376,7 @@
false
false
false
+ 0
6931
@@ -55454,6 +62385,7 @@
false
false
false
+ 0
6932
@@ -55462,6 +62394,7 @@
false
false
false
+ 0
6933
@@ -55470,6 +62403,7 @@
false
false
false
+ 0
6934
@@ -55478,6 +62412,7 @@
false
false
false
+ 0
6935
@@ -55486,6 +62421,7 @@
false
false
false
+ 0
6936
@@ -55494,6 +62430,7 @@
false
false
false
+ 0
6937
@@ -55502,6 +62439,7 @@
false
false
false
+ 0
6938
@@ -55510,6 +62448,7 @@
false
false
false
+ 0
6939
@@ -55518,6 +62457,7 @@
false
false
false
+ 0
6940
@@ -55526,6 +62466,7 @@
false
false
false
+ 0
6941
@@ -55534,6 +62475,7 @@
false
false
false
+ 0
6942
@@ -55542,6 +62484,7 @@
false
false
false
+ 0
6943
@@ -55550,6 +62493,7 @@
false
false
false
+ 0
6944
@@ -55558,6 +62502,7 @@
false
false
false
+ 0
6945
@@ -55566,6 +62511,7 @@
false
false
false
+ 0
6946
@@ -55574,6 +62520,7 @@
false
false
false
+ 0
6947
@@ -55582,6 +62529,7 @@
false
false
false
+ 0
6948
@@ -55590,6 +62538,7 @@
false
false
false
+ 0
6949
@@ -55598,6 +62547,7 @@
false
false
false
+ 0
6950
@@ -55606,6 +62556,7 @@
false
false
false
+ 0
6951
@@ -55614,6 +62565,7 @@
false
false
false
+ 0
6952
@@ -55622,6 +62574,7 @@
false
false
false
+ 0
6953
@@ -55630,6 +62583,7 @@
false
false
false
+ 0
6954
@@ -55638,6 +62592,7 @@
false
false
false
+ 0
6955
@@ -55646,6 +62601,7 @@
false
false
false
+ 0
6956
@@ -55654,6 +62610,7 @@
false
false
false
+ 0
6957
@@ -55662,6 +62619,7 @@
false
false
false
+ 0
6958
@@ -55670,6 +62628,7 @@
false
false
false
+ 0
6959
@@ -55678,6 +62637,7 @@
false
false
false
+ 0
6960
@@ -55686,6 +62646,7 @@
false
false
false
+ 0
6961
@@ -55694,6 +62655,7 @@
false
false
false
+ 0
6962
@@ -55702,6 +62664,7 @@
false
false
false
+ 0
6963
@@ -55710,6 +62673,7 @@
false
false
false
+ 0
6964
@@ -55718,6 +62682,7 @@
false
false
false
+ 0
6965
@@ -55726,6 +62691,7 @@
false
false
false
+ 0
6966
@@ -55734,6 +62700,7 @@
false
false
false
+ 0
6967
@@ -55742,6 +62709,7 @@
false
false
false
+ 0
6968
@@ -55750,6 +62718,7 @@
false
false
false
+ 0
6969
@@ -55758,6 +62727,7 @@
false
false
false
+ 0
6970
@@ -55766,6 +62736,7 @@
false
false
false
+ 0
6971
@@ -55774,6 +62745,7 @@
false
false
false
+ 0
6972
@@ -55782,6 +62754,7 @@
false
false
false
+ 0
6973
@@ -55790,6 +62763,7 @@
false
false
false
+ 0
6974
@@ -55798,6 +62772,7 @@
false
false
false
+ 0
6975
@@ -55806,6 +62781,7 @@
false
false
false
+ 0
6976
@@ -55814,6 +62790,7 @@
false
false
false
+ 0
6977
@@ -55822,6 +62799,7 @@
false
false
false
+ 0
6978
@@ -55830,6 +62808,7 @@
false
false
false
+ 0
6979
@@ -55838,6 +62817,7 @@
false
false
false
+ 0
6980
@@ -55846,6 +62826,7 @@
false
false
false
+ 0
6981
@@ -55854,6 +62835,7 @@
false
false
false
+ 0
6982
@@ -55862,6 +62844,7 @@
false
false
false
+ 0
6983
@@ -55870,6 +62853,7 @@
false
false
false
+ 0
6984
@@ -55878,6 +62862,7 @@
false
false
false
+ 0
6985
@@ -55886,6 +62871,7 @@
false
false
false
+ 0
6986
@@ -55894,6 +62880,7 @@
false
false
false
+ 0
6987
@@ -55902,6 +62889,7 @@
false
false
false
+ 0
6988
@@ -55910,6 +62898,7 @@
false
false
false
+ 0
6989
@@ -55918,6 +62907,7 @@
false
false
false
+ 0
6990
@@ -55926,6 +62916,7 @@
false
false
false
+ 0
6991
@@ -55934,6 +62925,7 @@
false
false
false
+ 0
6992
@@ -55942,6 +62934,7 @@
false
false
false
+ 0
6993
@@ -55950,6 +62943,7 @@
false
false
false
+ 0
6994
@@ -55958,6 +62952,7 @@
false
false
false
+ 0
6995
@@ -55966,6 +62961,7 @@
false
false
false
+ 0
6996
@@ -55974,6 +62970,7 @@
false
false
false
+ 0
6997
@@ -55982,6 +62979,7 @@
false
false
false
+ 0
6998
@@ -55990,6 +62988,7 @@
false
false
false
+ 0
6999
@@ -55998,6 +62997,7 @@
false
false
false
+ 0
7000
@@ -56006,6 +63006,7 @@
false
false
false
+ 0
7001
@@ -56014,6 +63015,7 @@
false
false
false
+ 0
7002
@@ -56022,6 +63024,7 @@
false
false
false
+ 0
7003
@@ -56030,6 +63033,7 @@
false
false
false
+ 0
7004
@@ -56038,6 +63042,7 @@
false
false
false
+ 0
7005
@@ -56046,6 +63051,7 @@
false
false
false
+ 0
7006
@@ -56054,6 +63060,7 @@
false
false
false
+ 0
7007
@@ -56062,6 +63069,7 @@
false
false
false
+ 0
7008
@@ -56070,6 +63078,7 @@
false
false
false
+ 0
7009
@@ -56078,6 +63087,7 @@
false
false
false
+ 0
7010
@@ -56086,6 +63096,7 @@
false
false
false
+ 0
7011
@@ -56094,6 +63105,7 @@
false
false
false
+ 0
7012
@@ -56102,6 +63114,7 @@
false
false
false
+ 0
7013
@@ -56110,6 +63123,7 @@
false
false
false
+ 0
7014
@@ -56118,6 +63132,7 @@
false
false
false
+ 0
7015
@@ -56126,6 +63141,7 @@
false
false
false
+ 0
7016
@@ -56134,6 +63150,7 @@
false
false
false
+ 0
7017
@@ -56142,6 +63159,7 @@
false
false
false
+ 0
7018
@@ -56150,6 +63168,7 @@
false
false
false
+ 0
7019
@@ -56158,6 +63177,7 @@
false
false
false
+ 0
7020
@@ -56166,6 +63186,7 @@
false
false
false
+ 0
7021
@@ -56174,6 +63195,7 @@
false
false
false
+ 0
7022
@@ -56182,6 +63204,7 @@
false
false
false
+ 0
7023
@@ -56190,6 +63213,7 @@
false
false
false
+ 0
7024
@@ -56198,6 +63222,7 @@
false
false
false
+ 0
7025
@@ -56206,6 +63231,7 @@
false
false
false
+ 0
7026
@@ -56214,6 +63240,7 @@
false
false
false
+ 0
7027
@@ -56222,6 +63249,7 @@
false
false
false
+ 0
7028
@@ -56230,6 +63258,7 @@
false
false
false
+ 0
7029
@@ -56238,6 +63267,7 @@
false
false
false
+ 0
7030
@@ -56246,6 +63276,7 @@
false
false
false
+ 0
7031
@@ -56254,6 +63285,7 @@
false
false
false
+ 0
7032
@@ -56262,6 +63294,7 @@
false
false
false
+ 0
7033
@@ -56270,6 +63303,7 @@
false
false
false
+ 0
7034
@@ -56278,6 +63312,7 @@
false
false
false
+ 0
7035
@@ -56286,6 +63321,7 @@
false
false
false
+ 0
7036
@@ -56294,6 +63330,7 @@
false
false
false
+ 0
7037
@@ -56302,6 +63339,7 @@
false
false
false
+ 0
7038
@@ -56310,6 +63348,7 @@
false
false
false
+ 0
7039
@@ -56318,6 +63357,7 @@
false
false
false
+ 0
7040
@@ -56326,6 +63366,7 @@
false
false
false
+ 0
7041
@@ -56334,6 +63375,7 @@
false
false
false
+ 0
7042
@@ -56342,6 +63384,7 @@
false
false
false
+ 0
7043
@@ -56350,6 +63393,7 @@
false
false
false
+ 0
7044
@@ -56358,6 +63402,7 @@
false
false
false
+ 0
7045
@@ -56366,6 +63411,7 @@
false
false
false
+ 0
7046
@@ -56374,6 +63420,7 @@
false
false
false
+ 0
7047
@@ -56382,6 +63429,7 @@
false
false
false
+ 0
7048
@@ -56390,6 +63438,7 @@
false
false
false
+ 0
7049
@@ -56398,6 +63447,7 @@
false
false
false
+ 0
7050
@@ -56406,6 +63456,7 @@
false
false
false
+ 0
7051
@@ -56414,6 +63465,7 @@
false
false
false
+ 0
7052
@@ -56422,6 +63474,7 @@
false
false
false
+ 0
7053
@@ -56430,6 +63483,7 @@
false
false
false
+ 0
7054
@@ -56438,6 +63492,7 @@
false
false
false
+ 0
7055
@@ -56446,6 +63501,7 @@
false
false
false
+ 0
7056
@@ -56454,6 +63510,7 @@
false
false
false
+ 0
7057
@@ -56462,6 +63519,7 @@
false
false
false
+ 0
7058
@@ -56470,6 +63528,7 @@
false
false
false
+ 0
7059
@@ -56478,6 +63537,7 @@
false
false
false
+ 0
7060
@@ -56486,6 +63546,7 @@
false
false
false
+ 0
7061
@@ -56494,6 +63555,7 @@
false
false
false
+ 0
7062
@@ -56502,6 +63564,7 @@
false
false
false
+ 0
7063
@@ -56510,6 +63573,7 @@
false
false
false
+ 0
7064
@@ -56518,6 +63582,7 @@
false
false
false
+ 0
7065
@@ -56526,6 +63591,7 @@
false
false
false
+ 0
7066
@@ -56534,6 +63600,7 @@
false
false
false
+ 0
7067
@@ -56542,6 +63609,7 @@
false
false
false
+ 0
7068
@@ -56550,6 +63618,7 @@
false
false
false
+ 0
7069
@@ -56558,6 +63627,7 @@
false
false
false
+ 0
7070
@@ -56566,6 +63636,7 @@
false
false
false
+ 0
7071
@@ -56574,6 +63645,7 @@
false
false
false
+ 0
7072
@@ -56582,6 +63654,7 @@
false
false
false
+ 0
7073
@@ -56590,6 +63663,7 @@
false
false
false
+ 0
7074
@@ -56598,6 +63672,7 @@
false
false
false
+ 0
7075
@@ -56606,6 +63681,7 @@
false
false
false
+ 0
7076
@@ -56614,6 +63690,7 @@
false
false
false
+ 0
7077
@@ -56622,6 +63699,7 @@
false
false
false
+ 0
7078
@@ -56630,6 +63708,7 @@
false
false
false
+ 0
7079
@@ -56638,6 +63717,7 @@
false
false
false
+ 0
7080
@@ -56646,6 +63726,7 @@
false
false
false
+ 0
7081
@@ -56654,6 +63735,7 @@
false
false
false
+ 0
7082
@@ -56662,6 +63744,7 @@
false
false
false
+ 0
7083
@@ -56670,6 +63753,7 @@
false
false
false
+ 0
7084
@@ -56678,6 +63762,7 @@
false
false
false
+ 0
7085
@@ -56686,6 +63771,7 @@
false
false
false
+ 0
7086
@@ -56694,6 +63780,7 @@
false
false
false
+ 0
7087
@@ -56702,6 +63789,7 @@
false
false
false
+ 0
7088
@@ -56710,6 +63798,7 @@
false
false
false
+ 0
7089
@@ -56718,6 +63807,7 @@
false
false
false
+ 0
7090
@@ -56726,6 +63816,7 @@
false
false
false
+ 0
7091
@@ -56734,6 +63825,7 @@
false
false
false
+ 0
7092
@@ -56742,6 +63834,7 @@
false
false
false
+ 0
7093
@@ -56750,6 +63843,7 @@
false
false
false
+ 0
7094
@@ -56758,6 +63852,7 @@
false
false
false
+ 0
7095
@@ -56766,6 +63861,7 @@
false
false
false
+ 0
7096
@@ -56774,6 +63870,7 @@
false
false
false
+ 0
7097
@@ -56782,6 +63879,7 @@
false
false
false
+ 0
7098
@@ -56790,6 +63888,7 @@
false
false
false
+ 0
7099
@@ -56798,6 +63897,7 @@
false
false
false
+ 0
7100
@@ -56806,6 +63906,7 @@
false
false
false
+ 0
7101
@@ -56814,6 +63915,7 @@
false
false
false
+ 0
7102
@@ -56822,6 +63924,7 @@
false
false
false
+ 0
7103
@@ -56830,6 +63933,7 @@
false
false
false
+ 0
7104
@@ -56838,6 +63942,7 @@
false
false
false
+ 0
7105
@@ -56846,6 +63951,7 @@
false
false
false
+ 0
7106
@@ -56854,6 +63960,7 @@
false
false
false
+ 0
7107
@@ -56862,6 +63969,7 @@
false
false
false
+ 0
7108
@@ -56870,6 +63978,7 @@
false
false
false
+ 0
7109
@@ -56878,6 +63987,7 @@
false
false
false
+ 0
7110
@@ -56886,6 +63996,7 @@
false
false
false
+ 0
7111
@@ -56894,6 +64005,7 @@
false
false
false
+ 0
7112
@@ -56902,6 +64014,7 @@
false
false
false
+ 0
7113
@@ -56910,6 +64023,7 @@
false
false
false
+ 0
7114
@@ -56918,6 +64032,7 @@
false
false
false
+ 0
7115
@@ -56926,6 +64041,7 @@
false
false
false
+ 0
7116
@@ -56934,6 +64050,7 @@
false
false
false
+ 0
7117
@@ -56942,6 +64059,7 @@
false
false
false
+ 0
7118
@@ -56950,6 +64068,7 @@
false
false
false
+ 0
7119
@@ -56958,6 +64077,7 @@
false
false
false
+ 0
7120
@@ -56966,6 +64086,7 @@
false
false
false
+ 0
7121
@@ -56974,6 +64095,7 @@
false
false
false
+ 0
7122
@@ -56982,6 +64104,7 @@
false
false
false
+ 0
7123
@@ -56990,6 +64113,7 @@
false
false
false
+ 0
7124
@@ -56998,6 +64122,7 @@
false
false
false
+ 0
7125
@@ -57006,6 +64131,7 @@
false
false
false
+ 0
7126
@@ -57014,6 +64140,7 @@
false
false
false
+ 0
7127
@@ -57022,6 +64149,7 @@
false
false
false
+ 0
7128
@@ -57030,6 +64158,7 @@
false
false
false
+ 0
7129
@@ -57038,6 +64167,7 @@
false
false
false
+ 0
7130
@@ -57046,6 +64176,7 @@
false
false
false
+ 0
7131
@@ -57054,6 +64185,7 @@
false
false
false
+ 0
7132
@@ -57062,6 +64194,7 @@
false
false
false
+ 0
7133
@@ -57070,6 +64203,7 @@
false
false
false
+ 0
7134
@@ -57078,6 +64212,7 @@
false
false
false
+ 0
7135
@@ -57086,6 +64221,7 @@
false
false
false
+ 0
7136
@@ -57094,6 +64230,7 @@
false
false
false
+ 0
7137
@@ -57102,6 +64239,7 @@
false
false
false
+ 0
7138
@@ -57110,6 +64248,7 @@
false
false
false
+ 0
7139
@@ -57118,6 +64257,7 @@
false
false
false
+ 0
7140
@@ -57126,6 +64266,7 @@
false
false
false
+ 0
7141
@@ -57134,6 +64275,7 @@
false
false
false
+ 0
7142
@@ -57142,6 +64284,7 @@
false
false
false
+ 0
7143
@@ -57150,6 +64293,7 @@
false
false
false
+ 0
7144
@@ -57158,6 +64302,7 @@
false
false
false
+ 0
7145
@@ -57166,6 +64311,7 @@
false
false
false
+ 0
7146
@@ -57174,6 +64320,7 @@
false
false
false
+ 0
7147
@@ -57182,6 +64329,7 @@
false
false
false
+ 0
7148
@@ -57190,6 +64338,7 @@
false
false
false
+ 0
7149
@@ -57198,6 +64347,7 @@
false
false
false
+ 0
7150
@@ -57206,6 +64356,7 @@
false
false
false
+ 0
7151
@@ -57214,6 +64365,7 @@
false
false
false
+ 0
7152
@@ -57222,6 +64374,7 @@
false
false
false
+ 0
7153
@@ -57230,6 +64383,7 @@
false
false
false
+ 0
7154
@@ -57238,6 +64392,7 @@
false
false
false
+ 0
7155
@@ -57246,6 +64401,7 @@
false
false
false
+ 0
7156
@@ -57254,6 +64410,7 @@
false
false
false
+ 0
7157
@@ -57262,6 +64419,7 @@
false
false
false
+ 0
7158
@@ -57270,6 +64428,7 @@
false
false
false
+ 0
7159
@@ -57278,6 +64437,7 @@
false
false
false
+ 0
7160
@@ -57286,6 +64446,7 @@
false
false
false
+ 0
7161
@@ -57294,6 +64455,7 @@
false
false
false
+ 0
7162
@@ -57302,6 +64464,7 @@
false
false
false
+ 0
7163
@@ -57310,6 +64473,7 @@
false
false
false
+ 0
7164
@@ -57318,6 +64482,7 @@
false
false
false
+ 0
7165
@@ -57326,6 +64491,7 @@
false
false
false
+ 0
7166
@@ -57334,6 +64500,7 @@
false
false
false
+ 0
7167
@@ -57342,6 +64509,7 @@
false
false
false
+ 0
7168
@@ -57350,6 +64518,7 @@
false
false
false
+ 0
7169
@@ -57358,6 +64527,7 @@
false
false
false
+ 0
7170
@@ -57366,6 +64536,7 @@
false
false
false
+ 0
7171
@@ -57374,6 +64545,7 @@
false
false
false
+ 0
7172
@@ -57382,6 +64554,7 @@
false
false
false
+ 0
7173
@@ -57390,6 +64563,7 @@
false
false
false
+ 0
7174
@@ -57398,6 +64572,7 @@
false
false
false
+ 0
7175
@@ -57406,6 +64581,7 @@
false
false
false
+ 0
7176
@@ -57414,6 +64590,7 @@
false
false
false
+ 0
7177
@@ -57422,6 +64599,7 @@
false
false
false
+ 0
7178
@@ -57430,6 +64608,7 @@
false
false
false
+ 0
7179
@@ -57438,6 +64617,7 @@
false
false
false
+ 0
7180
@@ -57446,6 +64626,7 @@
false
false
false
+ 0
7181
@@ -57454,6 +64635,7 @@
false
false
false
+ 0
7182
@@ -57462,6 +64644,7 @@
false
false
false
+ 0
7183
@@ -57470,6 +64653,7 @@
false
false
false
+ 0
7184
@@ -57478,6 +64662,7 @@
false
false
false
+ 0
7185
@@ -57486,6 +64671,7 @@
false
false
false
+ 0
7186
@@ -57494,6 +64680,7 @@
false
false
false
+ 0
7187
@@ -57502,6 +64689,7 @@
false
false
false
+ 0
7188
@@ -57510,6 +64698,7 @@
false
false
false
+ 0
7189
@@ -57518,6 +64707,7 @@
false
false
false
+ 0
7190
@@ -57526,6 +64716,7 @@
false
false
false
+ 0
7191
@@ -57534,6 +64725,7 @@
false
false
false
+ 0
7192
@@ -57542,6 +64734,7 @@
false
false
false
+ 0
7193
@@ -57550,6 +64743,7 @@
false
false
false
+ 0
7194
@@ -57558,6 +64752,7 @@
false
false
false
+ 0
7195
@@ -57566,6 +64761,7 @@
false
false
false
+ 0
7196
@@ -57574,6 +64770,7 @@
false
false
false
+ 0
7197
@@ -57582,6 +64779,7 @@
false
false
false
+ 0
7198
@@ -57590,6 +64788,7 @@
false
false
false
+ 0
7199
@@ -57598,6 +64797,7 @@
false
false
false
+ 0
7200
@@ -57606,6 +64806,7 @@
false
false
false
+ 0
7201
@@ -57614,6 +64815,7 @@
false
false
false
+ 0
7202
@@ -57622,6 +64824,7 @@
false
false
false
+ 0
7203
@@ -57630,6 +64833,7 @@
false
false
false
+ 0
7204
@@ -57638,6 +64842,7 @@
false
false
false
+ 0
7205
@@ -57646,6 +64851,7 @@
false
false
false
+ 0
7206
@@ -57654,6 +64860,7 @@
false
false
false
+ 0
7207
@@ -57662,6 +64869,7 @@
false
false
false
+ 0
7208
@@ -57670,6 +64878,7 @@
false
false
false
+ 0
7209
@@ -57678,6 +64887,7 @@
false
false
false
+ 0
7210
@@ -57686,6 +64896,7 @@
false
false
false
+ 0
7211
@@ -57694,6 +64905,7 @@
false
false
false
+ 0
7212
@@ -57702,6 +64914,7 @@
false
false
false
+ 0
7213
@@ -57710,6 +64923,7 @@
false
false
false
+ 0
7214
@@ -57718,6 +64932,7 @@
false
false
false
+ 0
7215
@@ -57726,6 +64941,7 @@
false
false
false
+ 0
7216
@@ -57734,6 +64950,7 @@
false
false
false
+ 0
7217
@@ -57742,6 +64959,7 @@
false
false
false
+ 0
7218
@@ -57750,6 +64968,7 @@
false
false
false
+ 0
7219
@@ -57758,6 +64977,7 @@
false
false
false
+ 0
7220
@@ -57766,6 +64986,7 @@
false
false
false
+ 0
7221
@@ -57774,6 +64995,7 @@
false
false
false
+ 0
7222
@@ -57782,6 +65004,7 @@
false
false
false
+ 0
7223
@@ -57790,6 +65013,7 @@
false
false
false
+ 0
7224
@@ -57798,6 +65022,7 @@
false
false
false
+ 0
7225
@@ -57806,6 +65031,7 @@
false
false
false
+ 0
7226
@@ -57814,6 +65040,7 @@
false
false
false
+ 0
7227
@@ -57822,6 +65049,7 @@
false
false
false
+ 0
7228
@@ -57830,6 +65058,7 @@
false
false
false
+ 0
7229
@@ -57838,6 +65067,7 @@
false
false
false
+ 0
7230
@@ -57846,6 +65076,7 @@
false
false
false
+ 0
7231
@@ -57854,6 +65085,7 @@
false
false
false
+ 0
7232
@@ -57862,6 +65094,7 @@
false
false
false
+ 0
7233
@@ -57870,6 +65103,7 @@
false
false
false
+ 0
7234
@@ -57878,6 +65112,7 @@
false
false
false
+ 0
7235
@@ -57886,6 +65121,7 @@
false
false
false
+ 0
7236
@@ -57894,6 +65130,7 @@
false
false
false
+ 0
7237
@@ -57902,6 +65139,7 @@
false
false
false
+ 0
7238
@@ -57910,6 +65148,7 @@
false
false
false
+ 0
7239
@@ -57918,6 +65157,7 @@
false
false
false
+ 0
7240
@@ -57926,6 +65166,7 @@
false
false
false
+ 0
7241
@@ -57934,6 +65175,7 @@
false
false
false
+ 0
7242
@@ -57942,6 +65184,7 @@
false
false
false
+ 0
7243
@@ -57950,6 +65193,7 @@
false
false
false
+ 0
7244
@@ -57958,6 +65202,7 @@
false
false
false
+ 0
7245
@@ -57966,6 +65211,7 @@
false
false
false
+ 0
7246
@@ -57974,6 +65220,7 @@
false
false
false
+ 0
7247
@@ -57982,6 +65229,7 @@
false
false
false
+ 0
7248
@@ -57990,6 +65238,7 @@
false
false
false
+ 0
7249
@@ -57998,6 +65247,7 @@
false
false
false
+ 0
7250
@@ -58006,6 +65256,7 @@
false
false
false
+ 0
7251
@@ -58014,6 +65265,7 @@
false
false
false
+ 0
7252
@@ -58022,6 +65274,7 @@
false
false
false
+ 0
7253
@@ -58030,6 +65283,7 @@
false
false
false
+ 0
7254
@@ -58038,6 +65292,7 @@
false
false
false
+ 0
7255
@@ -58046,6 +65301,7 @@
false
false
false
+ 0
7256
@@ -58054,6 +65310,7 @@
false
false
false
+ 0
7257
@@ -58062,6 +65319,7 @@
false
false
false
+ 0
7258
@@ -58070,6 +65328,7 @@
false
false
false
+ 0
7259
@@ -58078,6 +65337,7 @@
false
false
false
+ 0
7260
@@ -58086,6 +65346,7 @@
false
false
false
+ 0
7261
@@ -58094,6 +65355,7 @@
false
false
false
+ 0
7262
@@ -58102,6 +65364,7 @@
false
false
false
+ 0
7263
@@ -58110,6 +65373,7 @@
false
false
false
+ 0
7264
@@ -58118,6 +65382,7 @@
false
false
false
+ 0
7265
@@ -58126,6 +65391,7 @@
false
false
false
+ 0
7266
@@ -58134,6 +65400,7 @@
false
false
false
+ 0
7267
@@ -58142,6 +65409,7 @@
false
false
false
+ 0
7268
@@ -58150,6 +65418,7 @@
false
false
false
+ 0
7269
@@ -58158,6 +65427,7 @@
false
false
false
+ 0
7270
@@ -58166,6 +65436,7 @@
false
false
false
+ 0
7271
@@ -58174,6 +65445,7 @@
false
false
false
+ 0
7272
@@ -58182,6 +65454,7 @@
false
false
false
+ 0
7273
@@ -58190,6 +65463,7 @@
false
false
false
+ 0
7274
@@ -58198,6 +65472,7 @@
false
false
false
+ 0
7275
@@ -58206,6 +65481,7 @@
false
false
false
+ 0
7276
@@ -58214,6 +65490,7 @@
false
false
false
+ 0
7277
@@ -58222,6 +65499,7 @@
false
false
false
+ 0
7278
@@ -58230,6 +65508,7 @@
false
false
false
+ 0
7279
@@ -58238,6 +65517,7 @@
false
false
false
+ 0
7280
@@ -58246,6 +65526,7 @@
false
false
false
+ 0
7281
@@ -58254,6 +65535,7 @@
false
false
false
+ 0
7282
@@ -58262,6 +65544,7 @@
false
false
false
+ 0
7283
@@ -58270,6 +65553,7 @@
false
false
false
+ 0
7284
@@ -58278,6 +65562,7 @@
false
false
false
+ 0
7285
@@ -58286,6 +65571,7 @@
false
false
false
+ 0
7286
@@ -58294,6 +65580,7 @@
false
false
false
+ 0
7287
@@ -58302,6 +65589,7 @@
false
false
false
+ 0
7288
@@ -58310,6 +65598,7 @@
false
false
false
+ 0
7289
@@ -58318,6 +65607,7 @@
false
false
false
+ 0
7290
@@ -58326,6 +65616,7 @@
false
false
false
+ 0
7291
@@ -58334,6 +65625,7 @@
false
false
false
+ 0
7292
@@ -58342,6 +65634,7 @@
false
false
false
+ 0
7293
@@ -58350,6 +65643,7 @@
false
false
false
+ 0
7294
@@ -58358,6 +65652,7 @@
false
false
false
+ 0
7295
@@ -58366,6 +65661,7 @@
false
false
false
+ 0
7296
@@ -58374,6 +65670,7 @@
false
false
false
+ 0
7297
@@ -58382,6 +65679,7 @@
false
false
false
+ 0
7298
@@ -58390,6 +65688,7 @@
false
false
false
+ 0
7299
@@ -58398,6 +65697,7 @@
false
false
false
+ 0
7300
@@ -58406,6 +65706,7 @@
false
false
false
+ 0
7301
@@ -58414,6 +65715,7 @@
false
false
false
+ 0
7302
@@ -58422,6 +65724,7 @@
false
false
false
+ 0
7303
@@ -58430,6 +65733,7 @@
false
false
false
+ 0
7304
@@ -58438,6 +65742,7 @@
false
false
false
+ 0
7305
@@ -58446,6 +65751,7 @@
false
false
false
+ 0
7306
@@ -58454,6 +65760,7 @@
false
false
false
+ 0
7307
@@ -58462,6 +65769,7 @@
false
false
false
+ 0
7308
@@ -58470,6 +65778,7 @@
false
false
false
+ 0
7309
@@ -58478,6 +65787,7 @@
false
false
false
+ 0
7310
@@ -58486,6 +65796,7 @@
false
false
false
+ 0
7311
@@ -58494,6 +65805,7 @@
false
false
false
+ 0
7312
@@ -58502,6 +65814,7 @@
false
false
false
+ 0
7313
@@ -58510,6 +65823,7 @@
false
false
false
+ 0
7314
@@ -58518,6 +65832,7 @@
false
false
false
+ 0
7315
@@ -58526,6 +65841,7 @@
false
false
false
+ 0
7316
@@ -58534,6 +65850,7 @@
false
false
false
+ 0
7317
@@ -58542,6 +65859,7 @@
false
false
false
+ 0
7318
@@ -58550,6 +65868,7 @@
false
false
false
+ 0
7319
@@ -58558,6 +65877,7 @@
false
false
false
+ 0
7320
@@ -58566,6 +65886,7 @@
false
false
false
+ 0
7321
@@ -58574,6 +65895,7 @@
false
false
false
+ 0
7322
@@ -58582,6 +65904,7 @@
false
false
false
+ 0
7323
@@ -58590,6 +65913,7 @@
false
false
false
+ 0
7324
@@ -58598,6 +65922,7 @@
false
false
false
+ 0
7325
@@ -58606,6 +65931,7 @@
false
false
false
+ 0
7326
@@ -58614,6 +65940,7 @@
false
false
false
+ 0
7327
@@ -58622,6 +65949,7 @@
false
false
false
+ 0
7328
@@ -58630,6 +65958,7 @@
false
false
false
+ 0
7329
@@ -58638,6 +65967,7 @@
false
false
false
+ 0
7330
@@ -58646,6 +65976,7 @@
false
false
false
+ 0
7331
@@ -58654,6 +65985,7 @@
false
false
false
+ 0
7332
@@ -58662,6 +65994,7 @@
false
false
false
+ 0
7333
@@ -58670,6 +66003,7 @@
false
false
false
+ 0
7334
@@ -58678,6 +66012,7 @@
false
false
false
+ 0
7335
@@ -58686,6 +66021,7 @@
false
false
false
+ 0
7336
@@ -58694,6 +66030,7 @@
false
false
false
+ 0
7337
@@ -58702,6 +66039,7 @@
false
false
false
+ 0
7338
@@ -58710,6 +66048,7 @@
false
false
false
+ 0
7339
@@ -58718,6 +66057,7 @@
false
false
false
+ 0
7340
@@ -58726,6 +66066,7 @@
false
false
false
+ 0
7341
@@ -58734,6 +66075,7 @@
false
false
false
+ 0
7342
@@ -58742,6 +66084,7 @@
false
false
false
+ 0
7343
@@ -58750,6 +66093,7 @@
false
false
false
+ 0
7344
@@ -58758,6 +66102,7 @@
false
false
false
+ 0
7345
@@ -58766,6 +66111,7 @@
false
false
false
+ 0
7346
@@ -58774,6 +66120,7 @@
false
false
false
+ 0
7347
@@ -58782,6 +66129,7 @@
false
false
false
+ 0
7348
@@ -58790,6 +66138,7 @@
false
false
false
+ 0
7349
@@ -58798,6 +66147,7 @@
false
false
false
+ 0
7350
@@ -58806,6 +66156,7 @@
false
false
false
+ 0
7351
@@ -58814,6 +66165,7 @@
false
false
false
+ 0
7352
@@ -58822,6 +66174,7 @@
false
false
false
+ 0
7353
@@ -58830,6 +66183,7 @@
false
false
false
+ 0
7354
@@ -58838,6 +66192,7 @@
false
false
false
+ 0
7355
@@ -58846,6 +66201,7 @@
false
false
false
+ 0
7356
@@ -58854,6 +66210,7 @@
false
false
false
+ 0
7357
@@ -58862,6 +66219,7 @@
false
false
false
+ 0
7358
@@ -58870,6 +66228,7 @@
false
false
false
+ 0
7359
@@ -58878,6 +66237,7 @@
false
false
false
+ 0
7360
@@ -58886,6 +66246,7 @@
false
false
false
+ 0
7361
@@ -58894,6 +66255,7 @@
false
false
false
+ 0
7362
@@ -58902,6 +66264,7 @@
false
false
false
+ 0
7363
@@ -58910,6 +66273,7 @@
false
false
false
+ 0
7364
@@ -58918,6 +66282,7 @@
false
false
false
+ 0
7365
@@ -58926,6 +66291,7 @@
false
false
false
+ 0
7366
@@ -58934,6 +66300,7 @@
false
false
false
+ 0
7367
@@ -58942,6 +66309,7 @@
false
false
false
+ 0
7368
@@ -58950,6 +66318,7 @@
false
false
false
+ 0
7369
@@ -58958,6 +66327,7 @@
false
false
false
+ 0
7370
@@ -58966,6 +66336,7 @@
false
false
false
+ 0
7371
@@ -58974,6 +66345,7 @@
false
false
false
+ 0
7372
@@ -58982,6 +66354,7 @@
false
false
false
+ 0
7373
@@ -58990,6 +66363,7 @@
false
false
false
+ 0
7374
@@ -58998,6 +66372,7 @@
false
false
false
+ 0
7375
@@ -59006,6 +66381,7 @@
false
false
false
+ 0
7376
@@ -59014,6 +66390,7 @@
false
false
false
+ 0
7377
@@ -59022,6 +66399,7 @@
false
false
false
+ 0
7378
@@ -59030,6 +66408,7 @@
false
false
false
+ 0
7379
@@ -59038,6 +66417,7 @@
false
false
false
+ 0
7380
@@ -59046,6 +66426,7 @@
false
false
false
+ 0
7381
@@ -59054,6 +66435,7 @@
false
false
false
+ 0
7382
@@ -59062,6 +66444,7 @@
false
false
false
+ 0
7383
@@ -59070,6 +66453,7 @@
false
false
false
+ 0
7384
@@ -59078,6 +66462,7 @@
false
false
false
+ 0
7385
@@ -59086,6 +66471,7 @@
false
false
false
+ 0
7386
@@ -59094,6 +66480,7 @@
false
false
false
+ 0
7387
@@ -59102,6 +66489,7 @@
false
false
false
+ 0
7388
@@ -59110,6 +66498,7 @@
false
false
false
+ 0
7389
@@ -59118,6 +66507,7 @@
false
false
false
+ 0
7390
@@ -59126,6 +66516,7 @@
false
false
false
+ 0
7391
@@ -59134,6 +66525,7 @@
false
false
false
+ 0
7392
@@ -59142,6 +66534,7 @@
false
false
false
+ 0
7393
@@ -59150,6 +66543,7 @@
false
false
false
+ 0
7394
@@ -59158,6 +66552,7 @@
false
false
false
+ 0
7395
@@ -59166,6 +66561,7 @@
false
false
false
+ 0
7396
@@ -59174,6 +66570,7 @@
false
false
false
+ 0
7397
@@ -59182,6 +66579,7 @@
false
false
false
+ 0
7398
@@ -59190,6 +66588,7 @@
false
false
false
+ 0
7399
@@ -59198,6 +66597,7 @@
false
false
false
+ 0
7400
@@ -59206,6 +66606,7 @@
false
false
false
+ 0
7401
@@ -59214,6 +66615,7 @@
false
false
false
+ 0
7402
@@ -59222,6 +66624,7 @@
false
false
false
+ 0
7403
@@ -59230,6 +66633,7 @@
false
false
false
+ 0
7404
@@ -59238,6 +66642,7 @@
false
false
false
+ 0
7405
@@ -59246,6 +66651,7 @@
false
false
false
+ 0
7406
@@ -59254,6 +66660,7 @@
false
false
false
+ 0
7407
@@ -59262,6 +66669,7 @@
false
false
false
+ 0
7408
@@ -59270,6 +66678,7 @@
false
false
false
+ 0
7409
@@ -59278,6 +66687,7 @@
false
false
false
+ 0
7410
@@ -59286,6 +66696,7 @@
false
false
false
+ 0
7411
@@ -59294,6 +66705,7 @@
false
false
false
+ 0
7412
@@ -59302,6 +66714,7 @@
false
false
false
+ 0
7413
@@ -59310,6 +66723,7 @@
false
false
false
+ 0
7414
@@ -59318,6 +66732,7 @@
false
false
false
+ 0
7415
@@ -59326,6 +66741,7 @@
false
false
false
+ 0
7416
@@ -59334,6 +66750,7 @@
false
false
false
+ 0
7417
@@ -59342,6 +66759,7 @@
false
false
false
+ 0
7418
@@ -59350,6 +66768,7 @@
false
false
false
+ 0
7419
@@ -59358,6 +66777,7 @@
false
false
false
+ 0
7420
@@ -59366,6 +66786,7 @@
false
false
false
+ 0
7421
@@ -59374,6 +66795,7 @@
false
false
false
+ 0
7422
@@ -59382,6 +66804,7 @@
false
false
false
+ 0
7423
@@ -59390,6 +66813,7 @@
false
false
false
+ 0
7424
@@ -59398,6 +66822,7 @@
false
false
false
+ 0
7425
@@ -59406,6 +66831,7 @@
false
false
false
+ 0
7426
@@ -59414,6 +66840,7 @@
false
false
false
+ 0
7427
@@ -59422,6 +66849,7 @@
false
false
false
+ 0
7428
@@ -59430,6 +66858,7 @@
false
false
false
+ 0
7429
@@ -59438,6 +66867,7 @@
false
false
false
+ 0
7430
@@ -59446,6 +66876,7 @@
false
false
false
+ 0
7431
@@ -59454,6 +66885,7 @@
false
false
false
+ 0
7432
@@ -59462,6 +66894,7 @@
false
false
false
+ 0
7433
@@ -59470,6 +66903,7 @@
false
false
false
+ 0
7434
@@ -59478,6 +66912,7 @@
false
false
false
+ 0
7435
@@ -59486,6 +66921,7 @@
false
false
false
+ 0
7436
@@ -59494,6 +66930,7 @@
false
false
false
+ 0
7437
@@ -59502,6 +66939,7 @@
false
false
false
+ 0
7438
@@ -59510,6 +66948,7 @@
false
false
false
+ 0
7439
@@ -59518,6 +66957,7 @@
false
false
false
+ 0
7440
@@ -59526,6 +66966,7 @@
false
false
false
+ 0
7441
@@ -59534,6 +66975,7 @@
false
false
false
+ 0
7442
@@ -59542,6 +66984,7 @@
false
false
false
+ 0
7443
@@ -59550,6 +66993,7 @@
false
false
false
+ 0
7444
@@ -59558,6 +67002,7 @@
false
false
false
+ 0
7445
@@ -59566,6 +67011,7 @@
false
false
false
+ 0
7446
@@ -59574,6 +67020,7 @@
false
false
false
+ 0
7447
@@ -59582,6 +67029,7 @@
false
false
false
+ 0
7448
@@ -59590,6 +67038,7 @@
false
false
false
+ 0
7449
@@ -59598,6 +67047,7 @@
false
false
false
+ 0
7450
@@ -59606,6 +67056,7 @@
false
false
false
+ 0
7451
@@ -59614,6 +67065,7 @@
false
false
false
+ 0
7452
@@ -59622,6 +67074,7 @@
false
false
false
+ 0
7453
@@ -59630,6 +67083,7 @@
false
false
false
+ 0
7454
@@ -59638,6 +67092,7 @@
false
false
false
+ 0
7455
@@ -59646,6 +67101,7 @@
false
false
false
+ 0
7456
@@ -59654,6 +67110,7 @@
false
false
false
+ 0
7457
@@ -59662,6 +67119,7 @@
false
false
false
+ 0
7458
@@ -59670,6 +67128,7 @@
false
false
false
+ 0
7459
@@ -59678,6 +67137,7 @@
false
false
false
+ 0
7460
@@ -59686,6 +67146,7 @@
false
false
false
+ 0
7461
@@ -59694,6 +67155,7 @@
false
false
false
+ 0
7462
@@ -59702,6 +67164,7 @@
false
false
false
+ 0
7463
@@ -59710,6 +67173,7 @@
false
false
false
+ 0
7464
@@ -59718,6 +67182,7 @@
false
false
false
+ 0
7465
@@ -59726,6 +67191,7 @@
false
false
false
+ 0
7466
@@ -59734,6 +67200,7 @@
false
false
false
+ 0
7467
@@ -59742,6 +67209,7 @@
false
false
false
+ 0
7468
@@ -59750,6 +67218,7 @@
false
false
false
+ 0
7469
@@ -59758,6 +67227,7 @@
false
false
false
+ 0
7470
@@ -59766,6 +67236,7 @@
false
false
false
+ 0
7471
@@ -59774,6 +67245,7 @@
false
false
false
+ 0
7472
@@ -59782,6 +67254,7 @@
false
false
false
+ 0
7473
@@ -59790,6 +67263,7 @@
false
false
false
+ 0
7474
@@ -59798,6 +67272,7 @@
false
false
false
+ 0
7475
@@ -59806,6 +67281,7 @@
false
false
false
+ 0
7476
@@ -59814,6 +67290,7 @@
false
false
false
+ 0
7477
@@ -59822,6 +67299,7 @@
false
false
false
+ 0
7478
@@ -59830,6 +67308,7 @@
false
false
false
+ 0
7479
@@ -59838,6 +67317,7 @@
false
false
false
+ 0
7480
@@ -59846,6 +67326,7 @@
false
false
false
+ 0
7481
@@ -59854,6 +67335,7 @@
false
false
false
+ 0
7482
@@ -59862,6 +67344,7 @@
false
false
false
+ 0
7483
@@ -59870,6 +67353,7 @@
false
false
false
+ 0
7484
@@ -59878,6 +67362,7 @@
false
false
false
+ 0
7485
@@ -59886,6 +67371,7 @@
false
false
false
+ 0
7486
@@ -59894,6 +67380,7 @@
false
false
false
+ 0
7487
@@ -59902,6 +67389,7 @@
false
false
false
+ 0
7488
@@ -59910,6 +67398,7 @@
false
false
false
+ 0
7489
@@ -59918,6 +67407,7 @@
false
false
false
+ 0
7490
@@ -59926,6 +67416,7 @@
false
false
false
+ 0
7491
@@ -59934,6 +67425,7 @@
false
false
false
+ 0
7492
@@ -59942,6 +67434,7 @@
false
false
false
+ 0
7493
@@ -59950,6 +67443,7 @@
false
false
false
+ 0
7494
@@ -59958,6 +67452,7 @@
false
false
false
+ 0
7495
@@ -59966,6 +67461,7 @@
false
false
false
+ 0
7496
@@ -59974,6 +67470,7 @@
false
false
false
+ 0
7497
@@ -59982,6 +67479,7 @@
false
false
false
+ 0
7498
@@ -59990,6 +67488,7 @@
false
false
false
+ 0
7499
@@ -59998,6 +67497,7 @@
false
false
false
+ 0
7500
@@ -60006,6 +67506,7 @@
false
false
false
+ 0
7501
@@ -60014,6 +67515,7 @@
false
false
false
+ 0
7502
@@ -60022,6 +67524,7 @@
false
false
false
+ 0
7503
@@ -60030,6 +67533,7 @@
false
false
false
+ 0
7504
@@ -60038,6 +67542,7 @@
false
false
false
+ 0
7505
@@ -60046,6 +67551,7 @@
false
false
false
+ 0
7506
@@ -60054,6 +67560,7 @@
false
false
false
+ 0
7507
@@ -60062,6 +67569,7 @@
false
false
false
+ 0
7508
@@ -60070,6 +67578,7 @@
false
false
false
+ 0
7509
@@ -60078,6 +67587,7 @@
false
false
false
+ 0
7510
@@ -60086,6 +67596,7 @@
false
false
false
+ 0
7511
@@ -60094,6 +67605,7 @@
false
false
false
+ 0
7512
@@ -60102,6 +67614,7 @@
false
false
false
+ 0
7513
@@ -60110,6 +67623,7 @@
false
false
false
+ 0
7514
@@ -60118,6 +67632,7 @@
false
false
false
+ 0
7515
@@ -60126,6 +67641,7 @@
false
false
false
+ 0
7516
@@ -60134,6 +67650,7 @@
false
false
false
+ 0
7517
@@ -60142,6 +67659,7 @@
false
false
false
+ 0
7518
@@ -60150,6 +67668,7 @@
false
false
false
+ 0
7519
@@ -60158,6 +67677,7 @@
false
false
false
+ 0
7520
@@ -60166,6 +67686,7 @@
false
false
false
+ 0
7521
@@ -60174,6 +67695,7 @@
false
false
false
+ 0
7522
@@ -60182,6 +67704,7 @@
false
false
false
+ 0
7523
@@ -60190,6 +67713,7 @@
false
false
false
+ 0
7524
@@ -60198,6 +67722,7 @@
false
false
false
+ 0
7525
@@ -60206,6 +67731,7 @@
false
false
false
+ 0
7526
@@ -60214,6 +67740,7 @@
false
false
false
+ 0
7527
@@ -60222,6 +67749,7 @@
false
false
false
+ 0
7528
@@ -60230,6 +67758,7 @@
false
false
false
+ 0
7529
@@ -60238,6 +67767,7 @@
false
false
false
+ 0
7530
@@ -60246,6 +67776,7 @@
false
false
false
+ 0
7531
@@ -60254,6 +67785,7 @@
false
false
false
+ 0
7532
@@ -60262,6 +67794,7 @@
false
false
false
+ 0
7533
@@ -60270,6 +67803,7 @@
false
false
false
+ 0
7534
@@ -60278,6 +67812,7 @@
false
false
false
+ 0
7535
@@ -60286,6 +67821,7 @@
false
false
false
+ 0
7536
@@ -60294,6 +67830,7 @@
false
false
false
+ 0
7537
@@ -60302,6 +67839,7 @@
false
false
false
+ 0
7538
@@ -60310,6 +67848,7 @@
false
false
false
+ 0
7539
@@ -60318,6 +67857,7 @@
false
false
false
+ 0
7540
@@ -60326,6 +67866,7 @@
false
false
false
+ 0
7541
@@ -60334,6 +67875,7 @@
false
false
false
+ 0
7542
@@ -60342,6 +67884,7 @@
false
false
false
+ 0
7543
@@ -60350,6 +67893,7 @@
false
false
false
+ 0
7544
@@ -60358,6 +67902,7 @@
false
false
false
+ 0
7545
@@ -60366,6 +67911,7 @@
false
false
false
+ 0
7546
@@ -60374,6 +67920,7 @@
false
false
false
+ 0
7547
@@ -60382,6 +67929,7 @@
false
false
false
+ 0
7548
@@ -60390,6 +67938,7 @@
false
false
false
+ 0
7549
@@ -60398,6 +67947,7 @@
false
false
false
+ 0
7550
@@ -60406,6 +67956,7 @@
false
false
false
+ 0
7551
@@ -60414,6 +67965,7 @@
false
false
false
+ 0
7552
@@ -60422,6 +67974,7 @@
false
false
false
+ 0
7553
@@ -60430,6 +67983,7 @@
false
false
false
+ 0
7554
@@ -60438,6 +67992,7 @@
false
false
false
+ 0
7555
@@ -60446,6 +68001,7 @@
false
false
false
+ 0
7556
@@ -60454,6 +68010,7 @@
false
false
false
+ 0
7557
@@ -60462,6 +68019,7 @@
false
false
false
+ 0
7558
@@ -60470,6 +68028,7 @@
false
false
false
+ 0
7559
@@ -60478,6 +68037,7 @@
false
false
false
+ 0
7560
@@ -60486,6 +68046,7 @@
false
false
false
+ 0
7561
@@ -60494,6 +68055,7 @@
false
false
false
+ 0
7562
@@ -60502,6 +68064,7 @@
false
false
false
+ 0
7563
@@ -60510,6 +68073,7 @@
false
false
false
+ 0
7564
@@ -60518,6 +68082,7 @@
false
false
false
+ 0
7565
@@ -60526,6 +68091,7 @@
false
false
false
+ 0
7566
@@ -60534,6 +68100,7 @@
false
false
false
+ 0
7567
@@ -60542,6 +68109,7 @@
false
false
false
+ 0
7568
@@ -60550,6 +68118,7 @@
false
false
false
+ 0
7569
@@ -60558,6 +68127,7 @@
false
false
false
+ 0
7570
@@ -60566,6 +68136,7 @@
false
false
false
+ 0
7571
@@ -60574,6 +68145,7 @@
false
false
false
+ 0
7572
@@ -60582,6 +68154,7 @@
false
false
false
+ 0
7573
@@ -60590,6 +68163,7 @@
false
false
false
+ 0
7574
@@ -60598,6 +68172,7 @@
false
false
false
+ 0
7575
@@ -60606,6 +68181,7 @@
false
false
false
+ 0
7576
@@ -60614,6 +68190,7 @@
false
false
false
+ 0
7577
@@ -60622,6 +68199,7 @@
false
false
false
+ 0
7578
@@ -60630,6 +68208,7 @@
false
false
false
+ 0
7579
@@ -60638,6 +68217,7 @@
false
false
false
+ 0
7580
@@ -60646,6 +68226,7 @@
false
false
false
+ 0
7581
@@ -60654,6 +68235,7 @@
false
false
false
+ 0
7582
@@ -60662,6 +68244,7 @@
false
false
false
+ 0
7583
@@ -60670,6 +68253,7 @@
false
false
false
+ 0
7584
@@ -60678,6 +68262,7 @@
false
false
false
+ 0
7585
@@ -60686,6 +68271,7 @@
false
false
false
+ 0
7586
@@ -60694,6 +68280,7 @@
false
false
false
+ 0
7587
@@ -60702,6 +68289,7 @@
false
false
false
+ 0
7588
@@ -60710,6 +68298,7 @@
false
false
false
+ 0
7589
@@ -60718,6 +68307,7 @@
false
false
false
+ 0
7590
@@ -60726,6 +68316,7 @@
false
false
false
+ 0
7591
@@ -60734,6 +68325,7 @@
false
false
false
+ 0
7592
@@ -60742,6 +68334,7 @@
false
false
false
+ 0
7593
@@ -60750,6 +68343,7 @@
false
false
false
+ 0
7594
@@ -60758,6 +68352,7 @@
false
false
false
+ 0
7595
@@ -60766,6 +68361,7 @@
false
false
false
+ 0
7596
@@ -60774,6 +68370,7 @@
false
false
false
+ 0
7597
@@ -60782,6 +68379,7 @@
false
false
false
+ 0
7598
@@ -60790,6 +68388,7 @@
false
false
false
+ 0
7599
@@ -60798,6 +68397,7 @@
false
false
false
+ 0
7600
@@ -60806,6 +68406,7 @@
false
false
false
+ 0
7601
@@ -60814,6 +68415,7 @@
false
false
false
+ 0
7602
@@ -60822,6 +68424,7 @@
false
false
false
+ 0
7603
@@ -60830,6 +68433,7 @@
false
false
false
+ 0
7604
@@ -60838,6 +68442,7 @@
false
false
false
+ 0
7605
@@ -60846,6 +68451,7 @@
false
false
false
+ 0
7606
@@ -60854,6 +68460,7 @@
false
false
false
+ 0
7607
@@ -60862,6 +68469,7 @@
false
false
false
+ 0
7608
@@ -60870,6 +68478,7 @@
false
false
false
+ 0
7609
@@ -60878,6 +68487,7 @@
false
false
false
+ 0
7610
@@ -60886,6 +68496,7 @@
false
false
false
+ 0
7611
@@ -60894,6 +68505,7 @@
false
false
false
+ 0
7612
@@ -60902,6 +68514,7 @@
false
false
false
+ 0
7613
@@ -60910,6 +68523,7 @@
false
false
false
+ 0
7614
@@ -60918,6 +68532,7 @@
false
false
false
+ 0
7615
@@ -60926,6 +68541,7 @@
false
false
false
+ 0
7616
@@ -60934,6 +68550,7 @@
false
false
false
+ 0
7617
@@ -60942,6 +68559,7 @@
false
false
false
+ 0
7618
@@ -60950,6 +68568,7 @@
false
false
false
+ 0
7619
@@ -60958,6 +68577,7 @@
false
false
false
+ 0
7620
@@ -60966,6 +68586,7 @@
false
false
false
+ 0
7621
@@ -60974,6 +68595,7 @@
false
false
false
+ 0
7622
@@ -60982,6 +68604,7 @@
false
false
false
+ 0
7623
@@ -60990,6 +68613,7 @@
false
false
false
+ 0
7624
@@ -60998,6 +68622,7 @@
false
false
false
+ 0
7625
@@ -61006,6 +68631,7 @@
false
false
false
+ 0
7626
@@ -61014,6 +68640,7 @@
false
false
false
+ 0
7627
@@ -61022,6 +68649,7 @@
false
false
false
+ 0
7628
@@ -61030,6 +68658,7 @@
false
false
false
+ 0
7629
@@ -61038,6 +68667,7 @@
false
false
false
+ 0
7630
@@ -61046,6 +68676,7 @@
false
false
false
+ 0
7631
@@ -61054,6 +68685,7 @@
false
false
false
+ 0
7632
@@ -61062,6 +68694,7 @@
false
false
false
+ 0
7633
@@ -61070,6 +68703,7 @@
false
false
false
+ 0
7634
@@ -61078,6 +68712,7 @@
false
false
false
+ 0
7635
@@ -61086,6 +68721,7 @@
false
false
false
+ 0
7636
@@ -61094,6 +68730,7 @@
false
false
false
+ 0
7637
@@ -61102,6 +68739,7 @@
false
false
false
+ 0
7638
@@ -61110,6 +68748,7 @@
false
false
false
+ 0
7639
@@ -61118,6 +68757,7 @@
false
false
false
+ 0
7640
@@ -61126,6 +68766,7 @@
false
false
false
+ 0
7641
@@ -61134,6 +68775,7 @@
false
false
false
+ 0
7642
@@ -61142,6 +68784,7 @@
false
false
false
+ 0
7643
@@ -61150,6 +68793,7 @@
false
false
false
+ 0
7644
@@ -61158,6 +68802,7 @@
false
false
false
+ 0
7645
@@ -61166,6 +68811,7 @@
false
false
false
+ 0
7646
@@ -61174,6 +68820,7 @@
false
false
false
+ 0
7647
@@ -61182,6 +68829,7 @@
false
false
false
+ 0
7648
@@ -61190,6 +68838,7 @@
false
false
false
+ 0
7649
@@ -61198,6 +68847,7 @@
false
false
false
+ 0
7650
@@ -61206,6 +68856,7 @@
false
false
false
+ 0
7651
@@ -61214,6 +68865,7 @@
false
false
false
+ 0
7652
@@ -61222,6 +68874,7 @@
false
false
false
+ 0
7653
@@ -61230,6 +68883,7 @@
false
false
false
+ 0
7654
@@ -61238,6 +68892,7 @@
false
false
false
+ 0
7655
@@ -61246,6 +68901,7 @@
false
false
false
+ 0
7656
@@ -61254,6 +68910,7 @@
false
false
false
+ 0
7657
@@ -61262,6 +68919,7 @@
false
false
false
+ 0
7658
@@ -61270,6 +68928,7 @@
false
false
false
+ 0
7659
@@ -61278,6 +68937,7 @@
false
false
false
+ 0
7660
@@ -61286,6 +68946,7 @@
false
false
false
+ 0
7661
@@ -61294,6 +68955,7 @@
false
false
false
+ 0
7662
@@ -61302,6 +68964,7 @@
false
false
false
+ 0
7663
@@ -61310,6 +68973,7 @@
false
false
false
+ 0
7664
@@ -61318,6 +68982,7 @@
false
false
false
+ 0
7665
@@ -61326,6 +68991,7 @@
false
false
false
+ 0
7666
@@ -61334,6 +69000,7 @@
false
false
false
+ 0
7667
@@ -61342,6 +69009,7 @@
false
false
false
+ 0
7668
@@ -61350,6 +69018,7 @@
false
false
false
+ 0
7669
@@ -61358,6 +69027,7 @@
false
false
false
+ 0
7670
@@ -61366,6 +69036,7 @@
false
false
false
+ 0
7671
@@ -61374,6 +69045,7 @@
false
false
false
+ 0
7672
@@ -61382,6 +69054,7 @@
false
false
false
+ 0
7673
@@ -61390,6 +69063,7 @@
false
false
false
+ 0
7674
@@ -61398,6 +69072,7 @@
false
false
false
+ 0
7675
@@ -61406,6 +69081,7 @@
false
false
false
+ 0
7676
@@ -61414,6 +69090,7 @@
false
false
false
+ 0
7677
@@ -61422,6 +69099,7 @@
false
false
false
+ 0
7678
@@ -61430,6 +69108,7 @@
false
false
false
+ 0
7679
@@ -61438,6 +69117,7 @@
false
false
false
+ 0
7680
@@ -61446,6 +69126,7 @@
false
false
false
+ 0
7681
@@ -61454,6 +69135,7 @@
false
false
false
+ 0
7682
@@ -61462,6 +69144,7 @@
false
false
false
+ 0
7683
@@ -61470,6 +69153,7 @@
false
false
false
+ 0
7684
@@ -61478,6 +69162,7 @@
false
false
false
+ 0
7685
@@ -61486,6 +69171,7 @@
false
false
false
+ 0
7686
@@ -61494,6 +69180,7 @@
false
false
false
+ 0
7687
@@ -61502,6 +69189,7 @@
false
false
false
+ 0
7688
@@ -61510,6 +69198,7 @@
false
false
false
+ 0
7689
@@ -61518,6 +69207,7 @@
false
false
false
+ 0
7690
@@ -61526,6 +69216,7 @@
false
false
false
+ 0
7691
@@ -61534,6 +69225,7 @@
false
false
false
+ 0
7692
@@ -61542,6 +69234,7 @@
false
false
false
+ 0
7693
@@ -61550,6 +69243,7 @@
false
false
false
+ 0
7694
@@ -61558,6 +69252,7 @@
false
false
false
+ 0
7695
@@ -61566,6 +69261,7 @@
false
false
false
+ 0
7696
@@ -61574,6 +69270,7 @@
false
false
false
+ 0
7697
@@ -61582,6 +69279,7 @@
false
false
false
+ 0
7698
@@ -61590,6 +69288,7 @@
false
false
false
+ 0
7699
@@ -61598,6 +69297,7 @@
false
false
false
+ 0
7700
@@ -61606,6 +69306,7 @@
false
false
false
+ 0
7701
@@ -61614,6 +69315,7 @@
false
false
false
+ 0
7702
@@ -61622,6 +69324,7 @@
false
false
false
+ 0
7703
@@ -61630,6 +69333,7 @@
false
false
false
+ 0
7704
@@ -61638,6 +69342,7 @@
false
false
false
+ 0
7705
@@ -61646,6 +69351,7 @@
false
false
false
+ 0
7706
@@ -61654,6 +69360,7 @@
false
false
false
+ 0
7707
@@ -61662,6 +69369,7 @@
false
false
false
+ 0
7708
@@ -61670,6 +69378,7 @@
false
false
false
+ 0
7709
@@ -61678,6 +69387,7 @@
false
false
false
+ 0
7710
@@ -61686,6 +69396,7 @@
false
false
false
+ 0
7711
@@ -61694,6 +69405,7 @@
false
false
false
+ 0
7712
@@ -61702,6 +69414,7 @@
false
false
false
+ 0
7713
@@ -61710,6 +69423,7 @@
false
false
false
+ 0
7714
@@ -61718,6 +69432,7 @@
false
false
false
+ 0
7715
@@ -61726,6 +69441,7 @@
false
false
false
+ 0
7716
@@ -61734,6 +69450,7 @@
false
false
false
+ 0
7717
@@ -61742,6 +69459,7 @@
false
false
false
+ 0
7718
@@ -61750,6 +69468,7 @@
false
false
false
+ 0
7719
@@ -61758,6 +69477,7 @@
false
false
false
+ 0
7720
@@ -61766,6 +69486,7 @@
false
false
false
+ 0
7721
@@ -61774,6 +69495,7 @@
false
false
false
+ 0
7722
@@ -61782,6 +69504,7 @@
false
false
false
+ 0
7723
@@ -61790,6 +69513,7 @@
false
false
false
+ 0
7724
@@ -61798,6 +69522,7 @@
false
false
false
+ 0
7725
@@ -61806,6 +69531,7 @@
false
false
false
+ 0
7726
@@ -61814,6 +69540,7 @@
false
false
false
+ 0
7727
@@ -61822,6 +69549,7 @@
false
false
false
+ 0
7728
@@ -61830,6 +69558,7 @@
false
false
false
+ 0
7729
@@ -61838,6 +69567,7 @@
false
false
false
+ 0
7730
@@ -61846,6 +69576,7 @@
false
false
false
+ 0
7731
@@ -61854,6 +69585,7 @@
false
false
false
+ 0
7732
@@ -61862,6 +69594,7 @@
false
false
false
+ 0
7733
@@ -61870,6 +69603,7 @@
false
false
false
+ 0
7734
@@ -61878,6 +69612,7 @@
false
false
false
+ 0
7735
@@ -61886,6 +69621,7 @@
false
false
false
+ 0
7736
@@ -61894,6 +69630,7 @@
false
false
false
+ 0
7737
@@ -61902,6 +69639,7 @@
false
false
false
+ 0
7738
@@ -61910,6 +69648,7 @@
false
false
false
+ 0
7739
@@ -61918,6 +69657,7 @@
false
false
false
+ 0
7740
@@ -61926,6 +69666,7 @@
false
false
false
+ 0
7741
@@ -61934,6 +69675,7 @@
false
false
false
+ 0
7742
@@ -61942,6 +69684,7 @@
false
false
false
+ 0
7743
@@ -61950,6 +69693,7 @@
false
false
false
+ 0
7744
@@ -61958,6 +69702,7 @@
false
false
false
+ 0
7745
@@ -61966,6 +69711,7 @@
false
false
false
+ 0
7746
@@ -61974,6 +69720,7 @@
false
false
false
+ 0
7747
@@ -61982,6 +69729,7 @@
false
false
false
+ 0
7748
@@ -61990,6 +69738,7 @@
false
false
false
+ 0
7749
@@ -61998,6 +69747,7 @@
false
false
false
+ 0
7750
@@ -62006,6 +69756,7 @@
false
false
false
+ 0
7751
@@ -62014,6 +69765,7 @@
false
false
false
+ 0
7752
@@ -62022,6 +69774,7 @@
false
false
false
+ 0
7753
@@ -62030,6 +69783,7 @@
false
false
false
+ 0
7754
@@ -62038,6 +69792,7 @@
false
false
false
+ 0
7755
@@ -62046,6 +69801,7 @@
false
false
false
+ 0
7756
@@ -62054,6 +69810,7 @@
false
false
false
+ 0
7757
@@ -62062,6 +69819,7 @@
false
false
false
+ 0
7758
@@ -62070,6 +69828,7 @@
false
false
false
+ 0
7759
@@ -62078,6 +69837,7 @@
false
false
false
+ 0
7760
@@ -62086,6 +69846,7 @@
false
false
false
+ 0
7761
@@ -62094,6 +69855,7 @@
false
false
false
+ 0
7762
@@ -62102,6 +69864,7 @@
false
false
false
+ 0
7763
@@ -62110,6 +69873,7 @@
false
false
false
+ 0
7764
@@ -62118,6 +69882,7 @@
false
false
false
+ 0
7765
@@ -62126,6 +69891,7 @@
false
false
false
+ 0
7766
@@ -62134,6 +69900,7 @@
false
false
false
+ 0
7767
@@ -62142,6 +69909,7 @@
false
false
false
+ 0
7768
@@ -62150,6 +69918,7 @@
false
false
false
+ 0
7769
@@ -62158,6 +69927,7 @@
false
false
false
+ 0
7770
@@ -62166,6 +69936,7 @@
false
false
false
+ 0
7771
@@ -62174,6 +69945,7 @@
false
false
false
+ 0
7772
@@ -62182,6 +69954,7 @@
false
false
false
+ 0
7773
@@ -62190,6 +69963,7 @@
false
false
false
+ 0
7774
@@ -62198,6 +69972,7 @@
false
false
false
+ 0
7775
@@ -62206,6 +69981,7 @@
false
false
false
+ 0
7776
@@ -62214,6 +69990,7 @@
false
false
false
+ 0
7777
@@ -62222,6 +69999,7 @@
false
false
false
+ 0
7778
@@ -62230,6 +70008,7 @@
false
false
false
+ 0
7779
@@ -62238,6 +70017,7 @@
false
false
false
+ 0
7780
@@ -62246,6 +70026,7 @@
false
false
false
+ 0
7781
@@ -62254,6 +70035,7 @@
false
false
false
+ 0
7782
@@ -62262,6 +70044,7 @@
false
false
false
+ 0
7783
@@ -62270,6 +70053,7 @@
false
false
false
+ 0
7784
@@ -62278,6 +70062,7 @@
false
false
false
+ 0
7785
@@ -62286,6 +70071,7 @@
false
false
false
+ 0
7786
@@ -62294,6 +70080,7 @@
false
false
false
+ 0
7787
@@ -62302,6 +70089,7 @@
false
false
false
+ 0
7788
@@ -62310,6 +70098,7 @@
false
false
false
+ 0
7789
@@ -62318,6 +70107,7 @@
false
false
false
+ 0
7790
@@ -62326,6 +70116,7 @@
false
false
false
+ 0
7791
@@ -62334,6 +70125,7 @@
false
false
false
+ 0
7792
@@ -62342,6 +70134,7 @@
false
false
false
+ 0
7793
@@ -62350,6 +70143,7 @@
false
false
false
+ 0
7794
@@ -62358,6 +70152,7 @@
false
false
false
+ 0
7795
@@ -62366,6 +70161,7 @@
false
false
false
+ 0
7796
@@ -62374,6 +70170,7 @@
false
false
false
+ 0
7797
@@ -62382,6 +70179,7 @@
false
false
false
+ 0
7798
@@ -62390,6 +70188,7 @@
false
false
false
+ 0
7799
@@ -62398,6 +70197,7 @@
false
false
false
+ 0
7800
@@ -62406,6 +70206,7 @@
false
false
false
+ 0
7801
@@ -62414,6 +70215,7 @@
false
false
false
+ 0
7802
@@ -62422,6 +70224,7 @@
false
false
false
+ 0
7803
@@ -62430,6 +70233,7 @@
false
false
false
+ 0
7804
@@ -62438,6 +70242,7 @@
false
false
false
+ 0
7805
@@ -62446,6 +70251,7 @@
false
false
false
+ 0
7806
@@ -62454,6 +70260,7 @@
false
false
false
+ 0
7807
@@ -62462,6 +70269,7 @@
false
false
false
+ 0
7808
@@ -62470,6 +70278,7 @@
false
false
false
+ 0
7809
@@ -62478,6 +70287,7 @@
false
false
false
+ 0
7810
@@ -62486,6 +70296,7 @@
false
false
false
+ 0
7811
@@ -62494,6 +70305,7 @@
false
false
false
+ 0
7812
@@ -62502,6 +70314,7 @@
false
false
false
+ 0
7813
@@ -62510,6 +70323,7 @@
false
false
false
+ 0
7814
@@ -62518,6 +70332,7 @@
false
false
false
+ 0
7815
@@ -62526,6 +70341,7 @@
false
false
false
+ 0
7816
@@ -62534,6 +70350,7 @@
false
false
false
+ 0
7817
@@ -62542,6 +70359,7 @@
false
false
false
+ 0
7818
@@ -62550,6 +70368,7 @@
false
false
false
+ 0
7819
@@ -62558,6 +70377,7 @@
false
false
false
+ 0
7820
@@ -62566,6 +70386,7 @@
false
false
false
+ 0
7821
@@ -62574,6 +70395,7 @@
false
false
false
+ 0
7822
@@ -62582,6 +70404,7 @@
false
false
false
+ 0
7823
@@ -62590,6 +70413,7 @@
false
false
false
+ 0
7824
@@ -62598,6 +70422,7 @@
false
false
false
+ 0
7825
@@ -62606,6 +70431,7 @@
false
false
false
+ 0
7826
@@ -62614,6 +70440,7 @@
false
false
false
+ 0
7827
@@ -62622,6 +70449,7 @@
false
false
false
+ 0
7828
@@ -62630,6 +70458,7 @@
false
false
false
+ 0
7829
@@ -62638,6 +70467,7 @@
false
false
false
+ 0
7830
@@ -62646,6 +70476,7 @@
false
false
false
+ 0
7831
@@ -62654,6 +70485,7 @@
false
false
false
+ 0
7832
@@ -62662,6 +70494,7 @@
false
false
false
+ 0
7833
@@ -62670,6 +70503,7 @@
false
false
false
+ 0
7834
@@ -62678,6 +70512,7 @@
false
false
false
+ 0
7835
@@ -62686,6 +70521,7 @@
false
false
false
+ 0
7836
@@ -62694,6 +70530,7 @@
false
false
false
+ 0
7837
@@ -62702,6 +70539,7 @@
false
false
false
+ 0
7838
@@ -62710,6 +70548,7 @@
false
false
false
+ 0
7839
@@ -62718,6 +70557,7 @@
false
false
false
+ 0
7840
@@ -62726,6 +70566,7 @@
false
false
false
+ 0
7841
@@ -62734,6 +70575,7 @@
false
false
false
+ 0
7842
@@ -62742,6 +70584,7 @@
false
false
false
+ 0
7843
@@ -62750,6 +70593,7 @@
false
false
false
+ 0
7844
@@ -62758,6 +70602,7 @@
false
false
false
+ 0
7845
@@ -62766,6 +70611,7 @@
false
false
false
+ 0
7846
@@ -62774,6 +70620,7 @@
false
false
false
+ 0
7847
@@ -62782,6 +70629,7 @@
false
false
false
+ 0
7848
@@ -62790,6 +70638,7 @@
false
false
false
+ 0
7849
@@ -62798,6 +70647,7 @@
false
false
false
+ 0
7850
@@ -62806,6 +70656,7 @@
false
false
false
+ 0
7851
@@ -62814,6 +70665,7 @@
false
false
false
+ 0
7852
@@ -62822,6 +70674,7 @@
false
false
false
+ 0
7853
@@ -62830,6 +70683,7 @@
false
false
false
+ 0
7854
@@ -62838,6 +70692,7 @@
false
false
false
+ 0
7855
@@ -62846,6 +70701,7 @@
false
false
false
+ 0
7856
@@ -62854,6 +70710,7 @@
false
false
false
+ 0
7857
@@ -62862,6 +70719,7 @@
false
false
false
+ 0
7858
@@ -62870,6 +70728,7 @@
false
false
false
+ 0
7859
@@ -62878,6 +70737,7 @@
false
false
false
+ 0
7860
@@ -62886,6 +70746,7 @@
false
false
false
+ 0
7861
@@ -62894,6 +70755,7 @@
false
false
false
+ 0
7862
@@ -62902,6 +70764,7 @@
false
false
false
+ 0
7863
@@ -62910,6 +70773,7 @@
false
false
false
+ 0
7864
@@ -62918,6 +70782,7 @@
false
false
false
+ 0
7865
@@ -62926,6 +70791,7 @@
false
false
false
+ 0
7866
@@ -62934,6 +70800,7 @@
false
false
false
+ 0
7867
@@ -62942,6 +70809,7 @@
false
false
false
+ 0
7868
@@ -62950,6 +70818,7 @@
false
false
false
+ 0
7869
@@ -62958,6 +70827,7 @@
false
false
false
+ 0
7870
@@ -62966,6 +70836,7 @@
false
false
false
+ 0
7871
@@ -62974,6 +70845,7 @@
false
false
false
+ 0
7872
@@ -62982,6 +70854,7 @@
false
false
false
+ 0
7873
@@ -62990,6 +70863,7 @@
false
false
false
+ 0
7874
@@ -62998,6 +70872,7 @@
false
false
false
+ 0
7875
@@ -63006,6 +70881,7 @@
false
false
false
+ 0
7876
@@ -63014,6 +70890,7 @@
false
false
false
+ 0
7877
@@ -63022,6 +70899,7 @@
false
false
false
+ 0
7878
@@ -63030,6 +70908,7 @@
false
false
false
+ 0
7879
@@ -63038,6 +70917,7 @@
false
false
false
+ 0
7880
@@ -63046,6 +70926,7 @@
false
false
false
+ 0
7881
@@ -63054,6 +70935,7 @@
false
false
false
+ 0
7882
@@ -63062,6 +70944,7 @@
false
false
false
+ 0
7883
@@ -63070,6 +70953,7 @@
false
false
false
+ 0
7884
@@ -63078,6 +70962,7 @@
false
false
false
+ 0
7885
@@ -63086,6 +70971,7 @@
false
false
false
+ 0
7886
@@ -63094,6 +70980,7 @@
false
false
false
+ 0
7887
@@ -63102,6 +70989,7 @@
false
false
false
+ 0
7888
@@ -63110,6 +70998,7 @@
false
false
false
+ 0
7889
@@ -63118,6 +71007,7 @@
false
false
false
+ 0
7890
@@ -63126,6 +71016,7 @@
false
false
false
+ 0
7891
@@ -63134,6 +71025,7 @@
false
false
false
+ 0
7892
@@ -63142,6 +71034,7 @@
false
false
false
+ 0
7893
@@ -63150,6 +71043,7 @@
false
false
false
+ 0
7894
@@ -63158,6 +71052,7 @@
false
false
false
+ 0
7895
@@ -63166,6 +71061,7 @@
false
false
false
+ 0
7896
@@ -63174,6 +71070,7 @@
false
false
false
+ 0
7897
@@ -63182,6 +71079,7 @@
false
false
false
+ 0
7898
@@ -63190,6 +71088,7 @@
false
false
false
+ 0
7899
@@ -63198,6 +71097,7 @@
false
false
false
+ 0
7900
@@ -63206,6 +71106,7 @@
false
false
false
+ 0
7901
@@ -63214,6 +71115,7 @@
false
false
false
+ 0
7902
@@ -63222,6 +71124,7 @@
false
false
false
+ 0
7903
@@ -63230,6 +71133,7 @@
false
false
false
+ 0
7904
@@ -63238,6 +71142,7 @@
false
false
false
+ 0
7905
@@ -63246,6 +71151,7 @@
false
false
false
+ 0
7906
@@ -63254,6 +71160,7 @@
false
false
false
+ 0
7907
@@ -63262,6 +71169,7 @@
false
false
false
+ 0
7908
@@ -63270,6 +71178,7 @@
false
false
false
+ 0
7909
@@ -63278,6 +71187,7 @@
false
false
false
+ 0
7910
@@ -63286,6 +71196,7 @@
false
false
false
+ 0
7911
@@ -63294,6 +71205,7 @@
false
false
false
+ 0
7912
@@ -63302,6 +71214,7 @@
false
false
false
+ 0
7913
@@ -63310,6 +71223,7 @@
false
false
false
+ 0
7914
@@ -63318,6 +71232,7 @@
false
false
false
+ 0
7915
@@ -63326,6 +71241,7 @@
false
false
false
+ 0
7916
@@ -63334,6 +71250,7 @@
false
false
false
+ 0
7917
@@ -63342,6 +71259,7 @@
false
false
false
+ 0
7918
@@ -63350,6 +71268,7 @@
false
false
false
+ 0
7919
@@ -63358,6 +71277,7 @@
false
false
false
+ 0
7920
@@ -63366,6 +71286,7 @@
false
false
false
+ 0
7921
@@ -63374,6 +71295,7 @@
false
false
false
+ 0
7922
@@ -63382,6 +71304,7 @@
false
false
false
+ 0
7923
@@ -63390,6 +71313,7 @@
false
false
false
+ 0
7924
@@ -63398,6 +71322,7 @@
false
false
false
+ 0
7925
@@ -63406,6 +71331,7 @@
false
false
false
+ 0
7926
@@ -63414,6 +71340,7 @@
false
false
false
+ 0
7927
@@ -63422,6 +71349,7 @@
false
false
false
+ 0
7928
@@ -63430,6 +71358,7 @@
false
false
false
+ 0
7929
@@ -63438,6 +71367,7 @@
false
false
false
+ 0
7930
@@ -63446,6 +71376,7 @@
false
false
false
+ 0
7931
@@ -63454,6 +71385,7 @@
false
false
false
+ 0
7932
@@ -63462,6 +71394,7 @@
false
false
false
+ 0
7933
@@ -63470,6 +71403,7 @@
false
false
false
+ 0
7934
@@ -63478,6 +71412,7 @@
false
false
false
+ 0
7935
@@ -63486,6 +71421,7 @@
false
false
false
+ 0
7936
@@ -63494,6 +71430,7 @@
false
false
false
+ 0
7937
@@ -63502,6 +71439,7 @@
false
false
false
+ 0
7938
@@ -63510,6 +71448,7 @@
false
false
false
+ 0
7939
@@ -63518,6 +71457,7 @@
false
false
false
+ 0
7940
@@ -63526,6 +71466,7 @@
false
false
false
+ 0
7941
@@ -63534,6 +71475,7 @@
false
false
false
+ 0
7942
@@ -63542,6 +71484,7 @@
false
false
false
+ 0
7943
@@ -63550,6 +71493,7 @@
false
false
false
+ 0
7944
@@ -63558,6 +71502,7 @@
false
false
false
+ 0
7945
@@ -63566,6 +71511,7 @@
false
false
false
+ 0
7946
@@ -63574,6 +71520,7 @@
false
false
false
+ 0
7947
@@ -63582,6 +71529,7 @@
false
false
false
+ 0
7948
@@ -63590,6 +71538,7 @@
false
false
false
+ 0
7949
@@ -63598,6 +71547,7 @@
false
false
false
+ 0
7950
@@ -63606,6 +71556,7 @@
false
false
false
+ 0
7951
@@ -63614,6 +71565,7 @@
false
false
false
+ 0
7952
@@ -63622,6 +71574,7 @@
false
false
false
+ 0
7953
@@ -63630,6 +71583,7 @@
false
false
false
+ 0
7954
@@ -63638,6 +71592,7 @@
false
false
false
+ 0
7955
@@ -63646,6 +71601,7 @@
false
false
false
+ 0
7956
@@ -63654,6 +71610,7 @@
false
false
false
+ 0
7957
@@ -63662,6 +71619,7 @@
false
false
false
+ 0
7958
@@ -63670,6 +71628,7 @@
false
false
false
+ 0
7959
@@ -63678,6 +71637,7 @@
false
false
false
+ 0
7960
@@ -63686,6 +71646,7 @@
false
false
false
+ 0
7961
@@ -63694,6 +71655,7 @@
false
false
false
+ 0
7962
@@ -63702,6 +71664,7 @@
false
false
false
+ 0
7963
@@ -63710,6 +71673,7 @@
false
false
false
+ 0
7964
@@ -63718,6 +71682,7 @@
false
false
false
+ 0
7965
@@ -63726,6 +71691,7 @@
false
false
false
+ 0
7966
@@ -63734,6 +71700,7 @@
false
false
false
+ 0
7967
@@ -63742,6 +71709,7 @@
false
false
false
+ 0
7968
@@ -63750,6 +71718,7 @@
false
false
false
+ 0
7969
@@ -63758,6 +71727,7 @@
false
false
false
+ 0
7970
@@ -63766,6 +71736,7 @@
false
false
false
+ 0
7971
@@ -63774,6 +71745,7 @@
false
false
false
+ 0
7972
@@ -63782,6 +71754,7 @@
false
false
false
+ 0
7973
@@ -63790,6 +71763,7 @@
false
false
false
+ 0
7974
@@ -63798,6 +71772,7 @@
false
false
false
+ 0
7975
@@ -63806,6 +71781,7 @@
false
false
false
+ 0
7976
@@ -63814,6 +71790,7 @@
false
false
false
+ 0
7977
@@ -63822,6 +71799,7 @@
false
false
false
+ 0
7978
@@ -63830,6 +71808,7 @@
false
false
false
+ 0
7979
@@ -63838,6 +71817,7 @@
false
false
false
+ 0
7980
@@ -63846,6 +71826,7 @@
false
false
false
+ 0
7981
@@ -63854,6 +71835,7 @@
false
false
false
+ 0
7982
@@ -63862,6 +71844,7 @@
false
false
false
+ 0
7983
@@ -63870,6 +71853,7 @@
false
false
false
+ 0
7984
@@ -63878,6 +71862,7 @@
false
false
false
+ 0
7985
@@ -63886,6 +71871,7 @@
false
false
false
+ 0
7986
@@ -63894,6 +71880,7 @@
false
false
false
+ 0
7987
@@ -63902,6 +71889,7 @@
false
false
false
+ 0
7988
@@ -63910,6 +71898,7 @@
false
false
false
+ 0
7989
@@ -63918,6 +71907,7 @@
false
false
false
+ 0
7990
@@ -63926,6 +71916,7 @@
false
false
false
+ 0
7991
@@ -63934,6 +71925,7 @@
false
false
false
+ 0
7992
@@ -63942,6 +71934,7 @@
false
false
false
+ 0
7993
@@ -63950,6 +71943,7 @@
false
false
false
+ 0
7994
@@ -63958,6 +71952,7 @@
false
false
false
+ 0
7995
@@ -63966,6 +71961,7 @@
false
false
false
+ 0
7996
@@ -63974,6 +71970,7 @@
false
false
false
+ 0
7997
@@ -63982,6 +71979,7 @@
false
false
false
+ 0
7998
@@ -63990,6 +71988,7 @@
false
false
false
+ 0
7999
@@ -63998,6 +71997,7 @@
false
false
false
+ 0
8000
@@ -64006,6 +72006,7 @@
false
false
false
+ 0
8001
@@ -64014,6 +72015,7 @@
false
false
false
+ 0
8002
@@ -64022,6 +72024,7 @@
false
false
false
+ 0
8003
@@ -64030,6 +72033,7 @@
false
false
false
+ 0
8004
@@ -64038,6 +72042,7 @@
false
false
false
+ 0
8005
@@ -64046,6 +72051,7 @@
false
false
false
+ 0
8006
@@ -64054,6 +72060,7 @@
false
false
false
+ 0
8007
@@ -64062,6 +72069,7 @@
false
false
false
+ 0
8008
@@ -64070,6 +72078,7 @@
false
false
false
+ 0
8009
@@ -64078,6 +72087,7 @@
false
false
false
+ 0
8010
@@ -64086,6 +72096,7 @@
false
false
false
+ 0
8011
@@ -64094,6 +72105,7 @@
false
false
false
+ 0
8012
@@ -64102,6 +72114,7 @@
false
false
false
+ 0
8013
@@ -64110,6 +72123,7 @@
false
false
false
+ 0
8014
@@ -64118,6 +72132,7 @@
false
false
false
+ 0
8015
@@ -64126,6 +72141,7 @@
false
false
false
+ 0
8016
@@ -64134,6 +72150,7 @@
false
false
false
+ 0
8017
@@ -64142,6 +72159,7 @@
false
false
false
+ 0
8018
@@ -64150,6 +72168,7 @@
false
false
false
+ 0
8019
@@ -64158,6 +72177,7 @@
false
false
false
+ 0
8020
@@ -64166,6 +72186,7 @@
false
false
false
+ 0
8021
@@ -64174,6 +72195,7 @@
false
false
false
+ 0
8022
@@ -64182,6 +72204,7 @@
false
false
false
+ 0
8023
@@ -64190,6 +72213,7 @@
false
false
false
+ 0
8024
@@ -64198,6 +72222,7 @@
false
false
false
+ 0
8025
@@ -64206,6 +72231,7 @@
false
false
false
+ 0
8026
@@ -64214,6 +72240,7 @@
false
false
false
+ 0
8027
@@ -64222,6 +72249,7 @@
false
false
false
+ 0
8028
@@ -64230,6 +72258,7 @@
false
false
false
+ 0
8029
@@ -64238,6 +72267,7 @@
false
false
false
+ 0
8030
@@ -64246,6 +72276,7 @@
false
false
false
+ 0
8031
@@ -64254,6 +72285,7 @@
false
false
false
+ 0
8032
@@ -64262,6 +72294,7 @@
false
false
false
+ 0
8033
@@ -64270,6 +72303,7 @@
false
false
false
+ 0
8034
@@ -64278,6 +72312,7 @@
false
false
false
+ 0
8035
@@ -64286,6 +72321,7 @@
false
false
false
+ 0
8036
@@ -64294,6 +72330,7 @@
false
false
false
+ 0
8037
@@ -64302,6 +72339,7 @@
false
false
false
+ 0
8038
@@ -64310,6 +72348,7 @@
false
false
false
+ 0
8039
@@ -64318,6 +72357,7 @@
false
false
false
+ 0
8040
@@ -64326,6 +72366,7 @@
false
false
false
+ 0
8041
@@ -64334,6 +72375,7 @@
false
false
false
+ 0
8042
@@ -64342,6 +72384,7 @@
false
false
false
+ 0
8043
@@ -64350,6 +72393,7 @@
false
false
false
+ 0
8044
@@ -64358,6 +72402,7 @@
false
false
false
+ 0
8045
@@ -64366,6 +72411,7 @@
false
false
false
+ 0
8046
@@ -64374,6 +72420,7 @@
false
false
false
+ 0
8047
@@ -64382,6 +72429,7 @@
false
false
false
+ 0
8048
@@ -64390,6 +72438,7 @@
false
false
false
+ 0
8049
@@ -64398,6 +72447,7 @@
false
false
false
+ 0
8050
@@ -64406,6 +72456,7 @@
false
false
false
+ 0
8051
@@ -64414,6 +72465,7 @@
false
false
false
+ 0
8052
@@ -64422,6 +72474,7 @@
false
false
false
+ 0
8053
@@ -64430,6 +72483,7 @@
false
false
false
+ 0
8054
@@ -64438,6 +72492,7 @@
false
false
false
+ 0
8055
@@ -64446,6 +72501,7 @@
false
false
false
+ 0
8056
@@ -64454,6 +72510,7 @@
false
false
false
+ 0
8057
@@ -64462,6 +72519,7 @@
false
false
false
+ 0
8058
@@ -64470,6 +72528,7 @@
false
false
false
+ 0
8059
@@ -64478,6 +72537,7 @@
false
false
false
+ 0
8060
@@ -64486,6 +72546,7 @@
false
false
false
+ 0
8061
@@ -64494,6 +72555,7 @@
false
false
false
+ 0
8062
@@ -64502,6 +72564,7 @@
false
false
false
+ 0
8063
@@ -64510,6 +72573,7 @@
false
false
false
+ 0
8064
@@ -64518,6 +72582,7 @@
false
false
false
+ 0
8065
@@ -64526,6 +72591,7 @@
false
false
false
+ 0
8066
@@ -64534,6 +72600,7 @@
false
false
false
+ 0
8067
@@ -64542,6 +72609,7 @@
false
false
false
+ 0
8068
@@ -64550,6 +72618,7 @@
false
false
false
+ 0
8069
@@ -64558,6 +72627,7 @@
false
false
false
+ 0
8070
@@ -64566,6 +72636,7 @@
false
false
false
+ 0
8071
@@ -64574,6 +72645,7 @@
false
false
false
+ 0
8072
@@ -64582,6 +72654,7 @@
false
false
false
+ 0
8073
@@ -64590,6 +72663,7 @@
false
false
false
+ 0
8074
@@ -64598,6 +72672,7 @@
false
false
false
+ 0
8075
@@ -64606,6 +72681,7 @@
false
false
false
+ 0
8076
@@ -64614,6 +72690,7 @@
false
false
false
+ 0
8077
@@ -64622,6 +72699,7 @@
false
false
false
+ 0
8078
@@ -64630,6 +72708,7 @@
false
false
false
+ 0
8079
@@ -64638,6 +72717,7 @@
false
false
false
+ 0
8080
@@ -64646,6 +72726,7 @@
false
false
false
+ 0
8081
@@ -64654,6 +72735,7 @@
false
false
false
+ 0
8082
@@ -64662,6 +72744,7 @@
false
false
false
+ 0
8083
@@ -64670,6 +72753,7 @@
false
false
false
+ 0
8084
@@ -64678,6 +72762,7 @@
false
false
false
+ 0
8085
@@ -64686,6 +72771,7 @@
false
false
false
+ 0
8086
@@ -64694,6 +72780,7 @@
false
false
false
+ 0
8087
@@ -64702,6 +72789,7 @@
false
false
false
+ 0
8088
@@ -64710,6 +72798,7 @@
false
false
false
+ 0
8089
@@ -64718,6 +72807,7 @@
false
false
false
+ 0
8090
@@ -64726,6 +72816,7 @@
false
false
false
+ 0
8091
@@ -64734,6 +72825,7 @@
false
false
false
+ 0
8092
@@ -64742,6 +72834,7 @@
false
false
false
+ 0
8093
@@ -64750,6 +72843,7 @@
false
false
false
+ 0
8094
@@ -64758,6 +72852,7 @@
false
false
false
+ 0
8095
@@ -64766,6 +72861,7 @@
false
false
false
+ 0
8096
@@ -64774,6 +72870,7 @@
false
false
false
+ 0
8097
@@ -64782,6 +72879,7 @@
false
false
false
+ 0
8098
@@ -64790,6 +72888,7 @@
false
false
false
+ 0
8099
@@ -64798,6 +72897,7 @@
false
false
false
+ 0
8100
@@ -64806,6 +72906,7 @@
false
false
false
+ 0
8101
@@ -64814,6 +72915,7 @@
false
false
false
+ 0
8102
@@ -64822,6 +72924,7 @@
false
false
false
+ 0
8103
@@ -64830,6 +72933,7 @@
false
false
false
+ 0
8104
@@ -64838,6 +72942,7 @@
false
false
false
+ 0
8105
@@ -64846,6 +72951,7 @@
false
false
false
+ 0
8106
@@ -64854,6 +72960,7 @@
false
false
false
+ 0
8107
@@ -64862,6 +72969,7 @@
false
false
false
+ 0
8108
@@ -64870,6 +72978,7 @@
false
false
false
+ 0
8109
@@ -64878,6 +72987,7 @@
false
false
false
+ 0
8110
@@ -64886,6 +72996,7 @@
false
false
false
+ 0
8111
@@ -64894,6 +73005,7 @@
false
false
false
+ 0
8112
@@ -64902,6 +73014,7 @@
false
false
false
+ 0
8113
@@ -64910,6 +73023,7 @@
false
false
false
+ 0
8114
@@ -64918,6 +73032,7 @@
false
false
false
+ 0
8115
@@ -64926,6 +73041,7 @@
false
false
false
+ 0
8116
@@ -64934,6 +73050,7 @@
false
false
false
+ 0
8117
@@ -64942,6 +73059,7 @@
false
false
false
+ 0
8118
@@ -64950,6 +73068,7 @@
false
false
false
+ 0
8119
@@ -64958,6 +73077,7 @@
false
false
false
+ 0
8120
@@ -64966,6 +73086,7 @@
false
false
false
+ 0
8121
@@ -64974,6 +73095,7 @@
false
false
false
+ 0
8122
@@ -64982,6 +73104,7 @@
false
false
false
+ 0
8123
@@ -64990,6 +73113,7 @@
false
false
false
+ 0
8124
@@ -64998,6 +73122,7 @@
false
false
false
+ 0
8125
@@ -65006,6 +73131,7 @@
false
false
false
+ 0
8126
@@ -65014,6 +73140,7 @@
false
false
false
+ 0
8127
@@ -65022,6 +73149,7 @@
false
false
false
+ 0
8128
@@ -65030,6 +73158,7 @@
false
false
false
+ 0
8129
@@ -65038,6 +73167,7 @@
false
false
false
+ 0
8130
@@ -65046,6 +73176,7 @@
false
false
false
+ 0
8131
@@ -65054,6 +73185,7 @@
false
false
false
+ 0
8132
@@ -65062,6 +73194,7 @@
false
false
false
+ 0
8133
@@ -65070,6 +73203,7 @@
false
false
false
+ 0
8134
@@ -65078,6 +73212,7 @@
false
false
false
+ 0
8135
@@ -65086,6 +73221,7 @@
false
false
false
+ 0
8136
@@ -65094,6 +73230,7 @@
false
false
false
+ 0
8137
@@ -65102,6 +73239,7 @@
false
false
false
+ 0
8138
@@ -65110,6 +73248,7 @@
false
false
false
+ 0
8139
@@ -65118,6 +73257,7 @@
false
false
false
+ 0
8140
@@ -65126,6 +73266,7 @@
false
false
false
+ 0
8141
@@ -65134,6 +73275,7 @@
false
false
false
+ 0
8142
@@ -65142,6 +73284,7 @@
false
false
false
+ 0
8143
@@ -65150,6 +73293,7 @@
false
false
false
+ 0
8144
@@ -65158,6 +73302,7 @@
false
false
false
+ 0
8145
@@ -65166,6 +73311,7 @@
false
false
false
+ 0
8146
@@ -65174,6 +73320,7 @@
false
false
false
+ 0
8147
@@ -65182,6 +73329,7 @@
false
false
false
+ 0
8148
@@ -65190,6 +73338,7 @@
false
false
false
+ 0
8149
@@ -65198,6 +73347,7 @@
false
false
false
+ 0
8150
@@ -65206,6 +73356,7 @@
false
false
false
+ 0
8151
@@ -65214,6 +73365,7 @@
false
false
false
+ 0
8152
@@ -65222,6 +73374,7 @@
false
false
false
+ 0
8153
@@ -65230,6 +73383,7 @@
false
false
false
+ 0
8154
@@ -65238,6 +73392,7 @@
false
false
false
+ 0
8155
@@ -65246,6 +73401,7 @@
false
false
false
+ 0
8156
@@ -65254,6 +73410,7 @@
false
false
false
+ 0
8157
@@ -65262,6 +73419,7 @@
false
false
false
+ 0
8158
@@ -65270,6 +73428,7 @@
false
false
false
+ 0
8159
@@ -65278,6 +73437,7 @@
false
false
false
+ 0
8160
@@ -65286,6 +73446,7 @@
false
false
false
+ 0
8161
@@ -65294,6 +73455,7 @@
false
false
false
+ 0
8162
@@ -65302,6 +73464,7 @@
false
false
false
+ 0
8163
@@ -65310,6 +73473,7 @@
false
false
false
+ 0
8164
@@ -65318,6 +73482,7 @@
false
false
false
+ 0
8165
@@ -65326,6 +73491,7 @@
false
false
false
+ 0
8166
@@ -65334,6 +73500,7 @@
false
false
false
+ 0
8167
@@ -65342,6 +73509,7 @@
false
false
false
+ 0
8168
@@ -65350,6 +73518,7 @@
false
false
false
+ 0
8169
@@ -65358,6 +73527,7 @@
false
false
false
+ 0
8170
@@ -65366,6 +73536,7 @@
false
false
false
+ 0
8171
@@ -65374,6 +73545,7 @@
false
false
false
+ 0
8172
@@ -65382,6 +73554,7 @@
false
false
false
+ 0
8173
@@ -65390,6 +73563,7 @@
false
false
false
+ 0
8174
@@ -65398,6 +73572,7 @@
false
false
false
+ 0
8175
@@ -65406,6 +73581,7 @@
false
false
false
+ 0
8176
@@ -65414,6 +73590,7 @@
false
false
false
+ 0
8177
@@ -65422,6 +73599,7 @@
false
false
false
+ 0
8178
@@ -65430,6 +73608,7 @@
false
false
false
+ 0
8179
@@ -65438,6 +73617,7 @@
false
false
false
+ 0
8180
@@ -65446,6 +73626,7 @@
false
false
false
+ 0
8181
@@ -65454,6 +73635,7 @@
false
false
false
+ 0
8182
@@ -65462,6 +73644,7 @@
false
false
false
+ 0
8183
@@ -65470,6 +73653,7 @@
false
false
false
+ 0
8184
@@ -65478,6 +73662,7 @@
false
false
false
+ 0
8185
@@ -65486,6 +73671,7 @@
false
false
false
+ 0
8186
@@ -65494,6 +73680,7 @@
false
false
false
+ 0
8187
@@ -65502,6 +73689,7 @@
false
false
false
+ 0
8188
@@ -65510,6 +73698,7 @@
false
false
false
+ 0
8189
@@ -65518,6 +73707,7 @@
false
false
false
+ 0
8190
@@ -65526,6 +73716,7 @@
false
false
false
+ 0
8191
@@ -65534,6 +73725,7 @@
false
false
false
+ 0
8192
@@ -65542,6 +73734,7 @@
false
false
false
+ 0
8193
@@ -65550,6 +73743,7 @@
false
false
false
+ 0
8194
@@ -65558,6 +73752,7 @@
false
false
false
+ 0
8195
@@ -65566,6 +73761,7 @@
false
false
false
+ 0
8196
@@ -65574,6 +73770,7 @@
false
false
false
+ 0
8197
@@ -65582,6 +73779,7 @@
false
false
false
+ 0
8198
@@ -65590,6 +73788,7 @@
false
false
false
+ 0
8199
@@ -65598,6 +73797,7 @@
false
false
false
+ 0
8200
@@ -65606,6 +73806,7 @@
false
false
false
+ 0
8201
@@ -65614,6 +73815,7 @@
false
false
false
+ 0
8202
@@ -65622,6 +73824,7 @@
false
false
false
+ 0
8203
@@ -65630,6 +73833,7 @@
false
false
false
+ 0
8204
@@ -65638,6 +73842,7 @@
false
false
false
+ 0
8205
@@ -65646,6 +73851,7 @@
false
false
false
+ 0
8206
@@ -65654,6 +73860,7 @@
false
false
false
+ 0
8207
@@ -65662,6 +73869,7 @@
false
false
false
+ 0
8208
@@ -65670,6 +73878,7 @@
false
false
false
+ 0
8209
@@ -65678,6 +73887,7 @@
false
false
false
+ 0
8210
@@ -65686,6 +73896,7 @@
false
false
false
+ 0
8211
@@ -65694,6 +73905,7 @@
false
false
false
+ 0
8212
@@ -65702,6 +73914,7 @@
false
false
false
+ 0
8213
@@ -65710,6 +73923,7 @@
false
false
false
+ 0
8214
@@ -65718,6 +73932,7 @@
false
false
false
+ 0
8215
@@ -65726,6 +73941,7 @@
false
false
false
+ 0
8216
@@ -65734,6 +73950,7 @@
false
false
false
+ 0
8217
@@ -65742,6 +73959,7 @@
false
false
false
+ 0
8218
@@ -65750,6 +73968,7 @@
false
false
false
+ 0
8219
@@ -65758,6 +73977,7 @@
false
false
false
+ 0
8220
@@ -65766,6 +73986,7 @@
false
false
false
+ 0
8221
@@ -65774,6 +73995,7 @@
false
false
false
+ 0
8222
@@ -65782,6 +74004,7 @@
false
false
false
+ 0
8223
@@ -65790,6 +74013,7 @@
false
false
false
+ 0
8224
@@ -65798,6 +74022,7 @@
false
false
false
+ 0
8225
@@ -65806,6 +74031,7 @@
false
false
false
+ 0
8226
@@ -65814,6 +74040,7 @@
false
false
false
+ 0
8227
@@ -65822,6 +74049,7 @@
false
false
false
+ 0
8228
@@ -65830,6 +74058,7 @@
false
false
false
+ 0
8229
@@ -65838,6 +74067,7 @@
false
false
false
+ 0
8230
@@ -65846,6 +74076,7 @@
false
false
false
+ 0
8231
@@ -65854,6 +74085,7 @@
false
false
false
+ 0
8232
@@ -65862,6 +74094,7 @@
false
false
false
+ 0
8233
@@ -65870,6 +74103,7 @@
false
false
false
+ 0
8234
@@ -65878,6 +74112,7 @@
false
false
false
+ 0
8235
@@ -65886,6 +74121,7 @@
false
false
false
+ 0
8236
@@ -65894,6 +74130,7 @@
false
false
false
+ 0
8237
@@ -65902,6 +74139,7 @@
false
false
false
+ 0
8238
@@ -65910,6 +74148,7 @@
false
false
false
+ 0
8239
@@ -65918,6 +74157,7 @@
false
false
false
+ 0
8240
@@ -65926,6 +74166,7 @@
false
false
false
+ 0
8241
@@ -65934,6 +74175,7 @@
false
false
false
+ 0
8242
@@ -65942,6 +74184,7 @@
false
false
false
+ 0
8243
@@ -65950,6 +74193,7 @@
false
false
false
+ 0
8244
@@ -65958,6 +74202,7 @@
false
false
false
+ 0
8245
@@ -65966,6 +74211,7 @@
false
false
false
+ 0
8246
@@ -65974,6 +74220,7 @@
false
false
false
+ 0
8247
@@ -65982,6 +74229,7 @@
false
false
false
+ 0
8248
@@ -65990,6 +74238,7 @@
false
false
false
+ 0
8249
@@ -65998,6 +74247,7 @@
false
false
false
+ 0
8250
@@ -66006,6 +74256,7 @@
false
false
false
+ 0
8251
@@ -66014,6 +74265,7 @@
false
false
false
+ 0
8252
@@ -66022,6 +74274,7 @@
false
false
false
+ 0
8253
@@ -66030,6 +74283,7 @@
false
false
false
+ 0
8254
@@ -66038,6 +74292,7 @@
false
false
false
+ 0
8255
@@ -66046,6 +74301,7 @@
false
false
false
+ 0
8256
@@ -66054,6 +74310,7 @@
false
false
false
+ 0
8257
@@ -66062,6 +74319,7 @@
false
false
false
+ 0
8258
@@ -66070,6 +74328,7 @@
false
false
false
+ 0
8259
@@ -66078,6 +74337,7 @@
false
false
false
+ 0
8260
@@ -66086,6 +74346,7 @@
false
false
false
+ 0
8261
@@ -66094,6 +74355,7 @@
false
false
false
+ 0
8262
@@ -66102,6 +74364,7 @@
false
false
false
+ 0
8263
@@ -66110,6 +74373,7 @@
false
false
false
+ 0
8264
@@ -66118,6 +74382,7 @@
false
false
false
+ 0
8265
@@ -66126,6 +74391,7 @@
false
false
false
+ 0
8266
@@ -66134,6 +74400,7 @@
false
false
false
+ 0
8267
@@ -66142,6 +74409,7 @@
false
false
false
+ 0
8268
@@ -66150,6 +74418,7 @@
false
false
false
+ 0
8269
@@ -66158,6 +74427,7 @@
false
false
false
+ 0
8270
@@ -66166,6 +74436,7 @@
false
false
false
+ 0
8271
@@ -66174,6 +74445,7 @@
false
false
false
+ 0
8272
@@ -66182,6 +74454,7 @@
false
false
false
+ 0
8273
@@ -66190,6 +74463,7 @@
false
false
false
+ 0
8274
@@ -66198,6 +74472,7 @@
false
false
false
+ 0
8275
@@ -66206,6 +74481,7 @@
false
false
false
+ 0
8276
@@ -66214,6 +74490,7 @@
false
false
false
+ 0
8277
@@ -66222,6 +74499,7 @@
false
false
false
+ 0
8278
@@ -66230,6 +74508,7 @@
false
false
false
+ 0
8279
@@ -66238,6 +74517,7 @@
false
false
false
+ 0
8280
@@ -66246,6 +74526,7 @@
false
false
false
+ 0
8281
@@ -66254,6 +74535,7 @@
false
false
false
+ 0
8282
@@ -66262,6 +74544,7 @@
false
false
false
+ 0
8283
@@ -66270,6 +74553,7 @@
false
false
false
+ 0
8284
@@ -66278,6 +74562,7 @@
false
false
false
+ 0
8285
@@ -66286,6 +74571,7 @@
false
false
false
+ 0
8286
@@ -66294,6 +74580,7 @@
false
false
false
+ 0
8287
@@ -66302,6 +74589,7 @@
false
false
false
+ 0
8288
@@ -66310,6 +74598,7 @@
false
false
false
+ 0
8289
@@ -66318,6 +74607,7 @@
false
false
false
+ 0
8290
@@ -66326,6 +74616,7 @@
false
false
false
+ 0
8291
@@ -66334,6 +74625,7 @@
false
false
false
+ 0
8292
@@ -66342,6 +74634,7 @@
false
false
false
+ 0
8293
@@ -66350,6 +74643,7 @@
false
false
false
+ 0
8294
@@ -66358,6 +74652,7 @@
false
false
false
+ 0
8295
@@ -66366,6 +74661,7 @@
false
false
false
+ 0
8296
@@ -66374,6 +74670,7 @@
false
false
false
+ 0
8297
@@ -66382,6 +74679,7 @@
false
false
false
+ 0
8298
@@ -66390,6 +74688,7 @@
false
false
false
+ 0
8299
@@ -66398,6 +74697,7 @@
false
false
false
+ 0
8300
@@ -66406,6 +74706,7 @@
false
false
false
+ 0
8301
@@ -66414,6 +74715,7 @@
false
false
false
+ 0
8302
@@ -66422,6 +74724,7 @@
false
false
false
+ 0
8303
@@ -66430,6 +74733,7 @@
false
false
false
+ 0
8304
@@ -66438,6 +74742,7 @@
false
false
false
+ 0
8305
@@ -66446,6 +74751,7 @@
false
false
false
+ 0
8306
@@ -66454,6 +74760,7 @@
false
false
false
+ 0
8307
@@ -66462,6 +74769,7 @@
false
false
false
+ 0
8308
@@ -66470,6 +74778,7 @@
false
false
false
+ 0
8309
@@ -66478,6 +74787,7 @@
false
false
false
+ 0
8310
@@ -66486,6 +74796,7 @@
false
false
false
+ 0
8311
@@ -66494,6 +74805,7 @@
false
false
false
+ 0
8312
@@ -66502,6 +74814,7 @@
false
false
false
+ 0
8313
@@ -66510,6 +74823,7 @@
false
false
false
+ 0
8314
@@ -66518,6 +74832,7 @@
false
false
false
+ 0
8315
@@ -66526,6 +74841,7 @@
false
false
false
+ 0
8316
@@ -66534,6 +74850,7 @@
false
false
false
+ 0
8317
@@ -66542,6 +74859,7 @@
false
false
false
+ 0
8318
@@ -66550,6 +74868,7 @@
false
false
false
+ 0
8319
@@ -66558,6 +74877,7 @@
false
false
false
+ 0
8320
@@ -66566,6 +74886,7 @@
false
false
false
+ 0
8321
@@ -66574,6 +74895,7 @@
false
false
false
+ 0
8322
@@ -66582,6 +74904,7 @@
false
false
false
+ 0
8323
@@ -66590,6 +74913,7 @@
false
false
false
+ 0
8324
@@ -66598,6 +74922,7 @@
false
false
false
+ 0
8325
@@ -66606,6 +74931,7 @@
false
false
false
+ 0
8326
@@ -66614,6 +74940,7 @@
false
false
false
+ 0
8327
@@ -66622,6 +74949,7 @@
false
false
false
+ 0
8328
@@ -66630,6 +74958,7 @@
false
false
false
+ 0
8329
@@ -66638,6 +74967,7 @@
false
false
false
+ 0
8330
@@ -66646,6 +74976,7 @@
false
false
false
+ 0
8331
@@ -66654,6 +74985,7 @@
false
false
false
+ 0
8332
@@ -66662,6 +74994,7 @@
false
false
false
+ 0
8333
@@ -66670,6 +75003,7 @@
false
false
false
+ 0
8334
@@ -66678,6 +75012,7 @@
false
false
false
+ 0
8335
@@ -66686,6 +75021,7 @@
false
false
false
+ 0
8336
@@ -66694,6 +75030,7 @@
false
false
false
+ 0
8337
@@ -66702,6 +75039,7 @@
false
false
false
+ 0
8338
@@ -66710,6 +75048,7 @@
false
false
false
+ 0
8339
@@ -66718,6 +75057,7 @@
false
false
false
+ 0
8340
@@ -66726,6 +75066,7 @@
false
false
false
+ 0
8341
@@ -66734,6 +75075,7 @@
false
false
false
+ 0
8342
@@ -66742,6 +75084,7 @@
false
false
false
+ 0
8343
@@ -66750,6 +75093,7 @@
false
false
false
+ 0
8344
@@ -66758,6 +75102,7 @@
false
false
false
+ 0
8345
@@ -66766,6 +75111,7 @@
false
false
false
+ 0
8346
@@ -66774,6 +75120,7 @@
false
false
false
+ 0
8347
@@ -66782,6 +75129,7 @@
false
false
false
+ 0
8348
@@ -66790,6 +75138,7 @@
false
false
false
+ 0
8349
@@ -66798,6 +75147,7 @@
false
false
false
+ 0
8350
@@ -66806,6 +75156,7 @@
false
false
false
+ 0
8351
@@ -66814,6 +75165,7 @@
false
false
false
+ 0
8352
@@ -66822,6 +75174,7 @@
false
false
false
+ 0
8353
@@ -66830,6 +75183,7 @@
false
false
false
+ 0
8354
@@ -66838,6 +75192,7 @@
false
false
false
+ 0
8355
@@ -66846,6 +75201,7 @@
false
false
false
+ 0
8356
@@ -66854,6 +75210,7 @@
false
false
false
+ 0
8357
@@ -66862,6 +75219,7 @@
false
false
false
+ 0
8358
@@ -66870,6 +75228,7 @@
false
false
false
+ 0
8359
@@ -66878,6 +75237,7 @@
false
false
false
+ 0
8360
@@ -66886,6 +75246,7 @@
false
false
false
+ 0
8361
@@ -66894,6 +75255,7 @@
false
false
false
+ 0
8362
@@ -66902,6 +75264,7 @@
false
false
false
+ 0
8363
@@ -66910,6 +75273,7 @@
false
false
false
+ 0
8364
@@ -66918,6 +75282,7 @@
false
false
false
+ 0
8365
@@ -66926,6 +75291,7 @@
false
false
false
+ 0
8366
@@ -66934,6 +75300,7 @@
false
false
false
+ 0
8367
@@ -66942,6 +75309,7 @@
false
false
false
+ 0
8368
@@ -66950,6 +75318,7 @@
false
false
false
+ 0
8369
@@ -66958,6 +75327,7 @@
false
false
false
+ 0
8370
@@ -66966,6 +75336,7 @@
false
false
false
+ 0
8371
@@ -66974,6 +75345,7 @@
false
false
false
+ 0
8372
@@ -66982,6 +75354,7 @@
false
false
false
+ 0
8373
@@ -66990,6 +75363,7 @@
false
false
false
+ 0
8374
@@ -66998,6 +75372,7 @@
false
false
false
+ 0
8375
@@ -67006,6 +75381,7 @@
false
false
false
+ 0
8376
@@ -67014,6 +75390,7 @@
false
false
false
+ 0
8377
@@ -67022,6 +75399,7 @@
false
false
false
+ 0
8378
@@ -67030,6 +75408,7 @@
false
false
false
+ 0
8379
@@ -67038,6 +75417,7 @@
false
false
false
+ 0
8380
@@ -67046,6 +75426,7 @@
false
false
false
+ 0
8381
@@ -67054,6 +75435,7 @@
false
false
false
+ 0
8382
@@ -67062,6 +75444,7 @@
false
false
false
+ 0
8383
@@ -67070,6 +75453,7 @@
false
false
false
+ 0
8384
@@ -67078,6 +75462,7 @@
false
false
false
+ 0
8385
@@ -67086,6 +75471,7 @@
false
false
false
+ 0
8386
@@ -67094,6 +75480,7 @@
false
false
false
+ 0
8387
@@ -67102,6 +75489,7 @@
false
false
false
+ 0
8388
@@ -67110,6 +75498,7 @@
false
false
false
+ 0
8389
@@ -67118,6 +75507,7 @@
false
false
false
+ 0
8390
@@ -67126,6 +75516,7 @@
false
false
false
+ 0
8391
@@ -67134,6 +75525,7 @@
false
false
false
+ 0
8392
@@ -67142,6 +75534,7 @@
false
false
false
+ 0
8393
@@ -67150,6 +75543,7 @@
false
false
false
+ 0
8394
@@ -67158,6 +75552,7 @@
false
false
false
+ 0
8395
@@ -67166,6 +75561,7 @@
false
false
false
+ 0
8396
@@ -67174,6 +75570,7 @@
false
false
false
+ 0
8397
@@ -67182,6 +75579,7 @@
false
false
false
+ 0
8398
@@ -67190,6 +75588,7 @@
false
false
false
+ 0
8399
@@ -67198,6 +75597,7 @@
false
false
false
+ 0
8400
@@ -67206,6 +75606,7 @@
false
false
false
+ 0
8401
@@ -67214,6 +75615,7 @@
false
false
false
+ 0
8402
@@ -67222,6 +75624,7 @@
false
false
false
+ 0
8403
@@ -67230,6 +75633,7 @@
false
false
false
+ 0
8404
@@ -67238,6 +75642,7 @@
false
false
false
+ 0
8405
@@ -67246,6 +75651,7 @@
false
false
false
+ 0
8406
@@ -67254,6 +75660,7 @@
false
false
false
+ 0
8407
@@ -67262,6 +75669,7 @@
false
false
false
+ 0
8408
@@ -67270,6 +75678,7 @@
false
false
false
+ 0
8409
@@ -67278,6 +75687,7 @@
false
false
false
+ 0
8410
@@ -67286,6 +75696,7 @@
false
false
false
+ 0
8411
@@ -67294,6 +75705,7 @@
false
false
false
+ 0
8412
@@ -67302,6 +75714,7 @@
false
false
false
+ 0
8413
@@ -67310,6 +75723,7 @@
false
false
false
+ 0
8414
@@ -67318,6 +75732,7 @@
false
false
false
+ 0
8415
@@ -67326,6 +75741,7 @@
false
false
false
+ 0
8416
@@ -67334,6 +75750,7 @@
false
false
false
+ 0
8417
@@ -67342,6 +75759,7 @@
false
false
false
+ 0
8418
@@ -67350,6 +75768,7 @@
false
false
false
+ 0
8419
@@ -67358,6 +75777,7 @@
false
false
false
+ 0
8420
@@ -67366,6 +75786,7 @@
false
false
false
+ 0
8421
@@ -67374,6 +75795,7 @@
false
false
false
+ 0
8422
@@ -67382,6 +75804,7 @@
false
false
false
+ 0
8423
@@ -67390,6 +75813,7 @@
false
false
false
+ 0
8424
@@ -67398,6 +75822,7 @@
false
false
false
+ 0
8425
@@ -67406,6 +75831,7 @@
false
false
false
+ 0
8426
@@ -67414,6 +75840,7 @@
false
false
false
+ 0
8427
@@ -67422,6 +75849,7 @@
false
false
false
+ 0
8428
@@ -67430,6 +75858,7 @@
false
false
false
+ 0
8429
@@ -67438,6 +75867,7 @@
false
false
false
+ 0
8430
@@ -67446,6 +75876,7 @@
false
false
false
+ 0
8431
@@ -67454,6 +75885,7 @@
false
false
false
+ 0
8432
@@ -67462,6 +75894,7 @@
false
false
false
+ 0
8433
@@ -67470,6 +75903,7 @@
false
false
false
+ 0
8434
@@ -67478,6 +75912,7 @@
false
false
false
+ 0
8435
@@ -67486,6 +75921,7 @@
false
false
false
+ 0
8436
@@ -67494,6 +75930,7 @@
false
false
false
+ 0
8437
@@ -67502,6 +75939,7 @@
false
false
false
+ 0
8438
@@ -67510,6 +75948,7 @@
false
false
false
+ 0
8439
@@ -67518,6 +75957,7 @@
false
false
false
+ 0
8440
@@ -67526,6 +75966,7 @@
false
false
false
+ 0
8441
@@ -67534,6 +75975,7 @@
false
false
false
+ 0
8442
@@ -67542,6 +75984,7 @@
false
false
false
+ 0
8443
@@ -67550,6 +75993,7 @@
false
false
false
+ 0
8444
@@ -67558,6 +76002,7 @@
false
false
false
+ 0
8445
@@ -67566,6 +76011,7 @@
false
false
false
+ 0
8446
@@ -67574,6 +76020,7 @@
false
false
false
+ 0
8447
@@ -67582,6 +76029,7 @@
false
false
false
+ 0
8448
@@ -67590,6 +76038,7 @@
false
false
false
+ 0
8449
@@ -67598,6 +76047,7 @@
false
false
false
+ 0
8450
@@ -67606,6 +76056,7 @@
false
false
false
+ 0
8451
@@ -67614,6 +76065,7 @@
false
false
false
+ 0
8452
@@ -67622,6 +76074,7 @@
false
false
false
+ 0
8453
@@ -67630,6 +76083,7 @@
false
false
false
+ 0
8454
@@ -67638,6 +76092,7 @@
false
false
false
+ 0
8455
@@ -67646,6 +76101,7 @@
false
false
false
+ 0
8456
@@ -67654,6 +76110,7 @@
false
false
false
+ 0
8457
@@ -67662,6 +76119,7 @@
false
false
false
+ 0
8458
@@ -67670,6 +76128,7 @@
false
false
false
+ 0
8459
@@ -67678,6 +76137,7 @@
false
false
false
+ 0
8460
@@ -67686,6 +76146,7 @@
false
false
false
+ 0
8461
@@ -67694,6 +76155,7 @@
false
false
false
+ 0
8462
@@ -67702,6 +76164,7 @@
false
false
false
+ 0
8463
@@ -67710,6 +76173,7 @@
false
false
false
+ 0
8464
@@ -67718,6 +76182,7 @@
false
false
false
+ 0
8465
@@ -67726,6 +76191,7 @@
false
false
false
+ 0
8466
@@ -67734,6 +76200,7 @@
false
false
false
+ 0
8467
@@ -67742,6 +76209,7 @@
false
false
false
+ 0
8468
@@ -67750,6 +76218,7 @@
false
false
false
+ 0
8469
@@ -67758,6 +76227,7 @@
false
false
false
+ 0
8470
@@ -67766,6 +76236,7 @@
false
false
false
+ 0
8471
@@ -67774,6 +76245,7 @@
false
false
false
+ 0
8472
@@ -67782,6 +76254,7 @@
false
false
false
+ 0
8473
@@ -67790,6 +76263,7 @@
false
false
false
+ 0
8474
@@ -67798,6 +76272,7 @@
false
false
false
+ 0
8475
@@ -67806,6 +76281,7 @@
false
false
false
+ 0
8476
@@ -67814,6 +76290,7 @@
false
false
false
+ 0
8477
@@ -67822,6 +76299,7 @@
false
false
false
+ 0
8478
@@ -67830,6 +76308,7 @@
false
false
false
+ 0
8479
@@ -67838,6 +76317,7 @@
false
false
false
+ 0
8480
@@ -67846,6 +76326,7 @@
false
false
false
+ 0
8481
@@ -67854,6 +76335,7 @@
false
false
false
+ 0
8482
@@ -67862,6 +76344,7 @@
false
false
false
+ 0
8483
@@ -67870,6 +76353,7 @@
false
false
false
+ 0
8484
@@ -67878,6 +76362,7 @@
false
false
false
+ 0
8485
@@ -67886,6 +76371,7 @@
false
false
false
+ 0
8486
@@ -67894,6 +76380,7 @@
false
false
false
+ 0
8487
@@ -67902,6 +76389,7 @@
false
false
false
+ 0
8488
@@ -67910,6 +76398,7 @@
false
false
false
+ 0
8489
@@ -67918,6 +76407,7 @@
false
false
false
+ 0
8490
@@ -67926,6 +76416,7 @@
false
false
false
+ 0
8491
@@ -67934,6 +76425,7 @@
false
false
false
+ 0
8492
@@ -67942,6 +76434,7 @@
false
false
false
+ 0
8493
@@ -67950,6 +76443,7 @@
false
false
false
+ 0
8494
@@ -67958,6 +76452,7 @@
false
false
false
+ 0
8495
@@ -67966,6 +76461,7 @@
false
false
false
+ 0
8496
@@ -67974,6 +76470,7 @@
false
false
false
+ 0
8497
@@ -67982,6 +76479,7 @@
false
false
false
+ 0
8498
@@ -67990,6 +76488,7 @@
false
false
false
+ 0
8499
@@ -67998,6 +76497,7 @@
false
false
false
+ 0
8500
@@ -68006,6 +76506,7 @@
false
false
false
+ 0
8501
@@ -68014,6 +76515,7 @@
false
false
false
+ 0
8502
@@ -68022,6 +76524,7 @@
false
false
false
+ 0
8503
@@ -68030,6 +76533,7 @@
false
false
false
+ 0
8504
@@ -68038,6 +76542,7 @@
false
false
false
+ 0
8505
@@ -68046,6 +76551,7 @@
false
false
false
+ 0
8506
@@ -68054,6 +76560,7 @@
false
false
false
+ 0
8507
@@ -68062,6 +76569,7 @@
false
false
false
+ 0
8508
@@ -68070,6 +76578,7 @@
false
false
false
+ 0
8509
@@ -68078,6 +76587,7 @@
false
false
false
+ 0
8510
@@ -68086,6 +76596,7 @@
false
false
false
+ 0
8511
@@ -68094,6 +76605,7 @@
false
false
false
+ 0
8512
@@ -68102,6 +76614,7 @@
false
false
false
+ 0
8513
@@ -68110,6 +76623,7 @@
false
false
false
+ 0
8514
@@ -68118,6 +76632,7 @@
false
false
false
+ 0
8515
@@ -68126,6 +76641,7 @@
false
false
false
+ 0
8516
@@ -68134,6 +76650,7 @@
false
false
false
+ 0
8517
@@ -68142,6 +76659,7 @@
false
false
false
+ 0
8518
@@ -68150,6 +76668,7 @@
false
false
false
+ 0
8519
@@ -68158,6 +76677,7 @@
false
false
false
+ 0
8520
@@ -68166,6 +76686,7 @@
false
false
false
+ 0
8521
@@ -68174,6 +76695,7 @@
false
false
false
+ 0
8522
@@ -68182,6 +76704,7 @@
false
false
false
+ 0
8523
@@ -68190,6 +76713,7 @@
false
false
false
+ 0
8524
@@ -68198,6 +76722,7 @@
false
false
false
+ 0
8525
@@ -68206,6 +76731,7 @@
false
false
false
+ 0
8526
@@ -68214,6 +76740,7 @@
false
false
false
+ 0
8527
@@ -68222,6 +76749,7 @@
false
false
false
+ 0
8528
@@ -68230,6 +76758,7 @@
false
false
false
+ 0
8529
@@ -68238,6 +76767,7 @@
false
false
false
+ 0
8530
@@ -68246,6 +76776,7 @@
false
false
false
+ 0
8531
@@ -68254,6 +76785,7 @@
false
false
false
+ 0
8532
@@ -68262,6 +76794,7 @@
false
false
false
+ 0
8533
@@ -68270,6 +76803,7 @@
false
false
false
+ 0
8534
@@ -68278,6 +76812,7 @@
false
false
false
+ 0
8535
@@ -68286,6 +76821,7 @@
false
false
false
+ 0
8536
@@ -68294,6 +76830,7 @@
false
false
false
+ 0
8537
@@ -68302,6 +76839,7 @@
false
false
false
+ 0
8538
@@ -68310,6 +76848,7 @@
false
false
false
+ 0
8539
@@ -68318,6 +76857,7 @@
false
false
false
+ 0
8540
@@ -68326,6 +76866,7 @@
false
false
false
+ 0
8541
@@ -68334,6 +76875,7 @@
false
false
false
+ 0
8542
@@ -68342,6 +76884,7 @@
false
false
false
+ 0
8543
@@ -68350,6 +76893,7 @@
false
false
false
+ 0
8544
@@ -68358,6 +76902,7 @@
false
false
false
+ 0
8545
@@ -68366,6 +76911,7 @@
false
false
false
+ 0
8546
@@ -68374,6 +76920,7 @@
false
false
false
+ 0
8547
@@ -68382,6 +76929,7 @@
false
false
false
+ 0
8548
@@ -68390,6 +76938,7 @@
false
false
false
+ 0
8549
@@ -68398,6 +76947,7 @@
false
false
false
+ 0
8550
@@ -68406,6 +76956,7 @@
false
false
false
+ 0
8551
@@ -68414,6 +76965,7 @@
false
false
false
+ 0
8552
@@ -68422,6 +76974,7 @@
false
false
false
+ 0
8553
@@ -68430,6 +76983,7 @@
false
false
false
+ 0
8554
@@ -68438,6 +76992,7 @@
false
false
false
+ 0
8555
@@ -68446,6 +77001,7 @@
false
false
false
+ 0
8556
@@ -68454,6 +77010,7 @@
false
false
false
+ 0
8557
@@ -68462,6 +77019,7 @@
false
false
false
+ 0
8558
@@ -68470,6 +77028,7 @@
false
false
false
+ 0
8559
@@ -68478,6 +77037,7 @@
false
false
false
+ 0
8560
@@ -68486,6 +77046,7 @@
false
false
false
+ 0
8561
@@ -68494,6 +77055,7 @@
false
false
false
+ 0
8562
@@ -68502,6 +77064,7 @@
false
false
false
+ 0
8563
@@ -68510,6 +77073,7 @@
false
false
false
+ 0
8564
@@ -68518,6 +77082,7 @@
false
false
false
+ 0
8565
@@ -68526,6 +77091,7 @@
false
false
false
+ 0
8566
@@ -68534,6 +77100,7 @@
false
false
false
+ 0
8567
@@ -68542,6 +77109,7 @@
false
false
false
+ 0
8568
@@ -68550,6 +77118,7 @@
false
false
false
+ 0
8569
@@ -68558,6 +77127,7 @@
false
false
false
+ 0
8570
@@ -68566,6 +77136,7 @@
false
false
false
+ 0
8571
@@ -68574,6 +77145,7 @@
false
false
false
+ 0
8572
@@ -68582,6 +77154,7 @@
false
false
false
+ 0
8573
@@ -68590,6 +77163,7 @@
false
false
false
+ 0
8574
@@ -68598,6 +77172,7 @@
false
false
false
+ 0
8575
@@ -68606,6 +77181,7 @@
false
false
false
+ 0
8576
@@ -68614,6 +77190,7 @@
false
false
false
+ 0
8577
@@ -68622,6 +77199,7 @@
false
false
false
+ 0
8578
@@ -68630,6 +77208,7 @@
false
false
false
+ 0
8579
@@ -68638,6 +77217,7 @@
false
false
false
+ 0
8580
@@ -68646,6 +77226,7 @@
false
false
false
+ 0
8581
@@ -68654,6 +77235,7 @@
false
false
false
+ 0
8582
@@ -68662,6 +77244,7 @@
false
false
false
+ 0
8583
@@ -68670,6 +77253,7 @@
false
false
false
+ 0
8584
@@ -68678,6 +77262,7 @@
false
false
false
+ 0
8585
@@ -68686,6 +77271,7 @@
false
false
false
+ 0
8586
@@ -68694,6 +77280,7 @@
false
false
false
+ 0
8587
@@ -68702,6 +77289,7 @@
false
false
false
+ 0
8588
@@ -68710,6 +77298,7 @@
false
false
false
+ 0
8589
@@ -68718,6 +77307,7 @@
false
false
false
+ 0
8590
@@ -68726,6 +77316,7 @@
false
false
false
+ 0
8591
@@ -68734,6 +77325,7 @@
false
false
false
+ 0
8592
@@ -68742,6 +77334,7 @@
false
false
false
+ 0
8593
@@ -68750,6 +77343,7 @@
false
false
false
+ 0
8594
@@ -68758,6 +77352,7 @@
false
false
false
+ 0
8595
@@ -68766,6 +77361,7 @@
false
false
false
+ 0
8596
@@ -68774,6 +77370,7 @@
false
false
false
+ 0
8597
@@ -68782,6 +77379,7 @@
false
false
false
+ 0
8598
@@ -68790,6 +77388,7 @@
false
false
false
+ 0
8599
@@ -68798,6 +77397,7 @@
false
false
false
+ 0
8600
@@ -68806,6 +77406,7 @@
false
false
false
+ 0
8601
@@ -68814,6 +77415,7 @@
false
false
false
+ 0
8602
@@ -68822,6 +77424,7 @@
false
false
false
+ 0
8603
@@ -68830,6 +77433,7 @@
false
false
false
+ 0
8604
@@ -68838,6 +77442,7 @@
false
false
false
+ 0
8605
@@ -68846,6 +77451,7 @@
false
false
false
+ 0
8606
@@ -68854,6 +77460,7 @@
false
false
false
+ 0
8607
@@ -68862,6 +77469,7 @@
false
false
false
+ 0
8608
@@ -68870,6 +77478,7 @@
false
false
false
+ 0
8609
@@ -68878,6 +77487,7 @@
false
false
false
+ 0
8610
@@ -68886,6 +77496,7 @@
false
false
false
+ 0
8611
@@ -68894,6 +77505,7 @@
false
false
false
+ 0
8612
@@ -68902,6 +77514,7 @@
false
false
false
+ 0
8613
@@ -68910,6 +77523,7 @@
false
false
false
+ 0
8614
@@ -68918,6 +77532,7 @@
false
false
false
+ 0
8615
@@ -68926,6 +77541,7 @@
false
false
false
+ 0
8616
@@ -68934,6 +77550,7 @@
false
false
false
+ 0
8617
@@ -68942,6 +77559,7 @@
false
false
false
+ 0
8618
@@ -68950,6 +77568,7 @@
false
false
false
+ 0
8619
@@ -68958,6 +77577,7 @@
false
false
false
+ 0
8620
@@ -68966,6 +77586,7 @@
false
false
false
+ 0
8621
@@ -68974,6 +77595,7 @@
false
false
false
+ 0
8622
@@ -68982,6 +77604,7 @@
false
false
false
+ 0
8623
@@ -68990,6 +77613,7 @@
false
false
false
+ 0
8624
@@ -68998,6 +77622,7 @@
false
false
false
+ 0
8625
@@ -69006,6 +77631,7 @@
false
false
false
+ 0
8626
@@ -69014,6 +77640,7 @@
false
false
false
+ 0
8627
@@ -69022,6 +77649,7 @@
false
false
false
+ 0
8628
@@ -69030,6 +77658,7 @@
false
false
false
+ 0
8629
@@ -69038,6 +77667,7 @@
false
false
false
+ 0
8630
@@ -69046,6 +77676,7 @@
false
false
false
+ 0
8631
@@ -69054,6 +77685,7 @@
false
false
false
+ 0
8632
@@ -69062,6 +77694,7 @@
false
false
false
+ 0
8633
@@ -69070,6 +77703,7 @@
false
false
false
+ 0
8634
@@ -69078,6 +77712,7 @@
false
false
false
+ 0
8635
@@ -69086,6 +77721,7 @@
false
false
false
+ 0
8636
@@ -69094,6 +77730,7 @@
false
false
false
+ 0
8637
@@ -69102,6 +77739,7 @@
false
false
false
+ 0
8638
@@ -69110,6 +77748,7 @@
false
false
false
+ 0
8639
@@ -69118,6 +77757,7 @@
false
false
false
+ 0
8640
@@ -69126,6 +77766,7 @@
false
false
false
+ 0
8641
@@ -69134,6 +77775,7 @@
false
false
false
+ 0
8642
@@ -69142,6 +77784,7 @@
false
false
false
+ 0
8643
@@ -69150,6 +77793,7 @@
false
false
false
+ 0
8644
@@ -69158,6 +77802,7 @@
false
false
false
+ 0
8645
@@ -69166,6 +77811,7 @@
false
false
false
+ 0
8646
@@ -69174,6 +77820,7 @@
false
false
false
+ 0
8647
@@ -69182,6 +77829,7 @@
false
false
false
+ 0
8648
@@ -69190,6 +77838,7 @@
false
false
false
+ 0
8649
@@ -69198,6 +77847,7 @@
false
false
false
+ 0
8650
@@ -69206,6 +77856,7 @@
false
false
false
+ 0
8651
@@ -69214,6 +77865,7 @@
false
false
false
+ 0
8652
@@ -69222,6 +77874,7 @@
false
false
false
+ 0
8653
@@ -69230,6 +77883,7 @@
false
false
false
+ 0
8654
@@ -69238,6 +77892,7 @@
false
false
false
+ 0
8655
@@ -69246,6 +77901,7 @@
false
false
false
+ 0
8656
@@ -69254,6 +77910,7 @@
false
false
false
+ 0
8657
@@ -69262,6 +77919,7 @@
false
false
false
+ 0
8658
@@ -69270,6 +77928,7 @@
false
false
false
+ 0
8659
@@ -69278,6 +77937,7 @@
false
false
false
+ 0
8660
@@ -69286,6 +77946,7 @@
false
false
false
+ 0
8661
@@ -69294,6 +77955,7 @@
false
false
false
+ 0
8662
@@ -69302,6 +77964,7 @@
false
false
false
+ 0
8663
@@ -69310,6 +77973,7 @@
false
false
false
+ 0
8664
@@ -69318,6 +77982,7 @@
false
false
false
+ 0
8665
@@ -69326,6 +77991,7 @@
false
false
false
+ 0
8666
@@ -69334,6 +78000,7 @@
false
false
false
+ 0
8667
@@ -69342,6 +78009,7 @@
false
false
false
+ 0
8668
@@ -69350,6 +78018,7 @@
false
false
false
+ 0
8669
@@ -69358,6 +78027,7 @@
false
false
false
+ 0
8670
@@ -69366,6 +78036,7 @@
false
false
false
+ 0
8671
@@ -69374,6 +78045,7 @@
false
false
false
+ 0
8672
@@ -69382,6 +78054,7 @@
false
false
false
+ 0
8673
@@ -69390,6 +78063,7 @@
false
false
false
+ 0
8674
@@ -69398,6 +78072,7 @@
false
false
false
+ 0
8675
@@ -69406,6 +78081,7 @@
false
false
false
+ 0
8676
@@ -69414,6 +78090,7 @@
false
false
false
+ 0
8677
@@ -69422,6 +78099,7 @@
false
false
false
+ 0
8678
@@ -69430,6 +78108,7 @@
false
false
false
+ 0
8679
@@ -69438,6 +78117,7 @@
false
false
false
+ 0
8680
@@ -69446,6 +78126,7 @@
false
false
false
+ 0
8681
@@ -69454,6 +78135,7 @@
false
false
false
+ 0
8682
@@ -69462,6 +78144,7 @@
false
false
false
+ 0
8683
@@ -69470,6 +78153,7 @@
false
false
false
+ 0
8684
@@ -69478,6 +78162,7 @@
false
false
false
+ 0
8685
@@ -69486,6 +78171,7 @@
false
false
false
+ 0
8686
@@ -69494,6 +78180,7 @@
false
false
false
+ 0
8687
@@ -69502,6 +78189,7 @@
false
false
false
+ 0
8688
@@ -69510,6 +78198,7 @@
false
false
false
+ 0
8689
@@ -69518,6 +78207,7 @@
false
false
false
+ 0
8690
@@ -69526,6 +78216,7 @@
false
false
false
+ 0
8691
@@ -69534,6 +78225,7 @@
false
false
false
+ 0
8692
@@ -69542,6 +78234,7 @@
false
false
false
+ 0
8693
@@ -69550,6 +78243,7 @@
false
false
false
+ 0
8694
@@ -69558,6 +78252,7 @@
false
false
false
+ 0
8695
@@ -69566,6 +78261,7 @@
false
false
false
+ 0
8696
@@ -69574,6 +78270,7 @@
false
false
false
+ 0
8697
@@ -69582,6 +78279,7 @@
false
false
false
+ 0
8698
@@ -69590,6 +78288,7 @@
false
false
false
+ 0
8699
@@ -69598,6 +78297,7 @@
false
false
false
+ 0
8700
@@ -69606,6 +78306,7 @@
false
false
false
+ 0
8701
@@ -69614,6 +78315,7 @@
false
false
false
+ 0
8702
@@ -69622,6 +78324,7 @@
false
false
false
+ 0
8703
@@ -69630,6 +78333,7 @@
false
false
false
+ 0
8704
@@ -69638,6 +78342,7 @@
false
false
false
+ 0
8705
@@ -69646,6 +78351,7 @@
false
false
false
+ 0
8706
@@ -69654,6 +78360,7 @@
false
false
false
+ 0
8707
@@ -69662,6 +78369,7 @@
false
false
false
+ 0
8708
@@ -69670,6 +78378,7 @@
false
false
false
+ 0
8709
@@ -69678,6 +78387,7 @@
false
false
false
+ 0
8710
@@ -69686,6 +78396,7 @@
false
false
false
+ 0
8711
@@ -69694,6 +78405,7 @@
false
false
false
+ 0
8712
@@ -69702,6 +78414,7 @@
false
false
false
+ 0
8713
@@ -69710,6 +78423,7 @@
false
false
false
+ 0
8714
@@ -69718,6 +78432,7 @@
false
false
false
+ 0
8715
@@ -69726,6 +78441,7 @@
false
false
false
+ 0
8716
@@ -69734,6 +78450,7 @@
false
false
false
+ 0
8717
@@ -69742,6 +78459,7 @@
false
false
false
+ 0
8718
@@ -69750,6 +78468,7 @@
false
false
false
+ 0
8719
@@ -69758,6 +78477,7 @@
false
false
false
+ 0
8720
@@ -69766,6 +78486,7 @@
false
false
false
+ 0
8721
@@ -69774,6 +78495,7 @@
false
false
false
+ 0
8722
@@ -69782,6 +78504,7 @@
false
false
false
+ 0
8723
@@ -69790,6 +78513,7 @@
false
false
false
+ 0
8724
@@ -69798,6 +78522,7 @@
false
false
false
+ 0
8725
@@ -69806,6 +78531,7 @@
false
false
false
+ 0
8726
@@ -69814,6 +78540,7 @@
false
false
false
+ 0
8727
@@ -69822,6 +78549,7 @@
false
false
false
+ 0
8728
@@ -69830,6 +78558,7 @@
false
false
false
+ 0
8729
@@ -69838,6 +78567,7 @@
false
false
false
+ 0
8730
@@ -69846,6 +78576,7 @@
false
false
false
+ 0
8731
@@ -69854,6 +78585,7 @@
false
false
false
+ 0
8732
@@ -69862,6 +78594,7 @@
false
false
false
+ 0
8733
@@ -69870,6 +78603,7 @@
false
false
false
+ 0
8734
@@ -69878,6 +78612,7 @@
false
false
false
+ 0
8735
@@ -69886,6 +78621,7 @@
false
false
false
+ 0
8736
@@ -69894,6 +78630,7 @@
false
false
false
+ 0
8737
@@ -69902,6 +78639,7 @@
false
false
false
+ 0
8738
@@ -69910,6 +78648,7 @@
false
false
false
+ 0
8739
@@ -69918,6 +78657,7 @@
false
false
false
+ 0
8740
@@ -69926,6 +78666,7 @@
false
false
false
+ 0
8741
@@ -69934,6 +78675,7 @@
false
false
false
+ 0
8742
@@ -69942,6 +78684,7 @@
false
false
false
+ 0
8743
@@ -69950,6 +78693,7 @@
false
false
false
+ 0
8744
@@ -69958,6 +78702,7 @@
false
false
false
+ 0
8745
@@ -69966,6 +78711,7 @@
false
false
false
+ 0
8746
@@ -69974,6 +78720,7 @@
false
false
false
+ 0
8747
@@ -69982,6 +78729,7 @@
false
false
false
+ 0
8748
@@ -69990,6 +78738,7 @@
false
false
false
+ 0
8749
@@ -69998,6 +78747,7 @@
false
false
false
+ 0
8750
@@ -70006,6 +78756,7 @@
false
false
false
+ 0
8751
@@ -70014,6 +78765,7 @@
false
false
false
+ 0
8752
@@ -70022,6 +78774,7 @@
false
false
false
+ 0
8753
@@ -70030,6 +78783,7 @@
false
false
false
+ 0
8754
@@ -70038,6 +78792,7 @@
false
false
false
+ 0
8755
@@ -70046,6 +78801,7 @@
false
false
false
+ 0
8756
@@ -70054,6 +78810,7 @@
false
false
false
+ 0
8757
@@ -70062,6 +78819,7 @@
false
false
false
+ 0
8758
@@ -70070,6 +78828,7 @@
false
false
false
+ 0
8759
@@ -70078,6 +78837,7 @@
false
false
false
+ 0
8760
@@ -70086,6 +78846,7 @@
false
false
false
+ 0
8761
@@ -70094,6 +78855,7 @@
false
false
false
+ 0
8762
@@ -70102,6 +78864,7 @@
false
false
false
+ 0
8763
@@ -70110,6 +78873,7 @@
false
false
false
+ 0
8764
@@ -70118,6 +78882,7 @@
false
false
false
+ 0
8765
@@ -70126,6 +78891,7 @@
false
false
false
+ 0
8766
@@ -70134,6 +78900,7 @@
false
false
false
+ 0
8767
@@ -70142,6 +78909,7 @@
false
false
false
+ 0
8768
@@ -70150,6 +78918,7 @@
false
false
false
+ 0
8769
@@ -70158,6 +78927,7 @@
false
false
false
+ 0
8770
@@ -70166,6 +78936,7 @@
false
false
false
+ 0
8771
@@ -70174,6 +78945,7 @@
false
false
false
+ 0
8772
@@ -70182,6 +78954,7 @@
false
false
false
+ 0
8773
@@ -70190,6 +78963,7 @@
false
false
false
+ 0
8774
@@ -70198,6 +78972,7 @@
false
false
false
+ 0
8775
@@ -70206,6 +78981,7 @@
false
false
false
+ 0
8776
@@ -70214,6 +78990,7 @@
false
false
false
+ 0
8777
@@ -70222,6 +78999,7 @@
false
false
false
+ 0
8778
@@ -70230,6 +79008,7 @@
false
false
false
+ 0
8779
@@ -70238,6 +79017,7 @@
false
false
false
+ 0
8780
@@ -70246,6 +79026,7 @@
false
false
false
+ 0
8781
@@ -70254,6 +79035,7 @@
false
false
false
+ 0
8782
@@ -70262,6 +79044,7 @@
false
false
false
+ 0
8783
@@ -70270,6 +79053,7 @@
false
false
false
+ 0
8784
@@ -70278,6 +79062,7 @@
false
false
false
+ 0
8785
@@ -70286,6 +79071,7 @@
false
false
false
+ 0
8786
@@ -70294,6 +79080,7 @@
false
false
false
+ 0
8787
@@ -70302,6 +79089,7 @@
false
false
false
+ 0
8788
@@ -70310,6 +79098,7 @@
false
false
false
+ 0
8789
@@ -70318,6 +79107,7 @@
false
false
false
+ 0
8790
@@ -70326,6 +79116,7 @@
false
false
false
+ 0
8791
@@ -70334,6 +79125,7 @@
false
false
false
+ 0
8792
@@ -70342,6 +79134,7 @@
false
false
false
+ 0
8793
@@ -70350,6 +79143,7 @@
false
false
false
+ 0
8794
@@ -70358,6 +79152,7 @@
false
false
false
+ 0
8795
@@ -70366,6 +79161,7 @@
false
false
false
+ 0
8796
@@ -70374,6 +79170,7 @@
false
false
false
+ 0
8797
@@ -70382,6 +79179,7 @@
false
false
false
+ 0
8798
@@ -70390,6 +79188,7 @@
false
false
false
+ 0
8799
@@ -70398,6 +79197,7 @@
false
false
false
+ 0
8800
@@ -70406,6 +79206,7 @@
false
false
false
+ 0
8801
@@ -70414,6 +79215,7 @@
false
false
false
+ 0
8802
@@ -70422,6 +79224,7 @@
false
false
false
+ 0
8803
@@ -70430,6 +79233,7 @@
false
false
false
+ 0
8804
@@ -70438,6 +79242,7 @@
false
false
false
+ 0
8805
@@ -70446,6 +79251,7 @@
false
false
false
+ 0
8806
@@ -70454,6 +79260,7 @@
false
false
false
+ 0
8807
@@ -70462,6 +79269,7 @@
false
false
false
+ 0
8808
@@ -70470,6 +79278,7 @@
false
false
false
+ 0
8809
@@ -70478,6 +79287,7 @@
false
false
false
+ 0
8810
@@ -70486,6 +79296,7 @@
false
false
false
+ 0
8811
@@ -70494,6 +79305,7 @@
false
false
false
+ 0
8812
@@ -70502,6 +79314,7 @@
false
false
false
+ 0
8813
@@ -70510,6 +79323,7 @@
false
false
false
+ 0
8814
@@ -70518,6 +79332,7 @@
false
false
false
+ 0
8815
@@ -70526,6 +79341,7 @@
false
false
false
+ 0
8816
@@ -70534,6 +79350,7 @@
false
false
false
+ 0
8817
@@ -70542,6 +79359,7 @@
false
false
false
+ 0
8818
@@ -70550,6 +79368,7 @@
false
false
false
+ 0
8819
@@ -70558,6 +79377,7 @@
false
false
false
+ 0
8820
@@ -70566,6 +79386,7 @@
false
false
false
+ 0
8821
@@ -70574,6 +79395,7 @@
false
false
false
+ 0
8822
@@ -70582,6 +79404,7 @@
false
false
false
+ 0
8823
@@ -70590,6 +79413,7 @@
false
false
false
+ 0
8824
@@ -70598,6 +79422,7 @@
false
false
false
+ 0
8825
@@ -70606,6 +79431,7 @@
false
false
false
+ 0
8826
@@ -70614,6 +79440,7 @@
false
false
false
+ 0
8827
@@ -70622,6 +79449,7 @@
false
false
false
+ 0
8828
@@ -70630,6 +79458,7 @@
false
false
false
+ 0
8829
@@ -70638,6 +79467,7 @@
false
false
false
+ 0
8830
@@ -70646,6 +79476,7 @@
false
false
false
+ 0
8831
@@ -70654,6 +79485,7 @@
false
false
false
+ 0
8832
@@ -70662,6 +79494,7 @@
false
false
false
+ 0
8833
@@ -70670,6 +79503,7 @@
false
false
false
+ 0
8834
@@ -70678,6 +79512,7 @@
false
false
false
+ 0
8835
@@ -70686,6 +79521,7 @@
false
false
false
+ 0
8836
@@ -70694,6 +79530,7 @@
false
false
false
+ 0
8837
@@ -70702,6 +79539,7 @@
false
false
false
+ 0
8838
@@ -70710,6 +79548,7 @@
false
false
false
+ 0
8839
@@ -70718,6 +79557,7 @@
false
false
false
+ 0
8840
@@ -70726,6 +79566,7 @@
false
false
false
+ 0
8841
@@ -70734,6 +79575,7 @@
false
false
false
+ 0
8842
@@ -70742,6 +79584,7 @@
false
false
false
+ 0
8843
@@ -70750,6 +79593,7 @@
false
false
false
+ 0
8844
@@ -70758,6 +79602,7 @@
false
false
false
+ 0
8845
@@ -70766,6 +79611,7 @@
false
false
false
+ 0
8846
@@ -70774,6 +79620,7 @@
false
false
false
+ 0
8847
@@ -70782,6 +79629,7 @@
false
false
false
+ 0
8848
@@ -70790,6 +79638,7 @@
false
false
false
+ 0
8849
@@ -70798,6 +79647,7 @@
false
false
false
+ 0
8850
@@ -70806,6 +79656,7 @@
false
false
false
+ 0
8851
@@ -70814,6 +79665,7 @@
false
false
false
+ 0
8852
@@ -70822,6 +79674,7 @@
false
false
false
+ 0
8853
@@ -70830,6 +79683,7 @@
false
false
false
+ 0
8854
@@ -70838,6 +79692,7 @@
false
false
false
+ 0
8855
@@ -70846,6 +79701,7 @@
false
false
false
+ 0
8856
@@ -70854,6 +79710,7 @@
false
false
false
+ 0
8857
@@ -70862,6 +79719,7 @@
false
false
false
+ 0
8858
@@ -70870,6 +79728,7 @@
false
false
false
+ 0
8859
@@ -70878,6 +79737,7 @@
false
false
false
+ 0
8860
@@ -70886,6 +79746,7 @@
false
false
false
+ 0
8861
@@ -70894,6 +79755,7 @@
false
false
false
+ 0
8862
@@ -70902,6 +79764,7 @@
false
false
false
+ 0
8863
@@ -70910,6 +79773,7 @@
false
false
false
+ 0
8864
@@ -70918,6 +79782,7 @@
false
false
false
+ 0
8865
@@ -70926,6 +79791,7 @@
false
false
false
+ 0
8866
@@ -70934,6 +79800,7 @@
false
false
false
+ 0
8867
@@ -70942,6 +79809,7 @@
false
false
false
+ 0
8868
@@ -70950,6 +79818,7 @@
false
false
false
+ 0
8869
@@ -70958,6 +79827,7 @@
false
false
false
+ 0
8870
@@ -70966,6 +79836,7 @@
false
false
false
+ 0
8871
@@ -70974,6 +79845,7 @@
false
false
false
+ 0
8872
@@ -70982,6 +79854,7 @@
false
false
false
+ 0
8873
@@ -70990,6 +79863,7 @@
false
false
false
+ 0
8874
@@ -70998,6 +79872,7 @@
false
false
false
+ 0
8875
@@ -71006,6 +79881,7 @@
false
false
false
+ 0
8876
@@ -71014,6 +79890,7 @@
false
false
false
+ 0
8877
@@ -71022,6 +79899,7 @@
false
false
false
+ 0
8878
@@ -71030,6 +79908,7 @@
false
false
false
+ 0
8879
@@ -71038,6 +79917,7 @@
false
false
false
+ 0
8880
@@ -71046,6 +79926,7 @@
false
false
false
+ 0
8881
@@ -71054,6 +79935,7 @@
false
false
false
+ 0
8882
@@ -71062,6 +79944,7 @@
false
false
false
+ 0
8883
@@ -71070,6 +79953,7 @@
false
false
false
+ 0
8884
@@ -71078,6 +79962,7 @@
false
false
false
+ 0
8885
@@ -71086,6 +79971,7 @@
false
false
false
+ 0
8886
@@ -71094,6 +79980,7 @@
false
false
false
+ 0
8887
@@ -71102,6 +79989,7 @@
false
false
false
+ 0
8888
@@ -71110,6 +79998,7 @@
false
false
false
+ 0
8889
@@ -71118,6 +80007,7 @@
false
false
false
+ 0
8890
@@ -71126,6 +80016,7 @@
false
false
false
+ 0
8891
@@ -71134,6 +80025,7 @@
false
false
false
+ 0
8892
@@ -71142,6 +80034,7 @@
false
false
false
+ 0
8893
@@ -71150,6 +80043,7 @@
false
false
false
+ 0
8894
@@ -71158,6 +80052,7 @@
false
false
false
+ 0
8895
@@ -71166,6 +80061,7 @@
false
false
false
+ 0
8896
@@ -71174,6 +80070,7 @@
false
false
false
+ 0
8897
@@ -71182,6 +80079,7 @@
false
false
false
+ 0
8898
@@ -71190,6 +80088,7 @@
false
false
false
+ 0
8899
@@ -71198,6 +80097,7 @@
false
false
false
+ 0
8900
@@ -71206,6 +80106,7 @@
false
false
false
+ 0
8901
@@ -71214,6 +80115,7 @@
false
false
false
+ 0
8902
@@ -71222,6 +80124,7 @@
false
false
false
+ 0
8903
@@ -71230,6 +80133,7 @@
false
false
false
+ 0
8904
@@ -71238,6 +80142,7 @@
false
false
false
+ 0
8905
@@ -71246,6 +80151,7 @@
false
false
false
+ 0
8906
@@ -71254,6 +80160,7 @@
false
false
false
+ 0
8907
@@ -71262,6 +80169,7 @@
false
false
false
+ 0
8908
@@ -71270,6 +80178,7 @@
false
false
false
+ 0
8909
@@ -71278,6 +80187,7 @@
false
false
false
+ 0
8910
@@ -71286,6 +80196,7 @@
false
false
false
+ 0
8911
@@ -71294,6 +80205,7 @@
false
false
false
+ 0
8912
@@ -71302,6 +80214,7 @@
false
false
false
+ 0
8913
@@ -71310,6 +80223,7 @@
false
false
false
+ 0
8914
@@ -71318,6 +80232,7 @@
false
false
false
+ 0
8915
@@ -71326,6 +80241,7 @@
false
false
false
+ 0
8916
@@ -71334,6 +80250,7 @@
false
false
false
+ 0
8917
@@ -71342,6 +80259,7 @@
false
false
false
+ 0
8918
@@ -71350,6 +80268,7 @@
false
false
false
+ 0
8919
@@ -71358,6 +80277,7 @@
false
false
false
+ 0
8920
@@ -71366,6 +80286,7 @@
false
false
false
+ 0
8921
@@ -71374,6 +80295,7 @@
false
false
false
+ 0
8922
@@ -71382,6 +80304,7 @@
false
false
false
+ 0
8923
@@ -71390,6 +80313,7 @@
false
false
false
+ 0
8924
@@ -71398,6 +80322,7 @@
false
false
false
+ 0
8925
@@ -71406,6 +80331,7 @@
false
false
false
+ 0
8926
@@ -71414,6 +80340,7 @@
false
false
false
+ 0
8927
@@ -71422,6 +80349,7 @@
false
false
false
+ 0
8928
@@ -71430,6 +80358,7 @@
false
false
false
+ 0
8929
@@ -71438,6 +80367,7 @@
false
false
false
+ 0
8930
@@ -71446,6 +80376,7 @@
false
false
false
+ 0
8931
@@ -71454,6 +80385,7 @@
false
false
false
+ 0
8932
@@ -71462,6 +80394,7 @@
false
false
false
+ 0
8933
@@ -71470,6 +80403,7 @@
false
false
false
+ 0
8934
@@ -71478,6 +80412,7 @@
false
false
false
+ 0
8935
@@ -71486,6 +80421,7 @@
false
false
false
+ 0
8936
@@ -71494,6 +80430,7 @@
false
false
false
+ 0
8937
@@ -71502,6 +80439,7 @@
false
false
false
+ 0
8938
@@ -71510,6 +80448,7 @@
false
false
false
+ 0
8939
@@ -71518,6 +80457,7 @@
false
false
false
+ 0
8940
@@ -71526,6 +80466,7 @@
false
false
false
+ 0
8941
@@ -71534,6 +80475,7 @@
false
false
false
+ 0
8942
@@ -71542,6 +80484,7 @@
false
false
false
+ 0
8943
@@ -71550,6 +80493,7 @@
false
false
false
+ 0
8944
@@ -71558,6 +80502,7 @@
false
false
false
+ 0
8945
@@ -71566,6 +80511,7 @@
false
false
false
+ 0
8946
@@ -71574,6 +80520,7 @@
false
false
false
+ 0
8947
@@ -71582,6 +80529,7 @@
false
false
false
+ 0
8948
@@ -71590,6 +80538,7 @@
false
false
false
+ 0
8949
@@ -71598,6 +80547,7 @@
false
false
false
+ 0
8950
@@ -71606,6 +80556,7 @@
false
false
false
+ 0
8951
@@ -71614,6 +80565,7 @@
false
false
false
+ 0
8952
@@ -71622,6 +80574,7 @@
false
false
false
+ 0
8953
@@ -71630,6 +80583,7 @@
false
false
false
+ 0
8954
@@ -71638,6 +80592,7 @@
false
false
false
+ 0
8955
@@ -71646,6 +80601,7 @@
false
false
false
+ 0
8956
@@ -71654,6 +80610,7 @@
false
false
false
+ 0
8957
@@ -71662,6 +80619,7 @@
false
false
false
+ 0
8958
@@ -71670,6 +80628,7 @@
false
false
false
+ 0
8959
@@ -71678,6 +80637,7 @@
false
false
false
+ 0
8960
@@ -71686,6 +80646,7 @@
false
false
false
+ 0
8961
@@ -71694,6 +80655,7 @@
false
false
false
+ 0
8962
@@ -71702,6 +80664,7 @@
false
false
false
+ 0
8963
@@ -71710,6 +80673,7 @@
false
false
false
+ 0
8964
@@ -71718,6 +80682,7 @@
false
false
false
+ 0
8965
@@ -71726,6 +80691,7 @@
false
false
false
+ 0
8966
@@ -71734,6 +80700,7 @@
false
false
false
+ 0
8967
@@ -71742,6 +80709,7 @@
false
false
false
+ 0
8968
@@ -71750,6 +80718,7 @@
false
false
false
+ 0
8969
@@ -71758,6 +80727,7 @@
false
false
false
+ 0
8970
@@ -71766,6 +80736,7 @@
false
false
false
+ 0
8971
@@ -71774,6 +80745,7 @@
false
false
false
+ 0
8972
@@ -71782,6 +80754,7 @@
false
false
false
+ 0
8973
@@ -71790,6 +80763,7 @@
false
false
false
+ 0
8974
@@ -71798,6 +80772,7 @@
false
false
false
+ 0
8975
@@ -71806,6 +80781,7 @@
false
false
false
+ 0
8976
@@ -71814,6 +80790,7 @@
false
false
false
+ 0
8977
@@ -71822,6 +80799,7 @@
false
false
false
+ 0
8978
@@ -71830,6 +80808,7 @@
false
false
false
+ 0
8979
@@ -71838,6 +80817,7 @@
false
false
false
+ 0
8980
@@ -71846,6 +80826,7 @@
false
false
false
+ 0
8981
@@ -71854,6 +80835,7 @@
false
false
false
+ 0
8982
@@ -71862,6 +80844,7 @@
false
false
false
+ 0
8983
@@ -71870,6 +80853,7 @@
false
false
false
+ 0
8984
@@ -71878,6 +80862,7 @@
false
false
false
+ 0
8985
@@ -71886,6 +80871,7 @@
false
false
false
+ 0
8986
@@ -71894,6 +80880,7 @@
false
false
false
+ 0
8987
@@ -71902,6 +80889,7 @@
false
false
false
+ 0
8988
@@ -71910,6 +80898,7 @@
false
false
false
+ 0
8989
@@ -71918,6 +80907,7 @@
false
false
false
+ 0
8990
@@ -71926,6 +80916,7 @@
false
false
false
+ 0
8991
@@ -71934,6 +80925,7 @@
false
false
false
+ 0
8992
@@ -71942,6 +80934,7 @@
false
false
false
+ 0
8993
@@ -71950,6 +80943,7 @@
false
false
false
+ 0
8994
@@ -71958,6 +80952,7 @@
false
false
false
+ 0
8995
@@ -71966,6 +80961,7 @@
false
false
false
+ 0
8996
@@ -71974,6 +80970,7 @@
false
false
false
+ 0
8997
@@ -71982,6 +80979,7 @@
false
false
false
+ 0
8998
@@ -71990,6 +80988,7 @@
false
false
false
+ 0
8999
@@ -71998,6 +80997,7 @@
false
false
false
+ 0
9000
@@ -72006,6 +81006,7 @@
false
false
false
+ 0
9001
@@ -72014,6 +81015,7 @@
false
false
false
+ 0
9002
@@ -72022,6 +81024,7 @@
false
false
false
+ 0
9003
@@ -72030,6 +81033,7 @@
false
false
false
+ 0
9004
@@ -72038,6 +81042,7 @@
false
false
false
+ 0
9005
@@ -72046,6 +81051,7 @@
false
false
false
+ 0
9006
@@ -72054,6 +81060,7 @@
false
false
false
+ 0
9007
@@ -72062,6 +81069,7 @@
false
false
false
+ 0
9008
@@ -72070,6 +81078,7 @@
false
false
false
+ 0
9009
@@ -72078,6 +81087,7 @@
false
false
false
+ 0
9010
@@ -72086,6 +81096,7 @@
false
false
false
+ 0
9011
@@ -72094,6 +81105,7 @@
false
false
false
+ 0
9012
@@ -72102,6 +81114,7 @@
false
false
false
+ 0
9013
@@ -72110,6 +81123,7 @@
false
false
false
+ 0
9014
@@ -72118,6 +81132,7 @@
false
false
false
+ 0
9015
@@ -72126,6 +81141,7 @@
false
false
false
+ 0
9016
@@ -72134,6 +81150,7 @@
false
false
false
+ 0
9017
@@ -72142,6 +81159,7 @@
false
false
false
+ 0
9018
@@ -72150,6 +81168,7 @@
false
false
false
+ 0
9019
@@ -72158,6 +81177,7 @@
false
false
false
+ 0
9020
@@ -72166,6 +81186,7 @@
false
false
false
+ 0
9021
@@ -72174,6 +81195,7 @@
false
false
false
+ 0
9022
@@ -72182,6 +81204,7 @@
false
false
false
+ 0
9023
@@ -72190,6 +81213,7 @@
false
false
false
+ 0
9024
@@ -72198,6 +81222,7 @@
false
false
false
+ 0
9025
@@ -72206,6 +81231,7 @@
false
false
false
+ 0
9026
@@ -72214,6 +81240,7 @@
false
false
false
+ 0
9027
@@ -72222,6 +81249,7 @@
false
false
false
+ 0
9028
@@ -72230,6 +81258,7 @@
false
false
false
+ 0
9029
@@ -72238,6 +81267,7 @@
false
false
false
+ 0
9030
@@ -72246,6 +81276,7 @@
false
false
false
+ 0
9031
@@ -72254,6 +81285,7 @@
false
false
false
+ 0
9032
@@ -72262,6 +81294,7 @@
false
false
false
+ 0
9033
@@ -72270,6 +81303,7 @@
false
false
false
+ 0
9034
@@ -72278,6 +81312,7 @@
false
false
false
+ 0
9035
@@ -72286,6 +81321,7 @@
false
false
false
+ 0
9036
@@ -72294,6 +81330,7 @@
false
false
false
+ 0
9037
@@ -72302,6 +81339,7 @@
false
false
false
+ 0
9038
@@ -72310,6 +81348,7 @@
false
false
false
+ 0
9039
@@ -72318,6 +81357,7 @@
false
false
false
+ 0
9040
@@ -72326,6 +81366,7 @@
false
false
false
+ 0
9041
@@ -72334,6 +81375,7 @@
false
false
false
+ 0
9042
@@ -72342,6 +81384,7 @@
false
false
false
+ 0
9043
@@ -72350,6 +81393,7 @@
false
false
false
+ 0
9044
@@ -72358,6 +81402,7 @@
false
false
false
+ 0
9045
@@ -72366,6 +81411,7 @@
false
false
false
+ 0
9046
@@ -72374,6 +81420,7 @@
false
false
false
+ 0
9047
@@ -72382,6 +81429,7 @@
false
false
false
+ 0
9048
@@ -72390,6 +81438,7 @@
false
false
false
+ 0
9049
@@ -72398,6 +81447,7 @@
false
false
false
+ 0
9050
@@ -72406,6 +81456,7 @@
false
false
false
+ 0
9051
@@ -72414,6 +81465,7 @@
false
false
false
+ 0
9052
@@ -72422,6 +81474,7 @@
false
false
false
+ 0
9053
@@ -72430,6 +81483,7 @@
false
false
false
+ 0
9054
@@ -72438,6 +81492,7 @@
false
false
false
+ 0
9055
@@ -72446,6 +81501,7 @@
false
false
false
+ 0
9056
@@ -72454,6 +81510,7 @@
false
false
false
+ 0
9057
@@ -72462,6 +81519,7 @@
false
false
false
+ 0
9058
@@ -72470,6 +81528,7 @@
false
false
false
+ 0
9059
@@ -72478,6 +81537,7 @@
false
false
false
+ 0
9060
@@ -72486,6 +81546,7 @@
false
false
false
+ 0
9061
@@ -72494,6 +81555,7 @@
false
false
false
+ 0
9062
@@ -72502,6 +81564,7 @@
false
false
false
+ 0
9063
@@ -72510,6 +81573,7 @@
false
false
false
+ 0
9064
@@ -72518,6 +81582,7 @@
false
false
false
+ 0
9065
@@ -72526,6 +81591,7 @@
false
false
false
+ 0
9066
@@ -72534,6 +81600,7 @@
false
false
false
+ 0
9067
@@ -72542,6 +81609,7 @@
false
false
false
+ 0
9068
@@ -72550,6 +81618,7 @@
false
false
false
+ 0
9069
@@ -72558,6 +81627,7 @@
false
false
false
+ 0
9070
@@ -72566,6 +81636,7 @@
false
false
false
+ 0
9071
@@ -72574,6 +81645,7 @@
false
false
false
+ 0
9072
@@ -72582,6 +81654,7 @@
false
false
false
+ 0
9073
@@ -72590,6 +81663,7 @@
false
false
false
+ 0
9074
@@ -72598,6 +81672,7 @@
false
false
false
+ 0
9075
@@ -72606,6 +81681,7 @@
false
false
false
+ 0
9076
@@ -72614,6 +81690,7 @@
false
false
false
+ 0
9077
@@ -72622,6 +81699,7 @@
false
false
false
+ 0
9078
@@ -72630,6 +81708,7 @@
false
false
false
+ 0
9079
@@ -72638,6 +81717,7 @@
false
false
false
+ 0
9080
@@ -72646,6 +81726,7 @@
false
false
false
+ 0
9081
@@ -72654,6 +81735,7 @@
false
false
false
+ 0
9082
@@ -72662,6 +81744,7 @@
false
false
false
+ 0
9083
@@ -72670,6 +81753,7 @@
false
false
false
+ 0
9084
@@ -72678,6 +81762,7 @@
false
false
false
+ 0
9085
@@ -72686,6 +81771,7 @@
false
false
false
+ 0
9086
@@ -72694,6 +81780,7 @@
false
false
false
+ 0
9087
@@ -72702,6 +81789,7 @@
false
false
false
+ 0
9088
@@ -72710,6 +81798,7 @@
false
false
false
+ 0
9089
@@ -72718,6 +81807,7 @@
false
false
false
+ 0
9090
@@ -72726,6 +81816,7 @@
false
false
false
+ 0
9091
@@ -72734,6 +81825,7 @@
false
false
false
+ 0
9092
@@ -72742,6 +81834,7 @@
false
false
false
+ 0
9093
@@ -72750,6 +81843,7 @@
false
false
false
+ 0
9094
@@ -72758,6 +81852,7 @@
false
false
false
+ 0
9095
@@ -72766,6 +81861,7 @@
false
false
false
+ 0
9096
@@ -72774,6 +81870,7 @@
false
false
false
+ 0
9097
@@ -72782,6 +81879,7 @@
false
false
false
+ 0
9098
@@ -72790,6 +81888,7 @@
false
false
false
+ 0
9099
@@ -72798,6 +81897,7 @@
false
false
false
+ 0
9100
@@ -72806,6 +81906,7 @@
false
false
false
+ 0
9101
@@ -72814,6 +81915,7 @@
false
false
false
+ 0
9102
@@ -72822,6 +81924,7 @@
false
false
false
+ 0
9103
@@ -72830,6 +81933,7 @@
false
false
false
+ 0
9104
@@ -72838,6 +81942,7 @@
false
false
false
+ 0
9105
@@ -72846,6 +81951,7 @@
false
false
false
+ 0
9106
@@ -72854,6 +81960,7 @@
false
false
false
+ 0
9107
@@ -72862,6 +81969,7 @@
false
false
false
+ 0
9108
@@ -72870,6 +81978,7 @@
false
false
false
+ 0
9109
@@ -72878,6 +81987,7 @@
false
false
false
+ 0
9110
@@ -72886,6 +81996,7 @@
false
false
false
+ 0
9111
@@ -72894,6 +82005,7 @@
false
false
false
+ 0
9112
@@ -72902,6 +82014,7 @@
false
false
false
+ 0
9113
@@ -72910,6 +82023,7 @@
false
false
false
+ 0
9114
@@ -72918,6 +82032,7 @@
false
false
false
+ 0
9115
@@ -72926,6 +82041,7 @@
false
false
false
+ 0
9116
@@ -72934,6 +82050,7 @@
false
false
false
+ 0
9117
@@ -72942,6 +82059,7 @@
false
false
false
+ 0
9118
@@ -72950,6 +82068,7 @@
false
false
false
+ 0
9119
@@ -72958,6 +82077,7 @@
false
false
false
+ 0
9120
@@ -72966,6 +82086,7 @@
false
false
false
+ 0
9121
@@ -72974,6 +82095,7 @@
false
false
false
+ 0
9122
@@ -72982,6 +82104,7 @@
false
false
false
+ 0
9123
@@ -72990,6 +82113,7 @@
false
false
false
+ 0
9124
@@ -72998,6 +82122,7 @@
false
false
false
+ 0
9125
@@ -73006,6 +82131,7 @@
false
false
false
+ 0
9126
@@ -73014,6 +82140,7 @@
false
false
false
+ 0
9127
@@ -73022,6 +82149,7 @@
false
false
false
+ 0
9128
@@ -73030,6 +82158,7 @@
false
false
false
+ 0
9129
@@ -73038,6 +82167,7 @@
false
false
false
+ 0
9130
@@ -73046,6 +82176,7 @@
false
false
false
+ 0
9131
@@ -73054,6 +82185,7 @@
false
false
false
+ 0
9132
@@ -73062,6 +82194,7 @@
false
false
false
+ 0
9133
@@ -73070,6 +82203,7 @@
false
false
false
+ 0
9134
@@ -73078,6 +82212,7 @@
false
false
false
+ 0
9135
@@ -73086,6 +82221,7 @@
false
false
false
+ 0
9136
@@ -73094,6 +82230,7 @@
false
false
false
+ 0
9137
@@ -73102,6 +82239,7 @@
false
false
false
+ 0
9138
@@ -73110,6 +82248,7 @@
false
false
false
+ 0
9139
@@ -73118,6 +82257,7 @@
false
false
false
+ 0
9140
@@ -73126,6 +82266,7 @@
false
false
false
+ 0
9141
@@ -73134,6 +82275,7 @@
false
false
false
+ 0
9142
@@ -73142,6 +82284,7 @@
false
false
false
+ 0
9143
@@ -73150,6 +82293,7 @@
false
false
false
+ 0
9144
@@ -73158,6 +82302,7 @@
false
false
false
+ 0
9145
@@ -73166,6 +82311,7 @@
false
false
false
+ 0
9146
@@ -73174,6 +82320,7 @@
false
false
false
+ 0
9147
@@ -73182,6 +82329,7 @@
false
false
false
+ 0
9148
@@ -73190,6 +82338,7 @@
false
false
false
+ 0
9149
@@ -73198,6 +82347,7 @@
false
false
false
+ 0
9150
@@ -73206,6 +82356,7 @@
false
false
false
+ 0
9151
@@ -73214,6 +82365,7 @@
false
false
false
+ 0
9152
@@ -73222,6 +82374,7 @@
false
false
false
+ 0
9153
@@ -73230,6 +82383,7 @@
false
false
false
+ 0
9154
@@ -73238,6 +82392,7 @@
false
false
false
+ 0
9155
@@ -73246,6 +82401,7 @@
false
false
false
+ 0
9156
@@ -73254,6 +82410,7 @@
false
false
false
+ 0
9157
@@ -73262,6 +82419,7 @@
false
false
false
+ 0
9158
@@ -73270,6 +82428,7 @@
false
false
false
+ 0
9159
@@ -73278,6 +82437,7 @@
false
false
false
+ 0
9160
@@ -73286,6 +82446,7 @@
false
false
false
+ 0
9161
@@ -73294,6 +82455,7 @@
false
false
false
+ 0
9162
@@ -73302,6 +82464,7 @@
false
false
false
+ 0
9163
@@ -73310,6 +82473,7 @@
false
false
false
+ 0
9164
@@ -73318,6 +82482,7 @@
false
false
false
+ 0
9165
@@ -73326,6 +82491,7 @@
false
false
false
+ 0
9166
@@ -73334,6 +82500,7 @@
false
false
false
+ 0
9167
@@ -73342,6 +82509,7 @@
false
false
false
+ 0
9168
@@ -73350,6 +82518,7 @@
false
false
false
+ 0
9169
@@ -73358,6 +82527,7 @@
false
false
false
+ 0
9170
@@ -73366,6 +82536,7 @@
false
false
false
+ 0
9171
@@ -73374,6 +82545,7 @@
false
false
false
+ 0
9172
@@ -73382,6 +82554,7 @@
false
false
false
+ 0
9173
@@ -73390,6 +82563,7 @@
false
false
false
+ 0
9174
@@ -73398,6 +82572,7 @@
false
false
false
+ 0
9175
@@ -73406,6 +82581,7 @@
false
false
false
+ 0
9176
@@ -73414,6 +82590,7 @@
false
false
false
+ 0
9177
@@ -73422,6 +82599,7 @@
false
false
false
+ 0
9178
@@ -73430,6 +82608,7 @@
false
false
false
+ 0
9179
@@ -73438,6 +82617,7 @@
false
false
false
+ 0
9180
@@ -73446,6 +82626,7 @@
false
false
false
+ 0
9181
@@ -73454,6 +82635,7 @@
false
false
false
+ 0
9182
@@ -73462,6 +82644,7 @@
false
false
false
+ 0
9183
@@ -73470,6 +82653,7 @@
false
false
false
+ 0
9184
@@ -73478,6 +82662,7 @@
false
false
false
+ 0
9185
@@ -73486,6 +82671,7 @@
false
false
false
+ 0
9186
@@ -73494,6 +82680,7 @@
false
false
false
+ 0
9187
@@ -73502,6 +82689,7 @@
false
false
false
+ 0
9188
@@ -73510,6 +82698,7 @@
false
false
false
+ 0
9189
@@ -73518,6 +82707,7 @@
false
false
false
+ 0
9190
@@ -73526,6 +82716,7 @@
false
false
false
+ 0
9191
@@ -73534,6 +82725,7 @@
false
false
false
+ 0
9192
@@ -73542,6 +82734,7 @@
false
false
false
+ 0
9193
@@ -73550,6 +82743,7 @@
false
false
false
+ 0
9194
@@ -73558,6 +82752,7 @@
false
false
false
+ 0
9195
@@ -73566,6 +82761,7 @@
false
false
false
+ 0
9196
@@ -73574,6 +82770,7 @@
false
false
false
+ 0
9197
@@ -73582,6 +82779,7 @@
false
false
false
+ 0
9198
@@ -73590,6 +82788,7 @@
false
false
false
+ 0
9199
@@ -73598,6 +82797,7 @@
false
false
false
+ 0
9200
@@ -73606,6 +82806,7 @@
false
false
false
+ 0
9201
@@ -73614,6 +82815,7 @@
false
false
false
+ 0
9202
@@ -73622,6 +82824,7 @@
false
false
false
+ 0
9203
@@ -73630,6 +82833,7 @@
false
false
false
+ 0
9204
@@ -73638,6 +82842,7 @@
false
false
false
+ 0
9205
@@ -73646,6 +82851,7 @@
false
false
false
+ 0
9206
@@ -73654,6 +82860,7 @@
false
false
false
+ 0
9207
@@ -73662,6 +82869,7 @@
false
false
false
+ 0
9208
@@ -73670,6 +82878,7 @@
false
false
false
+ 0
9209
@@ -73678,6 +82887,7 @@
false
false
false
+ 0
9210
@@ -73686,6 +82896,7 @@
false
false
false
+ 0
9211
@@ -73694,6 +82905,7 @@
false
false
false
+ 0
9212
@@ -73702,6 +82914,7 @@
false
false
false
+ 0
9213
@@ -73710,6 +82923,7 @@
false
false
false
+ 0
9214
@@ -73718,6 +82932,7 @@
false
false
false
+ 0
9215
@@ -73726,6 +82941,7 @@
false
false
false
+ 0
9216
@@ -73734,6 +82950,7 @@
false
false
false
+ 0
9217
@@ -73742,6 +82959,7 @@
false
false
false
+ 0
9218
@@ -73750,6 +82968,7 @@
false
false
false
+ 0
9219
@@ -73758,6 +82977,7 @@
false
false
false
+ 0
9220
@@ -73766,6 +82986,7 @@
false
false
false
+ 0
9221
@@ -73774,6 +82995,7 @@
false
false
false
+ 0
9222
@@ -73782,6 +83004,7 @@
false
false
false
+ 0
9223
@@ -73790,6 +83013,7 @@
false
false
false
+ 0
9224
@@ -73798,6 +83022,7 @@
false
false
false
+ 0
9225
@@ -73806,6 +83031,7 @@
false
false
false
+ 0
9226
@@ -73814,6 +83040,7 @@
false
false
false
+ 0
9227
@@ -73822,6 +83049,7 @@
false
false
false
+ 0
9228
@@ -73830,6 +83058,7 @@
false
false
false
+ 0
9229
@@ -73838,6 +83067,7 @@
false
false
false
+ 0
9230
@@ -73846,6 +83076,7 @@
false
false
false
+ 0
9231
@@ -73854,6 +83085,7 @@
false
false
false
+ 0
9232
@@ -73862,6 +83094,7 @@
false
false
false
+ 0
9233
@@ -73870,6 +83103,7 @@
false
false
false
+ 0
9234
@@ -73878,6 +83112,7 @@
false
false
false
+ 0
9235
@@ -73886,6 +83121,7 @@
false
false
false
+ 0
9236
@@ -73894,6 +83130,7 @@
false
false
false
+ 0
9237
@@ -73902,6 +83139,7 @@
false
false
false
+ 0
9238
@@ -73910,6 +83148,7 @@
false
false
false
+ 0
9239
@@ -73918,6 +83157,7 @@
false
false
false
+ 0
9240
@@ -73926,6 +83166,7 @@
false
false
false
+ 0
9241
@@ -73934,6 +83175,7 @@
false
false
false
+ 0
9242
@@ -73942,6 +83184,7 @@
false
false
false
+ 0
9243
@@ -73950,6 +83193,7 @@
false
false
false
+ 0
9244
@@ -73958,6 +83202,7 @@
false
false
false
+ 0
9245
@@ -73966,6 +83211,7 @@
false
false
false
+ 0
9246
@@ -73974,6 +83220,7 @@
false
false
false
+ 0
9247
@@ -73982,6 +83229,7 @@
false
false
false
+ 0
9248
@@ -73990,6 +83238,7 @@
false
false
false
+ 0
9249
@@ -73998,6 +83247,7 @@
false
false
false
+ 0
9250
@@ -74006,6 +83256,7 @@
false
false
false
+ 0
9251
@@ -74014,6 +83265,7 @@
false
false
false
+ 0
9252
@@ -74022,6 +83274,7 @@
false
false
false
+ 0
9253
@@ -74030,6 +83283,7 @@
false
false
false
+ 0
9254
@@ -74038,6 +83292,7 @@
false
false
false
+ 0
9255
@@ -74046,6 +83301,7 @@
false
false
false
+ 0
9256
@@ -74054,6 +83310,7 @@
false
false
false
+ 0
9257
@@ -74062,6 +83319,7 @@
false
false
false
+ 0
9258
@@ -74070,6 +83328,7 @@
false
false
false
+ 0
9259
@@ -74078,6 +83337,7 @@
false
false
false
+ 0
9260
@@ -74086,6 +83346,7 @@
false
false
false
+ 0
9261
@@ -74094,6 +83355,7 @@
false
false
false
+ 0
9262
@@ -74102,6 +83364,7 @@
false
false
false
+ 0
9263
@@ -74110,6 +83373,7 @@
false
false
false
+ 0
9264
@@ -74118,6 +83382,7 @@
false
false
false
+ 0
9265
@@ -74126,6 +83391,7 @@
false
false
false
+ 0
9266
@@ -74134,6 +83400,7 @@
false
false
false
+ 0
9267
@@ -74142,6 +83409,7 @@
false
false
false
+ 0
9268
@@ -74150,6 +83418,7 @@
false
false
false
+ 0
9269
@@ -74158,6 +83427,7 @@
false
false
false
+ 0
9270
@@ -74166,6 +83436,7 @@
false
false
false
+ 0
9271
@@ -74174,6 +83445,7 @@
false
false
false
+ 0
9272
@@ -74182,6 +83454,7 @@
false
false
false
+ 0
9273
@@ -74190,6 +83463,7 @@
false
false
false
+ 0
9274
@@ -74198,6 +83472,7 @@
false
false
false
+ 0
9275
@@ -74206,6 +83481,7 @@
false
false
false
+ 0
9276
@@ -74214,6 +83490,7 @@
false
false
false
+ 0
9277
@@ -74222,6 +83499,7 @@
false
false
false
+ 0
9278
@@ -74230,6 +83508,7 @@
false
false
false
+ 0
9279
@@ -74238,6 +83517,7 @@
false
false
false
+ 0
9280
@@ -74246,6 +83526,7 @@
false
false
false
+ 0
9281
@@ -74254,6 +83535,7 @@
false
false
false
+ 0
9282
@@ -74262,6 +83544,7 @@
false
false
false
+ 0
9283
@@ -74270,6 +83553,7 @@
false
false
false
+ 0
9284
@@ -74278,6 +83562,7 @@
false
false
false
+ 0
9285
@@ -74286,6 +83571,7 @@
false
false
false
+ 0
9286
@@ -74294,6 +83580,7 @@
false
false
false
+ 0
9287
@@ -74302,6 +83589,7 @@
false
false
false
+ 0
9288
@@ -74310,6 +83598,7 @@
false
false
false
+ 0
9289
@@ -74318,6 +83607,7 @@
false
false
false
+ 0
9290
@@ -74326,6 +83616,7 @@
false
false
false
+ 0
9291
@@ -74334,6 +83625,7 @@
false
false
false
+ 0
9292
@@ -74342,6 +83634,7 @@
false
false
false
+ 0
9293
@@ -74350,6 +83643,7 @@
false
false
false
+ 0
9294
@@ -74358,6 +83652,7 @@
false
false
false
+ 0
9295
@@ -74366,6 +83661,7 @@
false
false
false
+ 0
9296
@@ -74374,6 +83670,7 @@
false
false
false
+ 0
9297
@@ -74382,6 +83679,7 @@
false
false
false
+ 0
9298
@@ -74390,6 +83688,7 @@
false
false
false
+ 0
9299
@@ -74398,6 +83697,7 @@
false
false
false
+ 0
9300
@@ -74406,6 +83706,7 @@
false
false
false
+ 0
9301
@@ -74414,6 +83715,7 @@
false
false
false
+ 0
9302
@@ -74422,6 +83724,7 @@
false
false
false
+ 0
9303
@@ -74430,6 +83733,7 @@
false
false
false
+ 0
9304
@@ -74438,6 +83742,7 @@
false
false
false
+ 0
9305
@@ -74446,6 +83751,7 @@
false
false
false
+ 0
9306
@@ -74454,6 +83760,7 @@
false
false
false
+ 0
9307
@@ -74462,6 +83769,7 @@
false
false
false
+ 0
9308
@@ -74470,6 +83778,7 @@
false
false
false
+ 0
9309
@@ -74478,6 +83787,7 @@
false
false
false
+ 0
9310
@@ -74486,6 +83796,7 @@
false
false
false
+ 0
9311
@@ -74494,6 +83805,7 @@
false
false
false
+ 0
9312
@@ -74502,6 +83814,7 @@
false
false
false
+ 0
9313
@@ -74510,6 +83823,7 @@
false
false
false
+ 0
9314
@@ -74518,6 +83832,7 @@
false
false
false
+ 0
9315
@@ -74526,6 +83841,7 @@
false
false
false
+ 0
9316
@@ -74534,6 +83850,7 @@
false
false
false
+ 0
9317
@@ -74542,6 +83859,7 @@
false
false
false
+ 0
9318
@@ -74550,6 +83868,7 @@
false
false
false
+ 0
9319
@@ -74558,6 +83877,7 @@
false
false
false
+ 0
9320
@@ -74566,6 +83886,7 @@
false
false
false
+ 0
9321
@@ -74574,6 +83895,7 @@
false
false
false
+ 0
9322
@@ -74582,6 +83904,7 @@
false
false
false
+ 0
9323
@@ -74590,6 +83913,7 @@
false
false
false
+ 0
9324
@@ -74598,6 +83922,7 @@
false
false
false
+ 0
9325
@@ -74606,6 +83931,7 @@
false
false
false
+ 0
9326
@@ -74614,6 +83940,7 @@
false
false
false
+ 0
9327
@@ -74622,6 +83949,7 @@
false
false
false
+ 0
9328
@@ -74630,6 +83958,7 @@
false
false
false
+ 0
9329
@@ -74638,6 +83967,7 @@
false
false
false
+ 0
9330
@@ -74646,6 +83976,7 @@
false
false
false
+ 0
9331
@@ -74654,6 +83985,7 @@
false
false
false
+ 0
9332
@@ -74662,6 +83994,7 @@
false
false
false
+ 0
9333
@@ -74670,6 +84003,7 @@
false
false
false
+ 0
9334
@@ -74678,6 +84012,7 @@
false
false
false
+ 0
9335
@@ -74686,6 +84021,7 @@
false
false
false
+ 0
9336
@@ -74694,6 +84030,7 @@
false
false
false
+ 0
9337
@@ -74702,6 +84039,7 @@
false
false
false
+ 0
9338
@@ -74710,6 +84048,7 @@
false
false
false
+ 0
9339
@@ -74718,6 +84057,7 @@
false
false
false
+ 0
9340
@@ -74726,6 +84066,7 @@
false
false
false
+ 0
9341
@@ -74734,6 +84075,7 @@
false
false
false
+ 0
9342
@@ -74742,6 +84084,7 @@
false
false
false
+ 0
9343
@@ -74750,6 +84093,7 @@
false
false
false
+ 0
9344
@@ -74758,6 +84102,7 @@
false
false
false
+ 0
9345
@@ -74766,6 +84111,7 @@
false
false
false
+ 0
9346
@@ -74774,6 +84120,7 @@
false
false
false
+ 0
9347
@@ -74782,6 +84129,7 @@
false
false
false
+ 0
9348
@@ -74790,6 +84138,7 @@
false
false
false
+ 0
9349
@@ -74798,6 +84147,7 @@
false
false
false
+ 0
9350
@@ -74806,6 +84156,7 @@
false
false
false
+ 0
9351
@@ -74814,6 +84165,7 @@
false
false
false
+ 0
9352
@@ -74822,6 +84174,7 @@
false
false
false
+ 0
9353
@@ -74830,6 +84183,7 @@
false
false
false
+ 0
9354
@@ -74838,6 +84192,7 @@
false
false
false
+ 0
9355
@@ -74846,6 +84201,7 @@
false
false
false
+ 0
9356
@@ -74854,6 +84210,7 @@
false
false
false
+ 0
9357
@@ -74862,6 +84219,7 @@
false
false
false
+ 0
9358
@@ -74870,6 +84228,7 @@
false
false
false
+ 0
9359
@@ -74878,6 +84237,7 @@
false
false
false
+ 0
9360
@@ -74886,6 +84246,7 @@
false
false
false
+ 0
9361
@@ -74894,6 +84255,7 @@
false
false
false
+ 0
9362
@@ -74902,6 +84264,7 @@
false
false
false
+ 0
9363
@@ -74910,6 +84273,7 @@
false
false
false
+ 0
9364
@@ -74918,6 +84282,7 @@
false
false
false
+ 0
9365
@@ -74926,6 +84291,7 @@
false
false
false
+ 0
9366
@@ -74934,6 +84300,7 @@
false
false
false
+ 0
9367
@@ -74942,6 +84309,7 @@
false
false
false
+ 0
9368
@@ -74950,6 +84318,7 @@
false
false
false
+ 0
9369
@@ -74958,6 +84327,7 @@
false
false
false
+ 0
9370
@@ -74966,6 +84336,7 @@
false
false
false
+ 0
9371
@@ -74974,6 +84345,7 @@
false
false
false
+ 0
9372
@@ -74982,6 +84354,7 @@
false
false
false
+ 0
9373
@@ -74990,6 +84363,7 @@
false
false
false
+ 0
9374
@@ -74998,6 +84372,7 @@
false
false
false
+ 0
9375
@@ -75006,6 +84381,7 @@
false
false
false
+ 0
9376
@@ -75014,6 +84390,7 @@
false
false
false
+ 0
9377
@@ -75022,6 +84399,7 @@
false
false
false
+ 0
9378
@@ -75030,6 +84408,7 @@
false
false
false
+ 0
9379
@@ -75038,6 +84417,7 @@
false
false
false
+ 0
9380
@@ -75046,6 +84426,7 @@
false
false
false
+ 0
9381
@@ -75054,6 +84435,7 @@
false
false
false
+ 0
9382
@@ -75062,6 +84444,7 @@
false
false
false
+ 0
9383
@@ -75070,6 +84453,7 @@
false
false
false
+ 0
9384
@@ -75078,6 +84462,7 @@
false
false
false
+ 0
9385
@@ -75086,6 +84471,7 @@
false
false
false
+ 0
9386
@@ -75094,6 +84480,7 @@
false
false
false
+ 0
9387
@@ -75102,6 +84489,7 @@
false
false
false
+ 0
9388
@@ -75110,6 +84498,7 @@
false
false
false
+ 0
9389
@@ -75118,6 +84507,7 @@
false
false
false
+ 0
9390
@@ -75126,6 +84516,7 @@
false
false
false
+ 0
9391
@@ -75134,6 +84525,7 @@
false
false
false
+ 0
9392
@@ -75142,6 +84534,7 @@
false
false
false
+ 0
9393
@@ -75150,6 +84543,7 @@
false
false
false
+ 0
9394
@@ -75158,6 +84552,7 @@
false
false
false
+ 0
9395
@@ -75166,6 +84561,7 @@
false
false
false
+ 0
9396
@@ -75174,6 +84570,7 @@
false
false
false
+ 0
9397
@@ -75182,6 +84579,7 @@
false
false
false
+ 0
9398
@@ -75190,6 +84588,7 @@
false
false
false
+ 0
9399
@@ -75198,6 +84597,7 @@
false
false
false
+ 0
9400
@@ -75206,6 +84606,7 @@
false
false
false
+ 0
9401
@@ -75214,6 +84615,7 @@
false
false
false
+ 0
9402
@@ -75222,6 +84624,7 @@
false
false
false
+ 0
9403
@@ -75230,6 +84633,7 @@
false
false
false
+ 0
9404
@@ -75238,6 +84642,7 @@
false
false
false
+ 0
9405
@@ -75246,6 +84651,7 @@
false
false
false
+ 0
9406
@@ -75254,6 +84660,7 @@
false
false
false
+ 0
9407
@@ -75262,6 +84669,7 @@
false
false
false
+ 0
9408
@@ -75270,6 +84678,7 @@
false
false
false
+ 0
9409
@@ -75278,6 +84687,7 @@
false
false
false
+ 0
9410
@@ -75286,6 +84696,7 @@
false
false
false
+ 0
9411
@@ -75294,6 +84705,7 @@
false
false
false
+ 0
9412
@@ -75302,6 +84714,7 @@
false
false
false
+ 0
9413
@@ -75310,6 +84723,7 @@
false
false
false
+ 0
9414
@@ -75318,6 +84732,7 @@
false
false
false
+ 0
9415
@@ -75326,6 +84741,7 @@
false
false
false
+ 0
9416
@@ -75334,6 +84750,7 @@
false
false
false
+ 0
9417
@@ -75342,6 +84759,7 @@
false
false
false
+ 0
9418
@@ -75350,6 +84768,7 @@
false
false
false
+ 0
9419
@@ -75358,6 +84777,7 @@
false
false
false
+ 0
9420
@@ -75366,6 +84786,7 @@
false
false
false
+ 0
9421
@@ -75374,6 +84795,7 @@
false
false
false
+ 0
9422
@@ -75382,6 +84804,7 @@
false
false
false
+ 0
9423
@@ -75390,6 +84813,7 @@
false
false
false
+ 0
9424
@@ -75398,6 +84822,7 @@
false
false
false
+ 0
9425
@@ -75406,6 +84831,7 @@
false
false
false
+ 0
9426
@@ -75414,6 +84840,7 @@
false
false
false
+ 0
9427
@@ -75422,6 +84849,7 @@
false
false
false
+ 0
9428
@@ -75430,6 +84858,7 @@
false
false
false
+ 0
9429
@@ -75438,6 +84867,7 @@
false
false
false
+ 0
9430
@@ -75446,6 +84876,7 @@
false
false
false
+ 0
9431
@@ -75454,6 +84885,7 @@
false
false
false
+ 0
9432
@@ -75462,6 +84894,7 @@
false
false
false
+ 0
9433
@@ -75470,6 +84903,7 @@
false
false
false
+ 0
9434
@@ -75478,6 +84912,7 @@
false
false
false
+ 0
9435
@@ -75486,6 +84921,7 @@
false
false
false
+ 0
9436
@@ -75494,6 +84930,7 @@
false
false
false
+ 0
9437
@@ -75502,6 +84939,7 @@
false
false
false
+ 0
9438
@@ -75510,6 +84948,7 @@
false
false
false
+ 0
9439
@@ -75518,6 +84957,7 @@
false
false
false
+ 0
9440
@@ -75526,6 +84966,7 @@
false
false
false
+ 0
9441
@@ -75534,6 +84975,7 @@
false
false
false
+ 0
9442
@@ -75542,6 +84984,7 @@
false
false
false
+ 0
9443
@@ -75550,6 +84993,7 @@
false
false
false
+ 0
9444
@@ -75558,6 +85002,7 @@
false
false
false
+ 0
9445
@@ -75566,6 +85011,7 @@
false
false
false
+ 0
9446
@@ -75574,6 +85020,7 @@
false
false
false
+ 0
9447
@@ -75582,6 +85029,7 @@
false
false
false
+ 0
9448
@@ -75590,6 +85038,7 @@
false
false
false
+ 0
9449
@@ -75598,6 +85047,7 @@
false
false
false
+ 0
9450
@@ -75606,6 +85056,7 @@
false
false
false
+ 0
9451
@@ -75614,6 +85065,7 @@
false
false
false
+ 0
9452
@@ -75622,6 +85074,7 @@
false
false
false
+ 0
9453
@@ -75630,6 +85083,7 @@
false
false
false
+ 0
9454
@@ -75638,6 +85092,7 @@
false
false
false
+ 0
9455
@@ -75646,6 +85101,7 @@
false
false
false
+ 0
9456
@@ -75654,6 +85110,7 @@
false
false
false
+ 0
9457
@@ -75662,6 +85119,7 @@
false
false
false
+ 0
9458
@@ -75670,6 +85128,7 @@
false
false
false
+ 0
9459
@@ -75678,6 +85137,7 @@
false
false
false
+ 0
9460
@@ -75686,6 +85146,7 @@
false
false
false
+ 0
9461
@@ -75694,6 +85155,7 @@
false
false
false
+ 0
9462
@@ -75702,6 +85164,7 @@
false
false
false
+ 0
9463
@@ -75710,6 +85173,7 @@
false
false
false
+ 0
9464
@@ -75718,6 +85182,7 @@
false
false
false
+ 0
9465
@@ -75726,6 +85191,7 @@
false
false
false
+ 0
9466
@@ -75734,6 +85200,7 @@
false
false
false
+ 0
9467
@@ -75742,6 +85209,7 @@
false
false
false
+ 0
9468
@@ -75750,6 +85218,7 @@
false
false
false
+ 0
9469
@@ -75758,6 +85227,7 @@
false
false
false
+ 0
9470
@@ -75766,6 +85236,7 @@
false
false
false
+ 0
9471
@@ -75774,6 +85245,7 @@
false
false
false
+ 0
9472
@@ -75782,6 +85254,7 @@
false
false
false
+ 0
9473
@@ -75790,6 +85263,7 @@
false
false
false
+ 0
9474
@@ -75798,6 +85272,7 @@
false
false
false
+ 0
9475
@@ -75806,6 +85281,7 @@
false
false
false
+ 0
9476
@@ -75814,6 +85290,7 @@
false
false
false
+ 0
9477
@@ -75822,6 +85299,7 @@
false
false
false
+ 0
9478
@@ -75830,6 +85308,7 @@
false
false
false
+ 0
9479
@@ -75838,6 +85317,7 @@
false
false
false
+ 0
9480
@@ -75846,6 +85326,7 @@
false
false
false
+ 0
9481
@@ -75854,6 +85335,7 @@
false
false
false
+ 0
9482
@@ -75862,6 +85344,7 @@
false
false
false
+ 0
9483
@@ -75870,6 +85353,7 @@
false
false
false
+ 0
9484
@@ -75878,6 +85362,7 @@
false
false
false
+ 0
9485
@@ -75886,6 +85371,7 @@
false
false
false
+ 0
9486
@@ -75894,6 +85380,7 @@
false
false
false
+ 0
9487
@@ -75902,6 +85389,7 @@
false
false
false
+ 0
9488
@@ -75910,6 +85398,7 @@
false
false
false
+ 0
9489
@@ -75918,6 +85407,7 @@
false
false
false
+ 0
9490
@@ -75926,6 +85416,7 @@
false
false
false
+ 0
9491
@@ -75934,6 +85425,7 @@
false
false
false
+ 0
9492
@@ -75942,6 +85434,7 @@
false
false
false
+ 0
9493
@@ -75950,6 +85443,7 @@
false
false
false
+ 0
9494
@@ -75958,6 +85452,7 @@
false
false
false
+ 0
9495
@@ -75966,6 +85461,7 @@
false
false
false
+ 0
9496
@@ -75974,6 +85470,7 @@
false
false
false
+ 0
9497
@@ -75982,6 +85479,7 @@
false
false
false
+ 0
9498
@@ -75990,6 +85488,7 @@
false
false
false
+ 0
9499
@@ -75998,6 +85497,7 @@
false
false
false
+ 0
9500
@@ -76006,6 +85506,7 @@
false
false
false
+ 0
9501
@@ -76014,6 +85515,7 @@
false
false
false
+ 0
9502
@@ -76022,6 +85524,7 @@
false
false
false
+ 0
9503
@@ -76030,6 +85533,7 @@
false
false
false
+ 0
9504
@@ -76038,6 +85542,7 @@
false
false
false
+ 0
9505
@@ -76046,6 +85551,7 @@
false
false
false
+ 0
9506
@@ -76054,6 +85560,7 @@
false
false
false
+ 0
9507
@@ -76062,6 +85569,7 @@
false
false
false
+ 0
9508
@@ -76070,6 +85578,7 @@
false
false
false
+ 0
9509
@@ -76078,6 +85587,7 @@
false
false
false
+ 0
9510
@@ -76086,6 +85596,7 @@
false
false
false
+ 0
9511
@@ -76094,6 +85605,7 @@
false
false
false
+ 0
9512
@@ -76102,6 +85614,7 @@
false
false
false
+ 0
9513
@@ -76110,6 +85623,7 @@
false
false
false
+ 0
9514
@@ -76118,6 +85632,7 @@
false
false
false
+ 0
9515
@@ -76126,6 +85641,7 @@
false
false
false
+ 0
9516
@@ -76134,6 +85650,7 @@
false
false
false
+ 0
9517
@@ -76142,6 +85659,7 @@
false
false
false
+ 0
9518
@@ -76150,6 +85668,7 @@
false
false
false
+ 0
9519
@@ -76158,6 +85677,7 @@
false
false
false
+ 0
9520
@@ -76166,6 +85686,7 @@
false
false
false
+ 0
9521
@@ -76174,6 +85695,7 @@
false
false
false
+ 0
9522
@@ -76182,6 +85704,7 @@
false
false
false
+ 0
9523
@@ -76190,6 +85713,7 @@
false
false
false
+ 0
9524
@@ -76198,6 +85722,7 @@
false
false
false
+ 0
9525
@@ -76206,6 +85731,7 @@
false
false
false
+ 0
9526
@@ -76214,6 +85740,7 @@
false
false
false
+ 0
9527
@@ -76222,6 +85749,7 @@
false
false
false
+ 0
9528
@@ -76230,6 +85758,7 @@
false
false
false
+ 0
9529
@@ -76238,6 +85767,7 @@
false
false
false
+ 0
9530
@@ -76246,6 +85776,7 @@
false
false
false
+ 0
9531
@@ -76254,6 +85785,7 @@
false
false
false
+ 0
9532
@@ -76262,6 +85794,7 @@
false
false
false
+ 0
9533
@@ -76270,6 +85803,7 @@
false
false
false
+ 0
9534
@@ -76278,6 +85812,7 @@
false
false
false
+ 0
9535
@@ -76286,6 +85821,7 @@
false
false
false
+ 0
9536
@@ -76294,6 +85830,7 @@
false
false
false
+ 0
9537
@@ -76302,6 +85839,7 @@
false
false
false
+ 0
9538
@@ -76310,6 +85848,7 @@
false
false
false
+ 0
9539
@@ -76318,6 +85857,7 @@
false
false
false
+ 0
9540
@@ -76326,6 +85866,7 @@
false
false
false
+ 0
9541
@@ -76334,6 +85875,7 @@
false
false
false
+ 0
9542
@@ -76342,6 +85884,7 @@
false
false
false
+ 0
9543
@@ -76350,6 +85893,7 @@
false
false
false
+ 0
9544
@@ -76358,6 +85902,7 @@
false
false
false
+ 0
9545
@@ -76366,6 +85911,7 @@
false
false
false
+ 0
9546
@@ -76374,6 +85920,7 @@
false
false
false
+ 0
9547
@@ -76382,6 +85929,7 @@
false
false
false
+ 0
9548
@@ -76390,6 +85938,7 @@
false
false
false
+ 0
9549
@@ -76398,6 +85947,7 @@
false
false
false
+ 0
9550
@@ -76406,6 +85956,7 @@
false
false
false
+ 0
9551
@@ -76414,6 +85965,7 @@
false
false
false
+ 0
9552
@@ -76422,6 +85974,7 @@
false
false
false
+ 0
9553
@@ -76430,6 +85983,7 @@
false
false
false
+ 0
9554
@@ -76438,6 +85992,7 @@
false
false
false
+ 0
9555
@@ -76446,6 +86001,7 @@
false
false
false
+ 0
9556
@@ -76454,6 +86010,7 @@
false
false
false
+ 0
9557
@@ -76462,6 +86019,7 @@
false
false
false
+ 0
9558
@@ -76470,6 +86028,7 @@
false
false
false
+ 0
9559
@@ -76478,6 +86037,7 @@
false
false
false
+ 0
9560
@@ -76486,6 +86046,7 @@
false
false
false
+ 0
9561
@@ -76494,6 +86055,7 @@
false
false
false
+ 0
9562
@@ -76502,6 +86064,7 @@
false
false
false
+ 0
9563
@@ -76510,6 +86073,7 @@
false
false
false
+ 0
9564
@@ -76518,6 +86082,7 @@
false
false
false
+ 0
9565
@@ -76526,6 +86091,7 @@
false
false
false
+ 0
9566
@@ -76534,6 +86100,7 @@
false
false
false
+ 0
9567
@@ -76542,6 +86109,7 @@
false
false
false
+ 0
9568
@@ -76550,6 +86118,7 @@
false
false
false
+ 0
9569
@@ -76558,6 +86127,7 @@
false
false
false
+ 0
9570
@@ -76566,6 +86136,7 @@
false
false
false
+ 0
9571
@@ -76574,6 +86145,7 @@
false
false
false
+ 0
9572
@@ -76582,6 +86154,7 @@
false
false
false
+ 0
9573
@@ -76590,6 +86163,7 @@
false
false
false
+ 0
9574
@@ -76598,6 +86172,7 @@
false
false
false
+ 0
9575
@@ -76606,6 +86181,7 @@
false
false
false
+ 0
9576
@@ -76614,6 +86190,7 @@
false
false
false
+ 0
9577
@@ -76622,6 +86199,7 @@
false
false
false
+ 0
9578
@@ -76630,6 +86208,7 @@
false
false
false
+ 0
9579
@@ -76638,6 +86217,7 @@
false
false
false
+ 0
9580
@@ -76646,6 +86226,7 @@
false
false
false
+ 0
9581
@@ -76654,6 +86235,7 @@
false
false
false
+ 0
9582
@@ -76662,6 +86244,7 @@
false
false
false
+ 0
9583
@@ -76670,6 +86253,7 @@
false
false
false
+ 0
9584
@@ -76678,6 +86262,7 @@
false
false
false
+ 0
9585
@@ -76686,6 +86271,7 @@
false
false
false
+ 0
9586
@@ -76694,6 +86280,7 @@
false
false
false
+ 0
9587
@@ -76702,6 +86289,7 @@
false
false
false
+ 0
9588
@@ -76710,6 +86298,7 @@
false
false
false
+ 0
9589
@@ -76718,6 +86307,7 @@
false
false
false
+ 0
9590
@@ -76726,6 +86316,7 @@
false
false
false
+ 0
9591
@@ -76734,6 +86325,7 @@
false
false
false
+ 0
9592
@@ -76742,6 +86334,7 @@
false
false
false
+ 0
9593
@@ -76750,6 +86343,7 @@
false
false
false
+ 0
9594
@@ -76758,6 +86352,7 @@
false
false
false
+ 0
9595
@@ -76766,6 +86361,7 @@
false
false
false
+ 0
9596
@@ -76774,6 +86370,7 @@
false
false
false
+ 0
9597
@@ -76782,6 +86379,7 @@
false
false
false
+ 0
9598
@@ -76790,6 +86388,7 @@
false
false
false
+ 0
9599
@@ -76798,6 +86397,7 @@
false
false
false
+ 0
9600
@@ -76806,6 +86406,7 @@
false
false
false
+ 0
9601
@@ -76814,6 +86415,7 @@
false
false
false
+ 0
9602
@@ -76822,6 +86424,7 @@
false
false
false
+ 0
9603
@@ -76830,6 +86433,7 @@
false
false
false
+ 0
9604
@@ -76838,6 +86442,7 @@
false
false
false
+ 0
9605
@@ -76846,6 +86451,7 @@
false
false
false
+ 0
9606
@@ -76854,6 +86460,7 @@
false
false
false
+ 0
9607
@@ -76862,6 +86469,7 @@
false
false
false
+ 0
9608
@@ -76870,6 +86478,7 @@
false
false
false
+ 0
9609
@@ -76878,6 +86487,7 @@
false
false
false
+ 0
9610
@@ -76886,6 +86496,7 @@
false
false
false
+ 0
9611
@@ -76894,6 +86505,7 @@
false
false
false
+ 0
9612
@@ -76902,6 +86514,7 @@
false
false
false
+ 0
9613
@@ -76910,6 +86523,7 @@
false
false
false
+ 0
9614
@@ -76918,6 +86532,7 @@
false
false
false
+ 0
9615
@@ -76926,6 +86541,7 @@
false
false
false
+ 0
9616
@@ -76934,6 +86550,7 @@
false
false
false
+ 0
9617
@@ -76942,6 +86559,7 @@
false
false
false
+ 0
9618
@@ -76950,6 +86568,7 @@
false
false
false
+ 0
9619
@@ -76958,6 +86577,7 @@
false
false
false
+ 0
9620
@@ -76966,6 +86586,7 @@
false
false
false
+ 0
9621
@@ -76974,6 +86595,7 @@
false
false
false
+ 0
9622
@@ -76982,6 +86604,7 @@
false
false
false
+ 0
9623
@@ -76990,6 +86613,7 @@
false
false
false
+ 0
9624
@@ -76998,6 +86622,7 @@
false
false
false
+ 0
9625
@@ -77006,6 +86631,7 @@
false
false
false
+ 0
9626
@@ -77014,6 +86640,7 @@
false
false
false
+ 0
9627
@@ -77022,6 +86649,7 @@
false
false
false
+ 0
9628
@@ -77030,6 +86658,7 @@
false
false
false
+ 0
9629
@@ -77038,6 +86667,7 @@
false
false
false
+ 0
9630
@@ -77046,6 +86676,7 @@
false
false
false
+ 0
9631
@@ -77054,6 +86685,7 @@
false
false
false
+ 0
9632
@@ -77062,6 +86694,7 @@
false
false
false
+ 0
9633
@@ -77070,6 +86703,7 @@
false
false
false
+ 0
9634
@@ -77078,6 +86712,7 @@
false
false
false
+ 0
9635
@@ -77086,6 +86721,7 @@
false
false
false
+ 0
9636
@@ -77094,6 +86730,7 @@
false
false
false
+ 0
9637
@@ -77102,6 +86739,7 @@
false
false
false
+ 0
9638
@@ -77110,6 +86748,7 @@
false
false
false
+ 0
9639
@@ -77118,6 +86757,7 @@
false
false
false
+ 0
9640
@@ -77126,6 +86766,7 @@
false
false
false
+ 0
9641
@@ -77134,6 +86775,7 @@
false
false
false
+ 0
9642
@@ -77142,6 +86784,7 @@
false
false
false
+ 0
9643
@@ -77150,6 +86793,7 @@
false
false
false
+ 0
9644
@@ -77158,6 +86802,7 @@
false
false
false
+ 0
9645
@@ -77166,6 +86811,7 @@
false
false
false
+ 0
9646
@@ -77174,6 +86820,7 @@
false
false
false
+ 0
9647
@@ -77182,6 +86829,7 @@
false
false
false
+ 0
9648
@@ -77190,6 +86838,7 @@
false
false
false
+ 0
9649
@@ -77198,6 +86847,7 @@
false
false
false
+ 0
9650
@@ -77206,6 +86856,7 @@
false
false
false
+ 0
9651
@@ -77214,6 +86865,7 @@
false
false
false
+ 0
9652
@@ -77222,6 +86874,7 @@
false
false
false
+ 0
9653
@@ -77230,6 +86883,7 @@
false
false
false
+ 0
9654
@@ -77238,6 +86892,7 @@
false
false
false
+ 0
9655
@@ -77246,6 +86901,7 @@
false
false
false
+ 0
9656
@@ -77254,6 +86910,7 @@
false
false
false
+ 0
9657
@@ -77262,6 +86919,7 @@
false
false
false
+ 0
9658
@@ -77270,6 +86928,7 @@
false
false
false
+ 0
9659
@@ -77278,6 +86937,7 @@
false
false
false
+ 0
9660
@@ -77286,6 +86946,7 @@
false
false
false
+ 0
9661
@@ -77294,6 +86955,7 @@
false
false
false
+ 0
9662
@@ -77302,6 +86964,7 @@
false
false
false
+ 0
9663
@@ -77310,6 +86973,7 @@
false
false
false
+ 0
9664
@@ -77318,6 +86982,7 @@
false
false
false
+ 0
9665
@@ -77326,6 +86991,7 @@
false
false
false
+ 0
9666
@@ -77334,6 +87000,7 @@
false
false
false
+ 0
9667
@@ -77342,6 +87009,7 @@
false
false
false
+ 0
9668
@@ -77350,6 +87018,7 @@
false
false
false
+ 0
9669
@@ -77358,6 +87027,7 @@
false
false
false
+ 0
9670
@@ -77366,6 +87036,7 @@
false
false
false
+ 0
9671
@@ -77374,6 +87045,7 @@
false
false
false
+ 0
9672
@@ -77382,6 +87054,7 @@
false
false
false
+ 0
9673
@@ -77390,6 +87063,7 @@
false
false
false
+ 0
9674
@@ -77398,6 +87072,7 @@
false
false
false
+ 0
9675
@@ -77406,6 +87081,7 @@
false
false
false
+ 0
9676
@@ -77414,6 +87090,7 @@
false
false
false
+ 0
9677
@@ -77422,6 +87099,7 @@
false
false
false
+ 0
9678
@@ -77430,6 +87108,7 @@
false
false
false
+ 0
9679
@@ -77438,6 +87117,7 @@
false
false
false
+ 0
9680
@@ -77446,6 +87126,7 @@
false
false
false
+ 0
9681
@@ -77454,6 +87135,7 @@
false
false
false
+ 0
9682
@@ -77462,6 +87144,7 @@
false
false
false
+ 0
9683
@@ -77470,6 +87153,7 @@
false
false
false
+ 0
9684
@@ -77478,6 +87162,7 @@
false
false
false
+ 0
9685
@@ -77486,6 +87171,7 @@
false
false
false
+ 0
9686
@@ -77494,6 +87180,7 @@
false
false
false
+ 0
9687
@@ -77502,6 +87189,7 @@
false
false
false
+ 0
9688
@@ -77510,6 +87198,7 @@
false
false
false
+ 0
9689
@@ -77518,6 +87207,7 @@
false
false
false
+ 0
9690
@@ -77526,6 +87216,7 @@
false
false
false
+ 0
9691
@@ -77534,6 +87225,7 @@
false
false
false
+ 0
9692
@@ -77542,6 +87234,7 @@
false
false
false
+ 0
9693
@@ -77550,6 +87243,7 @@
false
false
false
+ 0
9694
@@ -77558,6 +87252,7 @@
false
false
false
+ 0
9695
@@ -77566,6 +87261,7 @@
false
false
false
+ 0
9696
@@ -77574,6 +87270,7 @@
false
false
false
+ 0
9697
@@ -77582,6 +87279,7 @@
false
false
false
+ 0
9698
@@ -77590,6 +87288,7 @@
false
false
false
+ 0
9699
@@ -77598,6 +87297,7 @@
false
false
false
+ 0
9700
@@ -77606,6 +87306,7 @@
false
false
false
+ 0
9701
@@ -77614,6 +87315,7 @@
false
false
false
+ 0
9702
@@ -77622,6 +87324,7 @@
false
false
false
+ 0
9703
@@ -77630,6 +87333,7 @@
false
false
false
+ 0
9704
@@ -77638,6 +87342,7 @@
false
false
false
+ 0
9705
@@ -77646,6 +87351,7 @@
false
false
false
+ 0
9706
@@ -77654,6 +87360,7 @@
false
false
false
+ 0
9707
@@ -77662,6 +87369,7 @@
false
false
false
+ 0
9708
@@ -77670,6 +87378,7 @@
false
false
false
+ 0
9709
@@ -77678,6 +87387,7 @@
false
false
false
+ 0
9710
@@ -77686,6 +87396,7 @@
false
false
false
+ 0
9711
@@ -77694,6 +87405,7 @@
false
false
false
+ 0
9712
@@ -77702,6 +87414,7 @@
false
false
false
+ 0
9713
@@ -77710,6 +87423,7 @@
false
false
false
+ 0
9714
@@ -77718,6 +87432,7 @@
false
false
false
+ 0
9715
@@ -77726,6 +87441,7 @@
false
false
false
+ 0
9716
@@ -77734,6 +87450,7 @@
false
false
false
+ 0
9717
@@ -77742,6 +87459,7 @@
false
false
false
+ 0
9718
@@ -77750,6 +87468,7 @@
false
false
false
+ 0
9719
@@ -77758,6 +87477,7 @@
false
false
false
+ 0
9720
@@ -77766,6 +87486,7 @@
false
false
false
+ 0
9721
@@ -77774,6 +87495,7 @@
false
false
false
+ 0
9722
@@ -77782,6 +87504,7 @@
false
false
false
+ 0
9723
@@ -77790,6 +87513,7 @@
false
false
false
+ 0
9724
@@ -77798,6 +87522,7 @@
false
false
false
+ 0
9725
@@ -77806,6 +87531,7 @@
false
false
false
+ 0
9726
@@ -77814,6 +87540,7 @@
false
false
false
+ 0
9727
@@ -77822,6 +87549,7 @@
false
false
false
+ 0
9728
@@ -77830,6 +87558,7 @@
false
false
false
+ 0
9729
@@ -77838,6 +87567,7 @@
false
false
false
+ 0
9730
@@ -77846,6 +87576,7 @@
false
false
false
+ 0
9731
@@ -77854,6 +87585,7 @@
false
false
false
+ 0
9732
@@ -77862,6 +87594,7 @@
false
false
false
+ 0
9733
@@ -77870,6 +87603,7 @@
false
false
false
+ 0
9734
@@ -77878,6 +87612,7 @@
false
false
false
+ 0
9735
@@ -77886,6 +87621,7 @@
false
false
false
+ 0
9736
@@ -77894,6 +87630,7 @@
false
false
false
+ 0
9737
@@ -77902,6 +87639,7 @@
false
false
false
+ 0
9738
@@ -77910,6 +87648,7 @@
false
false
false
+ 0
9739
@@ -77918,6 +87657,7 @@
false
false
false
+ 0
9740
@@ -77926,6 +87666,7 @@
false
false
false
+ 0
9741
@@ -77934,6 +87675,7 @@
false
false
false
+ 0
9742
@@ -77942,6 +87684,7 @@
false
false
false
+ 0
9743
@@ -77950,6 +87693,7 @@
false
false
false
+ 0
9744
@@ -77958,6 +87702,7 @@
false
false
false
+ 0
9745
@@ -77966,6 +87711,7 @@
false
false
false
+ 0
9746
@@ -77974,6 +87720,7 @@
false
false
false
+ 0
9747
@@ -77982,6 +87729,7 @@
false
false
false
+ 0
9748
@@ -77990,6 +87738,7 @@
false
false
false
+ 0
9749
@@ -77998,6 +87747,7 @@
false
false
false
+ 0
9750
@@ -78006,6 +87756,7 @@
false
false
false
+ 0
9751
@@ -78014,6 +87765,7 @@
false
false
false
+ 0
9752
@@ -78022,6 +87774,7 @@
false
false
false
+ 0
9753
@@ -78030,6 +87783,7 @@
false
false
false
+ 0
9754
@@ -78038,6 +87792,7 @@
false
false
false
+ 0
9755
@@ -78046,6 +87801,7 @@
false
false
false
+ 0
9756
@@ -78054,6 +87810,7 @@
false
false
false
+ 0
9757
@@ -78062,6 +87819,7 @@
false
false
false
+ 0
9758
@@ -78070,6 +87828,7 @@
false
false
false
+ 0
9759
@@ -78078,6 +87837,7 @@
false
false
false
+ 0
9760
@@ -78086,6 +87846,7 @@
false
false
false
+ 0
9761
@@ -78094,6 +87855,7 @@
false
false
false
+ 0
9762
@@ -78102,6 +87864,7 @@
false
false
false
+ 0
9763
@@ -78110,6 +87873,7 @@
false
false
false
+ 0
9764
@@ -78118,6 +87882,7 @@
false
false
false
+ 0
9765
@@ -78126,6 +87891,7 @@
false
false
false
+ 0
9766
@@ -78134,6 +87900,7 @@
false
false
false
+ 0
9767
@@ -78142,6 +87909,7 @@
false
false
false
+ 0
9768
@@ -78150,6 +87918,7 @@
false
false
false
+ 0
9769
@@ -78158,6 +87927,7 @@
false
false
false
+ 0
9770
@@ -78166,6 +87936,7 @@
false
false
false
+ 0
9771
@@ -78174,6 +87945,7 @@
false
false
false
+ 0
9772
@@ -78182,6 +87954,7 @@
false
false
false
+ 0
9773
@@ -78190,6 +87963,7 @@
false
false
false
+ 0
9774
@@ -78198,6 +87972,7 @@
false
false
false
+ 0
9775
@@ -78206,6 +87981,7 @@
false
false
false
+ 0
9776
@@ -78214,6 +87990,7 @@
false
false
false
+ 0
9777
@@ -78222,6 +87999,7 @@
false
false
false
+ 0
9778
@@ -78230,6 +88008,7 @@
false
false
false
+ 0
9779
@@ -78238,6 +88017,7 @@
false
false
false
+ 0
9780
@@ -78246,6 +88026,7 @@
false
false
false
+ 0
9781
@@ -78254,6 +88035,7 @@
false
false
false
+ 0
9782
@@ -78262,6 +88044,7 @@
false
false
false
+ 0
9783
@@ -78270,6 +88053,7 @@
false
false
false
+ 0
9784
@@ -78278,6 +88062,7 @@
false
false
false
+ 0
9785
@@ -78286,6 +88071,7 @@
false
false
false
+ 0
9786
@@ -78294,6 +88080,7 @@
false
false
false
+ 0
9787
@@ -78302,6 +88089,7 @@
false
false
false
+ 0
9788
@@ -78310,6 +88098,7 @@
false
false
false
+ 0
9789
@@ -78318,6 +88107,7 @@
false
false
false
+ 0
9790
@@ -78326,6 +88116,7 @@
false
false
false
+ 0
9791
@@ -78334,6 +88125,7 @@
false
false
false
+ 0
9792
@@ -78342,6 +88134,7 @@
false
false
false
+ 0
9793
@@ -78350,6 +88143,7 @@
false
false
false
+ 0
9794
@@ -78358,6 +88152,7 @@
false
false
false
+ 0
9795
@@ -78366,6 +88161,7 @@
false
false
false
+ 0
9796
@@ -78374,6 +88170,7 @@
false
false
false
+ 0
9797
@@ -78382,6 +88179,7 @@
false
false
false
+ 0
9798
@@ -78390,6 +88188,7 @@
false
false
false
+ 0
9799
@@ -78398,6 +88197,7 @@
false
false
false
+ 0
9800
@@ -78406,6 +88206,7 @@
false
false
false
+ 0
9801
@@ -78414,6 +88215,7 @@
false
false
false
+ 0
9802
@@ -78422,6 +88224,7 @@
false
false
false
+ 0
9803
@@ -78430,6 +88233,7 @@
false
false
false
+ 0
9804
@@ -78438,6 +88242,7 @@
false
false
false
+ 0
9805
@@ -78446,6 +88251,7 @@
false
false
false
+ 0
9806
@@ -78454,6 +88260,7 @@
false
false
false
+ 0
9807
@@ -78462,6 +88269,7 @@
false
false
false
+ 0
9808
@@ -78470,6 +88278,7 @@
false
false
false
+ 0
9809
@@ -78478,6 +88287,7 @@
false
false
false
+ 0
9810
@@ -78486,6 +88296,7 @@
false
false
false
+ 0
9811
@@ -78494,6 +88305,7 @@
false
false
false
+ 0
9812
@@ -78502,6 +88314,7 @@
false
false
false
+ 0
9813
@@ -78510,6 +88323,7 @@
false
false
false
+ 0
9814
@@ -78518,6 +88332,7 @@
false
false
false
+ 0
9815
@@ -78526,6 +88341,7 @@
false
false
false
+ 0
9816
@@ -78534,6 +88350,7 @@
false
false
false
+ 0
9817
@@ -78542,6 +88359,7 @@
false
false
false
+ 0
9818
@@ -78550,6 +88368,7 @@
false
false
false
+ 0
9819
@@ -78558,6 +88377,7 @@
false
false
false
+ 0
9820
@@ -78566,6 +88386,7 @@
false
false
false
+ 0
9821
@@ -78574,6 +88395,7 @@
false
false
false
+ 0
9822
@@ -78582,6 +88404,7 @@
false
false
false
+ 0
9823
@@ -78590,6 +88413,7 @@
false
false
false
+ 0
9824
@@ -78598,6 +88422,7 @@
false
false
false
+ 0
9825
@@ -78606,6 +88431,7 @@
false
false
false
+ 0
9826
@@ -78614,6 +88440,7 @@
false
false
false
+ 0
9827
@@ -78622,6 +88449,7 @@
false
false
false
+ 0
9828
@@ -78630,6 +88458,7 @@
false
false
false
+ 0
9829
@@ -78638,6 +88467,7 @@
false
false
false
+ 0
9830
@@ -78646,6 +88476,7 @@
false
false
false
+ 0
9831
@@ -78654,6 +88485,7 @@
false
false
false
+ 0
9832
@@ -78662,6 +88494,7 @@
false
false
false
+ 0
9833
@@ -78670,6 +88503,7 @@
false
false
false
+ 0
9834
@@ -78678,6 +88512,7 @@
false
false
false
+ 0
9835
@@ -78686,6 +88521,7 @@
false
false
false
+ 0
9836
@@ -78694,6 +88530,7 @@
false
false
false
+ 0
9837
@@ -78702,6 +88539,7 @@
false
false
false
+ 0
9838
@@ -78710,6 +88548,7 @@
false
false
false
+ 0
9839
@@ -78718,6 +88557,7 @@
false
false
false
+ 0
9840
@@ -78726,6 +88566,7 @@
false
false
false
+ 0
9841
@@ -78734,6 +88575,7 @@
false
false
false
+ 0
9842
@@ -78742,6 +88584,7 @@
false
false
false
+ 0
9843
@@ -78750,6 +88593,7 @@
false
false
false
+ 0
9844
@@ -78758,6 +88602,7 @@
false
false
false
+ 0
9845
@@ -78766,6 +88611,7 @@
false
false
false
+ 0
9846
@@ -78774,6 +88620,7 @@
false
false
false
+ 0
9847
@@ -78782,6 +88629,7 @@
false
false
false
+ 0
9848
@@ -78790,6 +88638,7 @@
false
false
false
+ 0
9849
@@ -78798,6 +88647,7 @@
false
false
false
+ 0
9850
@@ -78806,6 +88656,7 @@
false
false
false
+ 0
9851
@@ -78814,6 +88665,7 @@
false
false
false
+ 0
9852
@@ -78822,6 +88674,7 @@
false
false
false
+ 0
9853
@@ -78830,6 +88683,7 @@
false
false
false
+ 0
9854
@@ -78838,6 +88692,7 @@
false
false
false
+ 0
9855
@@ -78846,6 +88701,7 @@
false
false
false
+ 0
9856
@@ -78854,6 +88710,7 @@
false
false
false
+ 0
9857
@@ -78862,6 +88719,7 @@
false
false
false
+ 0
9858
@@ -78870,6 +88728,7 @@
false
false
false
+ 0
9859
@@ -78878,6 +88737,7 @@
false
false
false
+ 0
9860
@@ -78886,6 +88746,7 @@
false
false
false
+ 0
9861
@@ -78894,6 +88755,7 @@
false
false
false
+ 0
9862
@@ -78902,6 +88764,7 @@
false
false
false
+ 0
9863
@@ -78910,6 +88773,7 @@
false
false
false
+ 0
9864
@@ -78918,6 +88782,7 @@
false
false
false
+ 0
9865
@@ -78926,6 +88791,7 @@
false
false
false
+ 0
9866
@@ -78934,6 +88800,7 @@
false
false
false
+ 0
9867
@@ -78942,6 +88809,7 @@
false
false
false
+ 0
9868
@@ -78950,6 +88818,7 @@
false
false
false
+ 0
9869
@@ -78958,6 +88827,7 @@
false
false
false
+ 0
9870
@@ -78966,6 +88836,7 @@
false
false
false
+ 0
9871
@@ -78974,6 +88845,7 @@
false
false
false
+ 0
9872
@@ -78982,6 +88854,7 @@
false
false
false
+ 0
9873
@@ -78990,6 +88863,7 @@
false
false
false
+ 0
9874
@@ -78998,6 +88872,7 @@
false
false
false
+ 0
9875
@@ -79006,6 +88881,7 @@
false
false
false
+ 0
9876
@@ -79014,6 +88890,7 @@
false
false
false
+ 0
9877
@@ -79022,6 +88899,7 @@
false
false
false
+ 0
9878
@@ -79030,6 +88908,7 @@
false
false
false
+ 0
9879
@@ -79038,6 +88917,7 @@
false
false
false
+ 0
9880
@@ -79046,6 +88926,7 @@
false
false
false
+ 0
9881
@@ -79054,6 +88935,7 @@
false
false
false
+ 0
9882
@@ -79062,6 +88944,7 @@
false
false
false
+ 0
9883
@@ -79070,6 +88953,7 @@
false
false
false
+ 0
9884
@@ -79078,6 +88962,7 @@
false
false
false
+ 0
9885
@@ -79086,6 +88971,7 @@
false
false
false
+ 0
9886
@@ -79094,6 +88980,7 @@
false
false
false
+ 0
9887
@@ -79102,6 +88989,7 @@
false
false
false
+ 0
9888
@@ -79110,6 +88998,7 @@
false
false
false
+ 0
9889
@@ -79118,6 +89007,7 @@
false
false
false
+ 0
9890
@@ -79126,6 +89016,7 @@
false
false
false
+ 0
9891
@@ -79134,6 +89025,7 @@
false
false
false
+ 0
9892
@@ -79142,6 +89034,7 @@
false
false
false
+ 0
9893
@@ -79150,6 +89043,7 @@
false
false
false
+ 0
9894
@@ -79158,6 +89052,7 @@
false
false
false
+ 0
9895
@@ -79166,6 +89061,7 @@
false
false
false
+ 0
9896
@@ -79174,6 +89070,7 @@
false
false
false
+ 0
9897
@@ -79182,6 +89079,7 @@
false
false
false
+ 0
9898
@@ -79190,6 +89088,7 @@
false
false
false
+ 0
9899
@@ -79198,6 +89097,7 @@
false
false
false
+ 0
9900
@@ -79206,6 +89106,7 @@
false
false
false
+ 0
9901
@@ -79214,6 +89115,7 @@
false
false
false
+ 0
9902
@@ -79222,6 +89124,7 @@
false
false
false
+ 0
9903
@@ -79230,6 +89133,7 @@
false
false
false
+ 0
9904
@@ -79238,6 +89142,7 @@
false
false
false
+ 0
9905
@@ -79246,6 +89151,7 @@
false
false
false
+ 0
9906
@@ -79254,6 +89160,7 @@
false
false
false
+ 0
9907
@@ -79262,6 +89169,7 @@
false
false
false
+ 0
9908
@@ -79270,6 +89178,7 @@
false
false
false
+ 0
9909
@@ -79278,6 +89187,7 @@
false
false
false
+ 0
9910
@@ -79286,6 +89196,7 @@
false
false
false
+ 0
9911
@@ -79294,6 +89205,7 @@
false
false
false
+ 0
9912
@@ -79302,6 +89214,7 @@
false
false
false
+ 0
9913
@@ -79310,6 +89223,7 @@
false
false
false
+ 0
9914
@@ -79318,6 +89232,7 @@
false
false
false
+ 0
9915
@@ -79326,6 +89241,7 @@
false
false
false
+ 0
9916
@@ -79334,6 +89250,7 @@
false
false
false
+ 0
9917
@@ -79342,6 +89259,7 @@
false
false
false
+ 0
9918
@@ -79350,6 +89268,7 @@
false
false
false
+ 0
9919
@@ -79358,6 +89277,7 @@
false
false
false
+ 0
9920
@@ -79366,6 +89286,7 @@
false
false
false
+ 0
9921
@@ -79374,6 +89295,7 @@
false
false
false
+ 0
9922
@@ -79382,6 +89304,7 @@
false
false
false
+ 0
9923
@@ -79390,6 +89313,7 @@
false
false
false
+ 0
9924
@@ -79398,6 +89322,7 @@
false
false
false
+ 0
9925
@@ -79406,6 +89331,7 @@
false
false
false
+ 0
9926
@@ -79414,6 +89340,7 @@
false
false
false
+ 0
9927
@@ -79422,6 +89349,7 @@
false
false
false
+ 0
9928
@@ -79430,6 +89358,7 @@
false
false
false
+ 0
9929
@@ -79438,6 +89367,7 @@
false
false
false
+ 0
9930
@@ -79446,6 +89376,7 @@
false
false
false
+ 0
9931
@@ -79454,6 +89385,7 @@
false
false
false
+ 0
9932
@@ -79462,6 +89394,7 @@
false
false
false
+ 0
9933
@@ -79470,6 +89403,7 @@
false
false
false
+ 0
9934
@@ -79478,6 +89412,7 @@
false
false
false
+ 0
9935
@@ -79486,6 +89421,7 @@
false
false
false
+ 0
9936
@@ -79494,6 +89430,7 @@
false
false
false
+ 0
9937
@@ -79502,6 +89439,7 @@
false
false
false
+ 0
9938
@@ -79510,6 +89448,7 @@
false
false
false
+ 0
9939
@@ -79518,6 +89457,7 @@
false
false
false
+ 0
9940
@@ -79526,6 +89466,7 @@
false
false
false
+ 0
9941
@@ -79534,6 +89475,7 @@
false
false
false
+ 0
9942
@@ -79542,6 +89484,7 @@
false
false
false
+ 0
9943
@@ -79550,6 +89493,7 @@
false
false
false
+ 0
9944
@@ -79558,6 +89502,7 @@
false
false
false
+ 0
9945
@@ -79566,6 +89511,7 @@
false
false
false
+ 0
9946
@@ -79574,6 +89520,7 @@
false
false
false
+ 0
9947
@@ -79582,6 +89529,7 @@
false
false
false
+ 0
9948
@@ -79590,6 +89538,7 @@
false
false
false
+ 0
9949
@@ -79598,6 +89547,7 @@
false
false
false
+ 0
9950
@@ -79606,6 +89556,7 @@
false
false
false
+ 0
9951
@@ -79614,6 +89565,7 @@
false
false
false
+ 0
9952
@@ -79622,6 +89574,7 @@
false
false
false
+ 0
9953
@@ -79630,6 +89583,7 @@
false
false
false
+ 0
9954
@@ -79638,6 +89592,7 @@
false
false
false
+ 0
9955
@@ -79646,6 +89601,7 @@
false
false
false
+ 0
9956
@@ -79654,6 +89610,7 @@
false
false
false
+ 0
9957
@@ -79662,6 +89619,7 @@
false
false
false
+ 0
9958
@@ -79670,6 +89628,7 @@
false
false
false
+ 0
9959
@@ -79678,6 +89637,7 @@
false
false
false
+ 0
9960
@@ -79686,6 +89646,7 @@
false
false
false
+ 0
9961
@@ -79694,6 +89655,7 @@
false
false
false
+ 0
9962
@@ -79702,6 +89664,7 @@
false
false
false
+ 0
9963
@@ -79710,6 +89673,7 @@
false
false
false
+ 0
9964
@@ -79718,6 +89682,7 @@
false
false
false
+ 0
9965
@@ -79726,6 +89691,7 @@
false
false
false
+ 0
9966
@@ -79734,6 +89700,7 @@
false
false
false
+ 0
9967
@@ -79742,6 +89709,7 @@
false
false
false
+ 0
9968
@@ -79750,6 +89718,7 @@
false
false
false
+ 0
9969
@@ -79758,6 +89727,7 @@
false
false
false
+ 0
9970
@@ -79766,6 +89736,7 @@
false
false
false
+ 0
9971
@@ -79774,6 +89745,7 @@
false
false
false
+ 0
9972
@@ -79782,6 +89754,7 @@
false
false
false
+ 0
9973
@@ -79790,6 +89763,7 @@
false
false
false
+ 0
9974
@@ -79798,6 +89772,7 @@
false
false
false
+ 0
9975
@@ -79806,6 +89781,7 @@
false
false
false
+ 0
9976
@@ -79814,6 +89790,7 @@
false
false
false
+ 0
9977
@@ -79822,6 +89799,7 @@
false
false
false
+ 0
9978
@@ -79830,6 +89808,7 @@
false
false
false
+ 0
9979
@@ -79838,6 +89817,7 @@
false
false
false
+ 0
9980
@@ -79846,6 +89826,7 @@
false
false
false
+ 0
9981
@@ -79854,6 +89835,7 @@
false
false
false
+ 0
9982
@@ -79862,6 +89844,7 @@
false
false
false
+ 0
9983
@@ -79870,6 +89853,7 @@
false
false
false
+ 0
9984
@@ -79878,6 +89862,7 @@
false
false
false
+ 0
9985
@@ -79886,6 +89871,7 @@
false
false
false
+ 0
9986
@@ -79894,6 +89880,7 @@
false
false
false
+ 0
9987
@@ -79902,6 +89889,7 @@
false
false
false
+ 0
9988
@@ -79910,6 +89898,7 @@
false
false
false
+ 0
9989
@@ -79918,6 +89907,7 @@
false
false
false
+ 0
9990
@@ -79926,6 +89916,7 @@
false
false
false
+ 0
9991
@@ -79934,6 +89925,7 @@
false
false
false
+ 0
9992
@@ -79942,6 +89934,7 @@
false
false
false
+ 0
9993
@@ -79950,6 +89943,7 @@
false
false
false
+ 0
9994
@@ -79958,6 +89952,7 @@
false
false
false
+ 0
9995
@@ -79966,6 +89961,7 @@
false
false
false
+ 0
9996
@@ -79974,6 +89970,7 @@
false
false
false
+ 0
9997
@@ -79982,6 +89979,7 @@
false
false
false
+ 0
9998
@@ -79990,6 +89988,7 @@
false
false
false
+ 0
9999
@@ -79998,6 +89997,7 @@
false
false
false
+ 0
10000
@@ -80006,6 +90006,7 @@
false
false
false
+ 0
10001
@@ -80014,6 +90015,7 @@
false
false
false
+ 0
10002
@@ -80022,6 +90024,7 @@
false
false
false
+ 0
10003
@@ -80030,6 +90033,7 @@
false
false
false
+ 0
10004
@@ -80038,6 +90042,7 @@
false
false
false
+ 0
10005
@@ -80046,6 +90051,7 @@
false
false
false
+ 0
10006
@@ -80054,6 +90060,7 @@
false
false
false
+ 0
10007
@@ -80062,6 +90069,7 @@
false
false
false
+ 0
10008
@@ -80070,6 +90078,7 @@
false
false
false
+ 0
10009
@@ -80078,6 +90087,7 @@
false
false
false
+ 0
10010
@@ -80086,6 +90096,7 @@
false
false
false
+ 0
10011
@@ -80094,6 +90105,7 @@
false
false
false
+ 0
10012
@@ -80102,6 +90114,7 @@
false
false
false
+ 0
10013
@@ -80110,6 +90123,7 @@
false
false
false
+ 0
10014
@@ -80118,6 +90132,7 @@
false
false
false
+ 0
10015
@@ -80126,6 +90141,7 @@
false
false
false
+ 0
10016
@@ -80134,6 +90150,7 @@
false
false
false
+ 0
10017
@@ -80142,6 +90159,7 @@
false
false
false
+ 0
10018
@@ -80150,6 +90168,7 @@
false
false
false
+ 0
10019
@@ -80158,6 +90177,7 @@
false
false
false
+ 0
10020
@@ -80166,6 +90186,7 @@
false
false
false
+ 0
10021
@@ -80174,6 +90195,7 @@
false
false
false
+ 0
10022
@@ -80182,6 +90204,7 @@
false
false
false
+ 0
10023
@@ -80190,6 +90213,7 @@
false
false
false
+ 0
10024
@@ -80198,6 +90222,7 @@
false
false
false
+ 0
10025
@@ -80206,6 +90231,7 @@
false
false
false
+ 0
10026
@@ -80214,6 +90240,7 @@
false
false
false
+ 0
10027
@@ -80222,6 +90249,7 @@
false
false
false
+ 0
10028
@@ -80230,6 +90258,7 @@
false
false
false
+ 0
10029
@@ -80238,6 +90267,7 @@
false
false
false
+ 0
10030
@@ -80246,6 +90276,7 @@
false
false
false
+ 0
10031
@@ -80254,6 +90285,7 @@
false
false
false
+ 0
10032
@@ -80262,6 +90294,7 @@
false
false
false
+ 0
10033
@@ -80270,6 +90303,7 @@
false
false
false
+ 0
10034
@@ -80278,6 +90312,7 @@
false
false
false
+ 0
10035
@@ -80286,6 +90321,7 @@
false
false
false
+ 0
10036
@@ -80294,6 +90330,7 @@
false
false
false
+ 0
10037
@@ -80302,6 +90339,7 @@
false
false
false
+ 0
10038
@@ -80310,6 +90348,7 @@
false
false
false
+ 0
10039
@@ -80318,6 +90357,7 @@
false
false
false
+ 0
10040
@@ -80326,6 +90366,7 @@
false
false
false
+ 0
10041
@@ -80334,6 +90375,7 @@
false
false
false
+ 0
10042
@@ -80342,6 +90384,7 @@
false
false
false
+ 0
10043
@@ -80350,6 +90393,7 @@
false
false
false
+ 0
10044
@@ -80358,6 +90402,7 @@
false
false
false
+ 0
10045
@@ -80366,6 +90411,7 @@
false
false
false
+ 0
10046
@@ -80374,6 +90420,7 @@
false
false
false
+ 0
10047
@@ -80382,6 +90429,7 @@
false
false
false
+ 0
10048
@@ -80390,6 +90438,7 @@
false
false
false
+ 0
10049
@@ -80398,6 +90447,7 @@
false
false
false
+ 0
10050
@@ -80406,6 +90456,7 @@
false
false
false
+ 0
10051
@@ -80414,6 +90465,7 @@
false
false
false
+ 0
10052
@@ -80422,6 +90474,7 @@
false
false
false
+ 0
10053
@@ -80430,6 +90483,7 @@
false
false
false
+ 0
10054
@@ -80438,6 +90492,7 @@
false
false
false
+ 0
10055
@@ -80446,6 +90501,7 @@
false
false
false
+ 0
10056
@@ -80454,6 +90510,7 @@
false
false
false
+ 0
10057
@@ -80462,6 +90519,7 @@
false
false
false
+ 0
10058
@@ -80470,6 +90528,7 @@
false
false
false
+ 0
10059
@@ -80478,6 +90537,7 @@
false
false
false
+ 0
10060
@@ -80486,6 +90546,7 @@
false
false
false
+ 0
10061
@@ -80494,6 +90555,7 @@
false
false
false
+ 0
10062
@@ -80502,6 +90564,7 @@
false
false
false
+ 0
10063
@@ -80510,6 +90573,7 @@
false
false
false
+ 0
10064
@@ -80518,6 +90582,7 @@
false
false
false
+ 0
10065
@@ -80526,6 +90591,7 @@
false
false
false
+ 0
10066
@@ -80534,6 +90600,7 @@
false
false
false
+ 0
10067
@@ -80542,6 +90609,7 @@
false
false
false
+ 0
10068
@@ -80550,6 +90618,7 @@
false
false
false
+ 0
10069
@@ -80558,6 +90627,7 @@
false
false
false
+ 0
10070
@@ -80566,6 +90636,7 @@
false
false
false
+ 0
10071
@@ -80574,6 +90645,7 @@
false
false
false
+ 0
10072
@@ -80582,6 +90654,7 @@
false
false
false
+ 0
10073
@@ -80590,6 +90663,7 @@
false
false
false
+ 0
10074
@@ -80598,6 +90672,7 @@
false
false
false
+ 0
10075
@@ -80606,6 +90681,7 @@
false
false
false
+ 0
10076
@@ -80614,6 +90690,7 @@
false
false
false
+ 0
10077
@@ -80622,6 +90699,7 @@
false
false
false
+ 0
10078
@@ -80630,6 +90708,7 @@
false
false
false
+ 0
10079
@@ -80638,6 +90717,7 @@
false
false
false
+ 0
10080
@@ -80646,6 +90726,7 @@
false
false
false
+ 0
10081
@@ -80654,6 +90735,7 @@
false
false
false
+ 0
10082
@@ -80662,6 +90744,7 @@
false
false
false
+ 0
10083
@@ -80670,6 +90753,7 @@
false
false
false
+ 0
10084
@@ -80678,6 +90762,7 @@
false
false
false
+ 0
10085
@@ -80686,6 +90771,7 @@
false
false
false
+ 0
10086
@@ -80694,6 +90780,7 @@
false
false
false
+ 0
10087
@@ -80702,6 +90789,7 @@
false
false
false
+ 0
10088
@@ -80710,6 +90798,7 @@
false
false
false
+ 0
10089
@@ -80718,6 +90807,7 @@
false
false
false
+ 0
10090
@@ -80726,6 +90816,7 @@
false
false
false
+ 0
10091
@@ -80734,6 +90825,7 @@
false
false
false
+ 0
10092
@@ -80742,6 +90834,7 @@
false
false
false
+ 0
10093
@@ -80750,6 +90843,7 @@
false
false
false
+ 0
10094
@@ -80758,6 +90852,7 @@
false
false
false
+ 0
10095
@@ -80766,6 +90861,7 @@
false
false
false
+ 0
10096
@@ -80774,6 +90870,7 @@
false
false
false
+ 0
10097
@@ -80782,6 +90879,7 @@
false
false
false
+ 0
10098
@@ -80790,6 +90888,7 @@
false
false
false
+ 0
10099
@@ -80798,6 +90897,7 @@
false
false
false
+ 0
10100
@@ -80806,6 +90906,7 @@
false
false
false
+ 0
10101
@@ -80814,6 +90915,7 @@
false
false
false
+ 0
10102
@@ -80822,6 +90924,7 @@
false
false
false
+ 0
10103
@@ -80830,6 +90933,7 @@
false
false
false
+ 0
10104
@@ -80838,6 +90942,7 @@
false
false
false
+ 0
10105
@@ -80846,6 +90951,7 @@
false
false
false
+ 0
10106
@@ -80854,6 +90960,7 @@
false
false
false
+ 0
10107
@@ -80862,6 +90969,7 @@
false
false
false
+ 0
10108
@@ -80870,6 +90978,7 @@
false
false
false
+ 0
10109
@@ -80878,6 +90987,7 @@
false
false
false
+ 0
10110
@@ -80886,6 +90996,7 @@
false
false
false
+ 0
10111
@@ -80894,6 +91005,7 @@
false
false
false
+ 0
10112
@@ -80902,6 +91014,7 @@
false
false
false
+ 0
10113
@@ -80910,6 +91023,7 @@
false
false
false
+ 0
10114
@@ -80918,6 +91032,7 @@
false
false
false
+ 0
10115
@@ -80926,6 +91041,7 @@
false
false
false
+ 0
10116
@@ -80934,6 +91050,7 @@
false
false
false
+ 0
10117
@@ -80942,6 +91059,7 @@
false
false
false
+ 0
10118
@@ -80950,6 +91068,7 @@
false
false
false
+ 0
10119
@@ -80958,6 +91077,7 @@
false
false
false
+ 0
10120
@@ -80966,6 +91086,7 @@
false
false
false
+ 0
10121
@@ -80974,6 +91095,7 @@
false
false
false
+ 0
10122
@@ -80982,6 +91104,7 @@
false
false
false
+ 0
10123
@@ -80990,6 +91113,7 @@
false
false
false
+ 0
10124
@@ -80998,6 +91122,7 @@
false
false
false
+ 0
10125
@@ -81006,6 +91131,7 @@
false
false
false
+ 0
10126
@@ -81014,6 +91140,7 @@
false
false
false
+ 0
10127
@@ -81022,6 +91149,7 @@
false
false
false
+ 0
10128
@@ -81030,6 +91158,7 @@
false
false
false
+ 0
10129
@@ -81038,6 +91167,7 @@
false
false
false
+ 0
10130
@@ -81046,6 +91176,7 @@
false
false
false
+ 0
10131
@@ -81054,6 +91185,7 @@
false
false
false
+ 0
10132
@@ -81062,6 +91194,7 @@
false
false
false
+ 0
10133
@@ -81070,6 +91203,7 @@
false
false
false
+ 0
10134
@@ -81078,6 +91212,7 @@
false
false
false
+ 0
10135
@@ -81086,6 +91221,7 @@
false
false
false
+ 0
10136
@@ -81094,6 +91230,7 @@
false
false
false
+ 0
10137
@@ -81102,6 +91239,7 @@
false
false
false
+ 0
10138
@@ -81110,6 +91248,7 @@
false
false
false
+ 0
10139
@@ -81118,6 +91257,7 @@
false
false
false
+ 0
10140
@@ -81126,6 +91266,7 @@
false
false
false
+ 0
10141
@@ -81134,6 +91275,7 @@
false
false
false
+ 0
10142
@@ -81142,6 +91284,7 @@
false
false
false
+ 0
10143
@@ -81150,6 +91293,7 @@
false
false
false
+ 0
10144
@@ -81158,6 +91302,7 @@
false
false
false
+ 0
10145
@@ -81166,6 +91311,7 @@
false
false
false
+ 0
10146
@@ -81174,6 +91320,7 @@
false
false
false
+ 0
10147
@@ -81182,6 +91329,7 @@
false
false
false
+ 0
10148
@@ -81190,6 +91338,7 @@
false
false
false
+ 0
10149
@@ -81198,6 +91347,7 @@
false
false
false
+ 0
10150
@@ -81206,6 +91356,7 @@
false
false
false
+ 0
10151
@@ -81214,6 +91365,7 @@
false
false
false
+ 0
10152
@@ -81222,6 +91374,7 @@
false
false
false
+ 0
10153
@@ -81230,6 +91383,7 @@
false
false
false
+ 0
10154
@@ -81238,6 +91392,7 @@
false
false
false
+ 0
10155
@@ -81246,6 +91401,7 @@
false
false
false
+ 0
10156
@@ -81254,6 +91410,7 @@
false
false
false
+ 0
10157
@@ -81262,6 +91419,7 @@
false
false
false
+ 0
10158
@@ -81270,6 +91428,7 @@
false
false
false
+ 0
10159
@@ -81278,6 +91437,7 @@
false
false
false
+ 0
10160
@@ -81286,6 +91446,7 @@
false
false
false
+ 0
10161
@@ -81294,6 +91455,7 @@
false
false
false
+ 0
10162
@@ -81302,6 +91464,7 @@
false
false
false
+ 0
10163
@@ -81310,6 +91473,7 @@
false
false
false
+ 0
10164
@@ -81318,6 +91482,7 @@
false
false
false
+ 0
10165
@@ -81326,6 +91491,7 @@
false
false
false
+ 0
10166
@@ -81334,6 +91500,7 @@
false
false
false
+ 0
10167
@@ -81342,6 +91509,7 @@
false
false
false
+ 0
10168
@@ -81350,6 +91518,7 @@
false
false
false
+ 0
10169
@@ -81358,6 +91527,7 @@
false
false
false
+ 0
10170
@@ -81366,6 +91536,7 @@
false
false
false
+ 0
10171
@@ -81374,6 +91545,7 @@
false
false
false
+ 0
10172
@@ -81382,6 +91554,7 @@
false
false
false
+ 0
10173
@@ -81390,6 +91563,7 @@
false
false
false
+ 0
10174
@@ -81398,6 +91572,7 @@
false
false
false
+ 0
10175
@@ -81406,6 +91581,7 @@
false
false
false
+ 0
10176
@@ -81414,6 +91590,7 @@
false
false
false
+ 0
10177
@@ -81422,6 +91599,7 @@
false
false
false
+ 0
10178
@@ -81430,6 +91608,7 @@
false
false
false
+ 0
10179
@@ -81438,6 +91617,7 @@
false
false
false
+ 0
10180
@@ -81446,6 +91626,7 @@
false
false
false
+ 0
10181
@@ -81454,6 +91635,7 @@
false
false
false
+ 0
10182
@@ -81462,6 +91644,7 @@
false
false
false
+ 0
10183
@@ -81470,6 +91653,7 @@
false
false
false
+ 0
10184
@@ -81478,6 +91662,7 @@
false
false
false
+ 0
10185
@@ -81486,6 +91671,7 @@
false
false
false
+ 0
10186
@@ -81494,6 +91680,7 @@
false
false
false
+ 0
10187
@@ -81502,6 +91689,7 @@
false
false
false
+ 0
10188
@@ -81510,6 +91698,7 @@
false
false
false
+ 0
10189
@@ -81518,6 +91707,7 @@
false
false
false
+ 0
10190
@@ -81526,6 +91716,7 @@
false
false
false
+ 0
10191
@@ -81534,6 +91725,7 @@
false
false
false
+ 0
10192
@@ -81542,6 +91734,7 @@
false
false
false
+ 0
10193
@@ -81550,6 +91743,7 @@
false
false
false
+ 0
10194
@@ -81558,6 +91752,7 @@
false
false
false
+ 0
10195
@@ -81566,6 +91761,7 @@
false
false
false
+ 0
10196
@@ -81574,6 +91770,7 @@
false
false
false
+ 0
10197
@@ -81582,6 +91779,7 @@
false
false
false
+ 0
10198
@@ -81590,6 +91788,7 @@
false
false
false
+ 0
10199
@@ -81598,6 +91797,7 @@
false
false
false
+ 0
10200
@@ -81606,6 +91806,7 @@
false
false
false
+ 0
10201
@@ -81614,6 +91815,7 @@
false
false
false
+ 0
10202
@@ -81622,6 +91824,7 @@
false
false
false
+ 0
10203
@@ -81630,6 +91833,7 @@
false
false
false
+ 0
10204
@@ -81638,6 +91842,7 @@
false
false
false
+ 0
10205
@@ -81646,6 +91851,7 @@
false
false
false
+ 0
10206
@@ -81654,6 +91860,7 @@
false
false
false
+ 0
10207
@@ -81662,6 +91869,7 @@
false
false
false
+ 0
10208
@@ -81670,6 +91878,7 @@
false
false
false
+ 0
10209
@@ -81678,6 +91887,7 @@
false
false
false
+ 0
10210
@@ -81686,6 +91896,7 @@
false
false
false
+ 0
10211
@@ -81694,6 +91905,7 @@
false
false
false
+ 0
10212
@@ -81702,6 +91914,7 @@
false
false
false
+ 0
10213
@@ -81710,6 +91923,7 @@
false
false
false
+ 0
10214
@@ -81718,6 +91932,7 @@
false
false
false
+ 0
10215
@@ -81726,6 +91941,7 @@
false
false
false
+ 0
10216
@@ -81734,6 +91950,7 @@
false
false
false
+ 0
10217
@@ -81742,6 +91959,7 @@
false
false
false
+ 0
10218
@@ -81750,6 +91968,7 @@
false
false
false
+ 0
10219
@@ -81758,6 +91977,7 @@
false
false
false
+ 0
10220
@@ -81766,6 +91986,7 @@
false
false
false
+ 0
10221
@@ -81774,6 +91995,7 @@
false
false
false
+ 0
10222
@@ -81782,6 +92004,7 @@
false
false
false
+ 0
10223
@@ -81790,6 +92013,7 @@
false
false
false
+ 0
10224
@@ -81798,6 +92022,7 @@
false
false
false
+ 0
10225
@@ -81806,6 +92031,7 @@
false
false
false
+ 0
10226
@@ -81814,6 +92040,7 @@
false
false
false
+ 0
10227
@@ -81822,6 +92049,7 @@
false
false
false
+ 0
10228
@@ -81830,6 +92058,7 @@
false
false
false
+ 0
10229
@@ -81838,6 +92067,7 @@
false
false
false
+ 0
10230
@@ -81846,6 +92076,7 @@
false
false
false
+ 0
10231
@@ -81854,6 +92085,7 @@
false
false
false
+ 0
10232
@@ -81862,6 +92094,7 @@
false
false
false
+ 0
10233
@@ -81870,6 +92103,7 @@
false
false
false
+ 0
10234
@@ -81878,6 +92112,7 @@
false
false
false
+ 0
10235
@@ -81886,6 +92121,7 @@
false
false
false
+ 0
10236
@@ -81894,6 +92130,7 @@
false
false
false
+ 0
10237
@@ -81902,6 +92139,7 @@
false
false
false
+ 0
10238
@@ -81910,6 +92148,7 @@
false
false
false
+ 0
10239
@@ -81918,6 +92157,7 @@
false
false
false
+ 0
10240
@@ -81926,6 +92166,7 @@
false
false
false
+ 0
10241
@@ -81934,6 +92175,7 @@
false
false
false
+ 0
10242
@@ -81942,6 +92184,7 @@
false
false
false
+ 0
10243
@@ -81950,6 +92193,7 @@
false
false
false
+ 0
10244
@@ -81958,6 +92202,7 @@
false
false
false
+ 0
10245
@@ -81966,6 +92211,7 @@
false
false
false
+ 0
10246
@@ -81974,6 +92220,7 @@
false
false
false
+ 0
10247
@@ -81982,6 +92229,7 @@
false
false
false
+ 0
10248
@@ -81990,6 +92238,7 @@
false
false
false
+ 0
10249
@@ -81998,6 +92247,7 @@
false
false
false
+ 0
10250
@@ -82006,6 +92256,7 @@
false
false
false
+ 0
10251
@@ -82014,6 +92265,7 @@
false
false
false
+ 0
10252
@@ -82022,6 +92274,7 @@
false
false
false
+ 0
10253
@@ -82030,6 +92283,7 @@
false
false
false
+ 0
10254
@@ -82038,6 +92292,7 @@
false
false
false
+ 0
10255
@@ -82046,6 +92301,7 @@
false
false
false
+ 0
10256
@@ -82054,6 +92310,7 @@
false
false
false
+ 0
10257
@@ -82062,6 +92319,7 @@
false
false
false
+ 0
10258
@@ -82070,6 +92328,7 @@
false
false
false
+ 0
10259
@@ -82078,6 +92337,7 @@
false
false
false
+ 0
10260
@@ -82086,6 +92346,7 @@
false
false
false
+ 0
10261
@@ -82094,6 +92355,7 @@
false
false
false
+ 0
10262
@@ -82102,6 +92364,7 @@
false
false
false
+ 0
10263
@@ -82110,6 +92373,7 @@
false
false
false
+ 0
10264
@@ -82118,6 +92382,7 @@
false
false
false
+ 0
10265
@@ -82126,6 +92391,7 @@
false
false
false
+ 0
10266
@@ -82134,6 +92400,7 @@
false
false
false
+ 0
10267
@@ -82142,6 +92409,7 @@
false
false
false
+ 0
10268
@@ -82150,6 +92418,7 @@
false
false
false
+ 0
10269
@@ -82158,6 +92427,7 @@
false
false
false
+ 0
10270
@@ -82166,6 +92436,7 @@
false
false
false
+ 0
10271
@@ -82174,6 +92445,7 @@
false
false
false
+ 0
10272
@@ -82182,6 +92454,7 @@
false
false
false
+ 0
10273
@@ -82190,6 +92463,7 @@
false
false
false
+ 0
10274
@@ -82198,6 +92472,7 @@
false
false
false
+ 0
10275
@@ -82206,6 +92481,7 @@
false
false
false
+ 0
10276
@@ -82214,6 +92490,7 @@
false
false
false
+ 0
10277
@@ -82222,6 +92499,7 @@
false
false
false
+ 0
10278
@@ -82230,6 +92508,7 @@
false
false
false
+ 0
10279
@@ -82238,6 +92517,7 @@
false
false
false
+ 0
10280
@@ -82246,6 +92526,7 @@
false
false
false
+ 0
10281
@@ -82254,6 +92535,7 @@
false
false
false
+ 0
10282
@@ -82262,6 +92544,7 @@
false
false
false
+ 0
10283
@@ -82270,6 +92553,7 @@
false
false
false
+ 0
10284
@@ -82278,6 +92562,7 @@
false
false
false
+ 0
10285
@@ -82286,6 +92571,7 @@
false
false
false
+ 0
10286
@@ -82294,6 +92580,7 @@
false
false
false
+ 0
10287
@@ -82302,6 +92589,7 @@
false
false
false
+ 0
10288
@@ -82310,6 +92598,7 @@
false
false
false
+ 0
10289
@@ -82318,6 +92607,7 @@
false
false
false
+ 0
10290
@@ -82326,6 +92616,7 @@
false
false
false
+ 0
10291
@@ -82334,6 +92625,7 @@
false
false
false
+ 0
10292
@@ -82342,6 +92634,7 @@
false
false
false
+ 0
10293
@@ -82350,6 +92643,7 @@
false
false
false
+ 0
10294
@@ -82358,6 +92652,7 @@
false
false
false
+ 0
10295
@@ -82366,6 +92661,7 @@
false
false
false
+ 0
10296
@@ -82374,6 +92670,7 @@
false
false
false
+ 0
10297
@@ -82382,6 +92679,7 @@
false
false
false
+ 0
10298
@@ -82390,6 +92688,7 @@
false
false
false
+ 0
10299
@@ -82398,6 +92697,7 @@
false
false
false
+ 0
10300
@@ -82406,6 +92706,7 @@
false
false
false
+ 0
10301
@@ -82414,6 +92715,7 @@
false
false
false
+ 0
10302
@@ -82422,6 +92724,7 @@
false
false
false
+ 0
10303
@@ -82430,6 +92733,7 @@
false
false
false
+ 0
10304
@@ -82438,6 +92742,7 @@
false
false
false
+ 0
10305
@@ -82446,6 +92751,7 @@
false
false
false
+ 0
10306
@@ -82454,6 +92760,7 @@
false
false
false
+ 0
10307
@@ -82462,6 +92769,7 @@
false
false
false
+ 0
10308
@@ -82470,6 +92778,7 @@
false
false
false
+ 0
10309
@@ -82478,6 +92787,7 @@
false
false
false
+ 0
10310
@@ -82486,6 +92796,7 @@
false
false
false
+ 0
10311
@@ -82494,6 +92805,7 @@
false
false
false
+ 0
10312
@@ -82502,6 +92814,7 @@
false
false
false
+ 0
10313
@@ -82510,6 +92823,7 @@
false
false
false
+ 0
10314
@@ -82518,6 +92832,7 @@
false
false
false
+ 0
10315
@@ -82526,6 +92841,7 @@
false
false
false
+ 0
10316
@@ -82534,6 +92850,7 @@
false
false
false
+ 0
10317
@@ -82542,6 +92859,7 @@
false
false
false
+ 0
10318
@@ -82550,6 +92868,7 @@
false
false
false
+ 0
10319
@@ -82558,6 +92877,7 @@
false
false
false
+ 0
10320
@@ -82566,6 +92886,7 @@
false
false
false
+ 0
10321
@@ -82574,6 +92895,7 @@
false
false
false
+ 0
10322
@@ -82582,6 +92904,7 @@
false
false
false
+ 0
10323
@@ -82590,6 +92913,7 @@
false
false
false
+ 0
10324
@@ -82598,6 +92922,7 @@
false
false
false
+ 0
10325
@@ -82606,6 +92931,7 @@
false
false
false
+ 0
10326
@@ -82614,6 +92940,7 @@
false
false
false
+ 0
10327
@@ -82622,6 +92949,7 @@
false
false
false
+ 0
10328
@@ -82630,6 +92958,7 @@
false
false
false
+ 0
10329
@@ -82638,6 +92967,7 @@
false
false
false
+ 0
10330
@@ -82646,6 +92976,7 @@
false
false
false
+ 0
10331
@@ -82654,6 +92985,7 @@
false
false
false
+ 0
10332
@@ -82662,6 +92994,7 @@
false
false
false
+ 0
10333
@@ -82670,6 +93003,7 @@
false
false
false
+ 0
10334
@@ -82678,6 +93012,7 @@
false
false
false
+ 0
10335
@@ -82686,6 +93021,7 @@
false
false
false
+ 0
10336
@@ -82694,6 +93030,7 @@
false
false
false
+ 0
10337
@@ -82702,6 +93039,7 @@
false
false
false
+ 0
10338
@@ -82710,6 +93048,7 @@
false
false
false
+ 0
10339
@@ -82718,6 +93057,7 @@
false
false
false
+ 0
10340
@@ -82726,6 +93066,7 @@
false
false
false
+ 0
10341
@@ -82734,6 +93075,7 @@
false
false
false
+ 0
10342
@@ -82742,6 +93084,7 @@
false
false
false
+ 0
10343
@@ -82750,6 +93093,7 @@
false
false
false
+ 0
10344
@@ -82758,6 +93102,7 @@
false
false
false
+ 0
10345
@@ -82766,6 +93111,7 @@
false
false
false
+ 0
10346
@@ -82774,6 +93120,7 @@
false
false
false
+ 0
10347
@@ -82782,6 +93129,7 @@
false
false
false
+ 0
10348
@@ -82790,6 +93138,7 @@
false
false
false
+ 0
10349
@@ -82798,6 +93147,7 @@
false
false
false
+ 0
10350
@@ -82806,6 +93156,7 @@
false
false
false
+ 0
10351
@@ -82814,6 +93165,7 @@
false
false
false
+ 0
10352
@@ -82822,6 +93174,7 @@
false
false
false
+ 0
10353
@@ -82830,6 +93183,7 @@
false
false
false
+ 0
10354
@@ -82838,6 +93192,7 @@
false
false
false
+ 0
10355
@@ -82846,6 +93201,7 @@
false
false
false
+ 0
10356
@@ -82854,6 +93210,7 @@
false
false
false
+ 0
10357
@@ -82862,6 +93219,7 @@
false
false
false
+ 0
10358
@@ -82870,6 +93228,7 @@
false
false
false
+ 0
10359
@@ -82878,6 +93237,7 @@
false
false
false
+ 0
10360
@@ -82886,6 +93246,7 @@
false
false
false
+ 0
10361
@@ -82894,6 +93255,7 @@
false
false
false
+ 0
10362
@@ -82902,6 +93264,7 @@
false
false
false
+ 0
10363
@@ -82910,6 +93273,7 @@
false
false
false
+ 0
10364
@@ -82918,6 +93282,7 @@
false
false
false
+ 0
10365
@@ -82926,6 +93291,7 @@
false
false
false
+ 0
10366
@@ -82934,6 +93300,7 @@
false
false
false
+ 0
10367
@@ -82942,6 +93309,7 @@
false
false
false
+ 0
10368
@@ -82950,6 +93318,7 @@
false
false
false
+ 0
10369
@@ -82958,6 +93327,7 @@
false
false
false
+ 0
10370
@@ -82966,6 +93336,7 @@
false
false
false
+ 0
10371
@@ -82974,6 +93345,7 @@
false
false
false
+ 0
10372
@@ -82982,6 +93354,7 @@
false
false
false
+ 0
10373
@@ -82990,6 +93363,7 @@
false
false
false
+ 0
10374
@@ -82998,6 +93372,7 @@
false
false
false
+ 0
10375
@@ -83006,6 +93381,7 @@
false
false
false
+ 0
10376
@@ -83014,6 +93390,7 @@
false
false
false
+ 0
10377
@@ -83022,6 +93399,7 @@
false
false
false
+ 0
10378
@@ -83030,6 +93408,7 @@
false
false
false
+ 0
10379
@@ -83038,6 +93417,7 @@
false
false
false
+ 0
10380
@@ -83046,6 +93426,7 @@
false
false
false
+ 0
10381
@@ -83054,6 +93435,7 @@
false
false
false
+ 0
10382
@@ -83062,6 +93444,7 @@
false
false
false
+ 0
10383
@@ -83070,6 +93453,7 @@
false
false
false
+ 0
10384
@@ -83078,6 +93462,7 @@
false
false
false
+ 0
10385
@@ -83086,6 +93471,7 @@
false
false
false
+ 0
10386
@@ -83094,6 +93480,7 @@
false
false
false
+ 0
10387
@@ -83102,6 +93489,7 @@
false
false
false
+ 0
10388
@@ -83110,6 +93498,7 @@
false
false
false
+ 0
10389
@@ -83118,6 +93507,7 @@
false
false
false
+ 0
10390
@@ -83126,6 +93516,7 @@
false
false
false
+ 0
10391
@@ -83134,6 +93525,7 @@
false
false
false
+ 0
10392
@@ -83142,6 +93534,7 @@
false
false
false
+ 0
10393
@@ -83150,6 +93543,7 @@
false
false
false
+ 0
10394
@@ -83158,6 +93552,7 @@
false
false
false
+ 0
10395
@@ -83166,6 +93561,7 @@
false
false
false
+ 0
10396
@@ -83174,6 +93570,7 @@
false
false
false
+ 0
10397
@@ -83182,6 +93579,7 @@
false
false
false
+ 0
10398
@@ -83190,6 +93588,7 @@
false
false
false
+ 0
10399
@@ -83198,6 +93597,7 @@
false
false
false
+ 0
10400
@@ -83206,6 +93606,7 @@
false
false
false
+ 0
10401
@@ -83214,6 +93615,7 @@
false
false
false
+ 0
10402
@@ -83222,6 +93624,7 @@
false
false
false
+ 0
10403
@@ -83230,6 +93633,7 @@
false
false
false
+ 0
10404
@@ -83238,6 +93642,7 @@
false
false
false
+ 0
10405
@@ -83246,6 +93651,7 @@
false
false
false
+ 0
10406
@@ -83254,6 +93660,7 @@
false
false
false
+ 0
10407
@@ -83262,6 +93669,7 @@
false
false
false
+ 0
10408
@@ -83270,6 +93678,7 @@
false
false
false
+ 0
10409
@@ -83278,6 +93687,7 @@
false
false
false
+ 0
10410
@@ -83286,6 +93696,7 @@
false
false
false
+ 0
10411
@@ -83294,6 +93705,7 @@
false
false
false
+ 0
10412
@@ -83302,6 +93714,7 @@
false
false
false
+ 0
10413
@@ -83310,6 +93723,7 @@
false
false
false
+ 0
10414
@@ -83318,6 +93732,7 @@
false
false
false
+ 0
10415
@@ -83326,6 +93741,7 @@
false
false
false
+ 0
10416
@@ -83334,6 +93750,7 @@
false
false
false
+ 0
10417
@@ -83342,6 +93759,7 @@
false
false
false
+ 0
10418
@@ -83350,6 +93768,7 @@
false
false
false
+ 0
10419
@@ -83358,6 +93777,7 @@
false
false
false
+ 0
10420
@@ -83366,6 +93786,7 @@
false
false
false
+ 0
10421
@@ -83374,6 +93795,7 @@
false
false
false
+ 0
10422
@@ -83382,6 +93804,7 @@
false
false
false
+ 0
10423
@@ -83390,6 +93813,7 @@
false
false
false
+ 0
10424
@@ -83398,6 +93822,7 @@
false
false
false
+ 0
10425
@@ -83406,6 +93831,7 @@
false
false
false
+ 0
10426
@@ -83414,6 +93840,7 @@
false
false
false
+ 0
10427
@@ -83422,6 +93849,7 @@
false
false
false
+ 0
10428
@@ -83430,6 +93858,7 @@
false
false
false
+ 0
10429
@@ -83438,6 +93867,7 @@
false
false
false
+ 0
10430
@@ -83446,6 +93876,7 @@
false
false
false
+ 0
10431
@@ -83454,6 +93885,7 @@
false
false
false
+ 0
10432
@@ -83462,6 +93894,7 @@
false
false
false
+ 0
10433
@@ -83470,6 +93903,7 @@
false
false
false
+ 0
10434
@@ -83478,6 +93912,7 @@
false
false
false
+ 0
10435
@@ -83486,6 +93921,7 @@
false
false
false
+ 0
10436
@@ -83494,6 +93930,7 @@
false
false
false
+ 0
10437
@@ -83502,6 +93939,7 @@
false
false
false
+ 0
10438
@@ -83510,6 +93948,7 @@
false
false
false
+ 0
10439
@@ -83518,6 +93957,7 @@
false
false
false
+ 0
10440
@@ -83526,6 +93966,7 @@
false
false
false
+ 0
10441
@@ -83534,6 +93975,7 @@
false
false
false
+ 0
10442
@@ -83542,6 +93984,7 @@
false
false
false
+ 0
10443
@@ -83550,6 +93993,7 @@
false
false
false
+ 0
10444
@@ -83558,6 +94002,7 @@
false
false
false
+ 0
10445
@@ -83566,6 +94011,7 @@
false
false
false
+ 0
10446
@@ -83574,6 +94020,7 @@
false
false
false
+ 0
10447
@@ -83582,6 +94029,7 @@
false
false
false
+ 0
10448
@@ -83590,6 +94038,7 @@
false
false
false
+ 0
10449
@@ -83598,6 +94047,7 @@
false
false
false
+ 0
10450
@@ -83606,6 +94056,7 @@
false
false
false
+ 0
10451
@@ -83614,6 +94065,7 @@
false
false
false
+ 0
10452
@@ -83622,6 +94074,7 @@
false
false
false
+ 0
10453
@@ -83630,6 +94083,7 @@
false
false
false
+ 0
10454
@@ -83638,6 +94092,7 @@
false
false
false
+ 0
10455
@@ -83646,6 +94101,7 @@
false
false
false
+ 0
10456
@@ -83654,6 +94110,7 @@
false
false
false
+ 0
10457
@@ -83662,6 +94119,7 @@
false
false
false
+ 0
10458
@@ -83670,6 +94128,7 @@
false
false
false
+ 0
10459
@@ -83678,6 +94137,7 @@
false
false
false
+ 0
10460
@@ -83686,6 +94146,7 @@
false
false
false
+ 0
10461
@@ -83694,6 +94155,7 @@
false
false
false
+ 0
10462
@@ -83702,6 +94164,7 @@
false
false
false
+ 0
10463
@@ -83710,6 +94173,7 @@
false
false
false
+ 0
10464
@@ -83718,6 +94182,7 @@
false
false
false
+ 0
10465
@@ -83726,6 +94191,7 @@
false
false
false
+ 0
10466
@@ -83734,6 +94200,7 @@
false
false
false
+ 0
10467
@@ -83742,6 +94209,7 @@
false
false
false
+ 0
10468
@@ -83750,6 +94218,7 @@
false
false
false
+ 0
10469
@@ -83758,6 +94227,7 @@
false
false
false
+ 0
10470
@@ -83766,6 +94236,7 @@
false
false
false
+ 0
10471
@@ -83774,6 +94245,7 @@
false
false
false
+ 0
10472
@@ -83782,6 +94254,7 @@
false
false
false
+ 0
10473
@@ -83790,6 +94263,7 @@
false
false
false
+ 0
10474
@@ -83798,6 +94272,7 @@
false
false
false
+ 0
10475
@@ -83806,6 +94281,7 @@
false
false
false
+ 0
10476
@@ -83814,6 +94290,7 @@
false
false
false
+ 0
10477
@@ -83822,6 +94299,7 @@
false
false
false
+ 0
10478
@@ -83830,6 +94308,7 @@
false
false
false
+ 0
10479
@@ -83838,6 +94317,7 @@
false
false
false
+ 0
10480
@@ -83846,6 +94326,7 @@
false
false
false
+ 0
10481
@@ -83854,6 +94335,7 @@
false
false
false
+ 0
10482
@@ -83862,6 +94344,7 @@
false
false
false
+ 0
10483
@@ -83870,6 +94353,7 @@
false
false
false
+ 0
10484
@@ -83878,6 +94362,7 @@
false
false
false
+ 0
10485
@@ -83886,6 +94371,7 @@
false
false
false
+ 0
10486
@@ -83894,6 +94380,7 @@
false
false
false
+ 0
10487
@@ -83902,6 +94389,7 @@
false
false
false
+ 0
10488
@@ -83910,6 +94398,7 @@
false
false
false
+ 0
10489
@@ -83918,6 +94407,7 @@
false
false
false
+ 0
10490
@@ -83926,6 +94416,7 @@
false
false
false
+ 0
10491
@@ -83934,6 +94425,7 @@
false
false
false
+ 0
10492
@@ -83942,6 +94434,7 @@
false
false
false
+ 0
10493
@@ -83950,6 +94443,7 @@
false
false
false
+ 0
10494
@@ -83958,6 +94452,7 @@
false
false
false
+ 0
10495
@@ -83966,6 +94461,7 @@
false
false
false
+ 0
10496
@@ -83974,6 +94470,7 @@
false
false
false
+ 0
10497
@@ -83982,6 +94479,7 @@
false
false
false
+ 0
10498
@@ -83990,6 +94488,7 @@
false
false
false
+ 0
10499
@@ -83998,6 +94497,7 @@
false
false
false
+ 0
10500
@@ -84006,6 +94506,7 @@
false
false
false
+ 0
10501
@@ -84014,6 +94515,7 @@
false
false
false
+ 0
10502
@@ -84022,6 +94524,7 @@
false
false
false
+ 0
10503
@@ -84030,6 +94533,7 @@
false
false
false
+ 0
10504
@@ -84038,6 +94542,7 @@
false
false
false
+ 0
10505
@@ -84046,6 +94551,7 @@
false
false
false
+ 0
10506
@@ -84054,6 +94560,7 @@
false
false
false
+ 0
10507
@@ -84062,6 +94569,7 @@
false
false
false
+ 0
10508
@@ -84070,6 +94578,7 @@
false
false
false
+ 0
10509
@@ -84078,6 +94587,7 @@
false
false
false
+ 0
10510
@@ -84086,6 +94596,7 @@
false
false
false
+ 0
10511
@@ -84094,6 +94605,7 @@
false
false
false
+ 0
10512
@@ -84102,6 +94614,7 @@
false
false
false
+ 0
10513
@@ -84110,6 +94623,7 @@
false
false
false
+ 0
10514
@@ -84118,6 +94632,7 @@
false
false
false
+ 0
10515
@@ -84126,6 +94641,7 @@
false
false
false
+ 0
10516
@@ -84134,6 +94650,7 @@
false
false
false
+ 0
10517
@@ -84142,6 +94659,7 @@
false
false
false
+ 0
10518
@@ -84150,6 +94668,7 @@
false
false
false
+ 0
10519
@@ -84158,6 +94677,7 @@
false
false
false
+ 0
10520
@@ -84166,6 +94686,7 @@
false
false
false
+ 0
10521
@@ -84174,6 +94695,7 @@
false
false
false
+ 0
10522
@@ -84182,6 +94704,7 @@
false
false
false
+ 0
10523
@@ -84190,6 +94713,7 @@
false
false
false
+ 0
10524
@@ -84198,6 +94722,7 @@
false
false
false
+ 0
10525
@@ -84206,6 +94731,7 @@
false
false
false
+ 0
10526
@@ -84214,6 +94740,7 @@
false
false
false
+ 0
10527
@@ -84222,6 +94749,7 @@
false
false
false
+ 0
10528
@@ -84230,6 +94758,7 @@
false
false
false
+ 0
10529
@@ -84238,6 +94767,7 @@
false
false
false
+ 0
10530
@@ -84246,6 +94776,7 @@
false
false
false
+ 0
10531
@@ -84254,6 +94785,7 @@
false
false
false
+ 0
10532
@@ -84262,6 +94794,7 @@
false
false
false
+ 0
10533
@@ -84270,6 +94803,7 @@
false
false
false
+ 0
10534
@@ -84278,6 +94812,7 @@
false
false
false
+ 0
10535
@@ -84286,6 +94821,7 @@
false
false
false
+ 0
10536
@@ -84294,6 +94830,7 @@
false
false
false
+ 0
10537
@@ -84302,6 +94839,7 @@
false
false
false
+ 0
10538
@@ -84310,6 +94848,7 @@
false
false
false
+ 0
10539
@@ -84318,6 +94857,7 @@
false
false
false
+ 0
10540
@@ -84326,6 +94866,7 @@
false
false
false
+ 0
10541
@@ -84334,6 +94875,7 @@
false
false
false
+ 0
10542
@@ -84342,6 +94884,7 @@
false
false
false
+ 0
10543
@@ -84350,6 +94893,7 @@
false
false
false
+ 0
10544
@@ -84358,6 +94902,7 @@
false
false
false
+ 0
10545
@@ -84366,6 +94911,7 @@
false
false
false
+ 0
10546
@@ -84374,6 +94920,7 @@
false
false
false
+ 0
10547
@@ -84382,6 +94929,7 @@
false
false
false
+ 0
10548
@@ -84390,6 +94938,7 @@
false
false
false
+ 0
10549
@@ -84398,6 +94947,7 @@
false
false
false
+ 0
10550
@@ -84406,6 +94956,7 @@
false
false
false
+ 0
10551
@@ -84414,6 +94965,7 @@
false
false
false
+ 0
10552
@@ -84422,6 +94974,7 @@
false
false
false
+ 0
10553
@@ -84430,6 +94983,7 @@
false
false
false
+ 0
10554
@@ -84438,6 +94992,7 @@
false
false
false
+ 0
10555
@@ -84446,6 +95001,7 @@
false
false
false
+ 0
10556
@@ -84454,6 +95010,7 @@
false
false
false
+ 0
10557
@@ -84462,6 +95019,7 @@
false
false
false
+ 0
10558
@@ -84470,6 +95028,7 @@
false
false
false
+ 0
10559
@@ -84478,6 +95037,7 @@
false
false
false
+ 0
10560
@@ -84486,6 +95046,7 @@
false
false
false
+ 0
10561
@@ -84494,6 +95055,7 @@
false
false
false
+ 0
10562
@@ -84502,6 +95064,7 @@
false
false
false
+ 0
10563
@@ -84510,6 +95073,7 @@
false
false
false
+ 0
10564
@@ -84518,6 +95082,7 @@
false
false
false
+ 0
10565
@@ -84526,6 +95091,7 @@
false
false
false
+ 0
10566
@@ -84534,6 +95100,7 @@
false
false
false
+ 0
10567
@@ -84542,6 +95109,7 @@
false
false
false
+ 0
10568
@@ -84550,6 +95118,7 @@
false
false
false
+ 0
10569
@@ -84558,6 +95127,7 @@
false
false
false
+ 0
10570
@@ -84566,6 +95136,7 @@
false
false
false
+ 0
10571
@@ -84574,6 +95145,7 @@
false
false
false
+ 0
10572
@@ -84582,6 +95154,7 @@
false
false
false
+ 0
10573
@@ -84590,6 +95163,7 @@
false
false
false
+ 0
10574
@@ -84598,6 +95172,7 @@
false
false
false
+ 0
10575
@@ -84606,6 +95181,7 @@
false
false
false
+ 0
10576
@@ -84614,6 +95190,7 @@
false
false
false
+ 0
10577
@@ -84622,6 +95199,7 @@
false
false
false
+ 0
10578
@@ -84630,6 +95208,7 @@
false
false
false
+ 0
10579
@@ -84638,6 +95217,7 @@
false
false
false
+ 0
10580
@@ -84646,6 +95226,7 @@
false
false
false
+ 0
10581
@@ -84654,6 +95235,7 @@
false
false
false
+ 0
10582
@@ -84662,6 +95244,7 @@
false
false
false
+ 0
10583
@@ -84670,6 +95253,7 @@
false
false
false
+ 0
10584
@@ -84678,6 +95262,7 @@
false
false
false
+ 0
10585
@@ -84686,6 +95271,7 @@
false
false
false
+ 0
10586
@@ -84694,6 +95280,7 @@
false
false
false
+ 0
10587
@@ -84702,6 +95289,7 @@
false
false
false
+ 0
10588
@@ -84710,6 +95298,7 @@
false
false
false
+ 0
10589
@@ -84718,6 +95307,7 @@
false
false
false
+ 0
10590
@@ -84726,6 +95316,7 @@
false
false
false
+ 0
10591
@@ -84734,6 +95325,7 @@
false
false
false
+ 0
10592
@@ -84742,6 +95334,7 @@
false
false
false
+ 0
10593
@@ -84750,6 +95343,7 @@
false
false
false
+ 0
10594
@@ -84758,6 +95352,7 @@
false
false
false
+ 0
10595
@@ -84766,6 +95361,7 @@
false
false
false
+ 0
10596
@@ -84774,6 +95370,7 @@
false
false
false
+ 0
10597
@@ -84782,6 +95379,7 @@
false
false
false
+ 0
10598
@@ -84790,6 +95388,7 @@
false
false
false
+ 0
10599
@@ -84798,6 +95397,7 @@
false
false
false
+ 0
10600
@@ -84806,6 +95406,7 @@
false
false
false
+ 0
10601
@@ -84814,6 +95415,7 @@
false
false
false
+ 0
10602
@@ -84822,6 +95424,7 @@
false
false
false
+ 0
10603
@@ -84830,6 +95433,7 @@
false
false
false
+ 0
10604
@@ -84838,6 +95442,7 @@
false
false
false
+ 0
10605
@@ -84846,6 +95451,7 @@
false
false
false
+ 0
10606
@@ -84854,6 +95460,7 @@
false
false
false
+ 0
10607
@@ -84862,6 +95469,7 @@
false
false
false
+ 0
10608
@@ -84870,6 +95478,7 @@
false
false
false
+ 0
10609
@@ -84878,6 +95487,7 @@
false
false
false
+ 0
10610
@@ -84886,6 +95496,7 @@
false
false
false
+ 0
10611
@@ -84894,6 +95505,7 @@
false
false
false
+ 0
10612
@@ -84902,6 +95514,7 @@
false
false
false
+ 0
10613
@@ -84910,6 +95523,7 @@
false
false
false
+ 0
10614
@@ -84918,6 +95532,7 @@
false
false
false
+ 0
10615
@@ -84926,6 +95541,7 @@
false
false
false
+ 0
10616
@@ -84934,6 +95550,7 @@
false
false
false
+ 0
10617
@@ -84942,6 +95559,7 @@
false
false
false
+ 0
10618
@@ -84950,6 +95568,7 @@
false
false
false
+ 0
10619
@@ -84958,6 +95577,7 @@
false
false
false
+ 0
10620
@@ -84966,6 +95586,7 @@
false
false
false
+ 0
10621
@@ -84974,6 +95595,7 @@
false
false
false
+ 0
10622
@@ -84982,6 +95604,7 @@
false
false
false
+ 0
10623
@@ -84990,6 +95613,7 @@
false
false
false
+ 0
10624
@@ -84998,6 +95622,7 @@
false
false
false
+ 0
10625
@@ -85006,6 +95631,7 @@
false
false
false
+ 0
10626
@@ -85014,6 +95640,7 @@
false
false
false
+ 0
10627
@@ -85022,6 +95649,7 @@
false
false
false
+ 0
10628
@@ -85030,6 +95658,7 @@
false
false
false
+ 0
10629
@@ -85038,6 +95667,7 @@
false
false
false
+ 0
10630
@@ -85046,6 +95676,7 @@
false
false
false
+ 0
10631
@@ -85054,6 +95685,7 @@
false
false
false
+ 0
10632
@@ -85062,6 +95694,7 @@
false
false
false
+ 0
10633
@@ -85070,6 +95703,7 @@
false
false
false
+ 0
10634
@@ -85078,6 +95712,7 @@
false
false
false
+ 0
10635
@@ -85086,6 +95721,7 @@
false
false
false
+ 0
10636
@@ -85094,6 +95730,7 @@
false
false
false
+ 0
10637
@@ -85102,6 +95739,7 @@
false
false
false
+ 0
10638
@@ -85110,6 +95748,7 @@
false
false
false
+ 0
10639
@@ -85118,6 +95757,7 @@
false
false
false
+ 0
10640
@@ -85126,6 +95766,7 @@
false
false
false
+ 0
10641
@@ -85134,6 +95775,7 @@
false
false
false
+ 0
10642
@@ -85142,6 +95784,7 @@
false
false
false
+ 0
10643
@@ -85150,6 +95793,7 @@
false
false
false
+ 0
10644
@@ -85158,6 +95802,7 @@
false
false
false
+ 0
10645
@@ -85166,6 +95811,7 @@
false
false
false
+ 0
10646
@@ -85174,6 +95820,7 @@
false
false
false
+ 0
10647
@@ -85182,6 +95829,7 @@
false
false
false
+ 0
10648
@@ -85190,6 +95838,7 @@
false
false
false
+ 0
10649
@@ -85198,6 +95847,7 @@
false
false
false
+ 0
10650
@@ -85206,6 +95856,7 @@
false
false
false
+ 0
10651
@@ -85214,6 +95865,7 @@
false
false
false
+ 0
10652
@@ -85222,6 +95874,7 @@
false
false
false
+ 0
10653
@@ -85230,6 +95883,7 @@
false
false
false
+ 0
10654
@@ -85238,6 +95892,7 @@
false
false
false
+ 0
10655
@@ -85246,6 +95901,7 @@
false
false
false
+ 0
10656
@@ -85254,6 +95910,7 @@
false
false
false
+ 0
10657
@@ -85262,6 +95919,7 @@
false
false
false
+ 0
10658
@@ -85270,6 +95928,7 @@
false
false
false
+ 0
10659
@@ -85278,6 +95937,7 @@
false
false
false
+ 0
10660
@@ -85286,6 +95946,7 @@
false
false
false
+ 0
10661
@@ -85294,6 +95955,7 @@
false
false
false
+ 0
10662
@@ -85302,6 +95964,7 @@
false
false
false
+ 0
10663
@@ -85310,6 +95973,7 @@
false
false
false
+ 0
10664
@@ -85318,6 +95982,7 @@
false
false
false
+ 0
10665
@@ -85326,6 +95991,7 @@
false
false
false
+ 0
10666
@@ -85334,6 +96000,7 @@
false
false
false
+ 0
10667
@@ -85342,6 +96009,7 @@
false
false
false
+ 0
10668
@@ -85350,6 +96018,7 @@
false
false
false
+ 0
10669
@@ -85358,6 +96027,7 @@
false
false
false
+ 0
10670
@@ -85366,6 +96036,7 @@
false
false
false
+ 0
10671
@@ -85374,6 +96045,7 @@
false
false
false
+ 0
10672
@@ -85382,6 +96054,7 @@
false
false
false
+ 0
10673
@@ -85390,6 +96063,7 @@
false
false
false
+ 0
10674
@@ -85398,6 +96072,7 @@
false
false
false
+ 0
10675
@@ -85406,6 +96081,7 @@
false
false
false
+ 0
10676
@@ -85414,6 +96090,7 @@
false
false
false
+ 0
10677
@@ -85422,6 +96099,7 @@
false
false
false
+ 0
10678
@@ -85430,6 +96108,7 @@
false
false
false
+ 0
10679
@@ -85438,6 +96117,7 @@
false
false
false
+ 0
10680
@@ -85446,6 +96126,7 @@
false
false
false
+ 0
10681
@@ -85454,6 +96135,7 @@
false
false
false
+ 0
10682
@@ -85462,6 +96144,7 @@
false
false
false
+ 0
10683
@@ -85470,6 +96153,7 @@
false
false
false
+ 0
10684
@@ -85478,6 +96162,7 @@
false
false
false
+ 0
10685
@@ -85486,6 +96171,7 @@
false
false
false
+ 0
10686
@@ -85494,6 +96180,7 @@
false
false
false
+ 0
10687
@@ -85502,6 +96189,7 @@
false
false
false
+ 0
10688
@@ -85510,6 +96198,7 @@
false
false
false
+ 0
10689
@@ -85518,6 +96207,7 @@
false
false
false
+ 0
10690
@@ -85526,6 +96216,7 @@
false
false
false
+ 0
10691
@@ -85534,6 +96225,7 @@
false
false
false
+ 0
10692
@@ -85542,6 +96234,7 @@
false
false
false
+ 0
10693
@@ -85550,6 +96243,7 @@
false
false
false
+ 0
10694
@@ -85558,6 +96252,7 @@
false
false
false
+ 0
10695
@@ -85566,6 +96261,7 @@
false
false
false
+ 0
10696
@@ -85574,6 +96270,7 @@
false
false
false
+ 0
10697
@@ -85582,6 +96279,7 @@
false
false
false
+ 0
10698
@@ -85590,6 +96288,7 @@
false
false
false
+ 0
10699
@@ -85598,6 +96297,7 @@
false
false
false
+ 0
10700
@@ -85606,6 +96306,7 @@
false
false
false
+ 0
10701
@@ -85614,6 +96315,7 @@
false
false
false
+ 0
10702
@@ -85622,6 +96324,7 @@
false
false
false
+ 0
10703
@@ -85630,6 +96333,7 @@
false
false
false
+ 0
10704
@@ -85638,6 +96342,7 @@
false
false
false
+ 0
10705
@@ -85646,6 +96351,7 @@
false
false
false
+ 0
10706
@@ -85654,6 +96360,7 @@
false
false
false
+ 0
10707
@@ -85662,6 +96369,7 @@
false
false
false
+ 0
10708
@@ -85670,6 +96378,7 @@
false
false
false
+ 0
10709
@@ -85678,6 +96387,7 @@
false
false
false
+ 0
10710
@@ -85686,6 +96396,7 @@
false
false
false
+ 0
10711
@@ -85694,6 +96405,7 @@
false
false
false
+ 0
10712
@@ -85702,6 +96414,7 @@
false
false
false
+ 0
10713
@@ -85710,6 +96423,7 @@
false
false
false
+ 0
10714
@@ -85718,6 +96432,7 @@
false
false
false
+ 0
10715
@@ -85726,6 +96441,7 @@
false
false
false
+ 0
10716
@@ -85734,6 +96450,7 @@
false
false
false
+ 0
10717
@@ -85742,6 +96459,7 @@
false
false
false
+ 0
10718
@@ -85750,6 +96468,7 @@
false
false
false
+ 0
10719
@@ -85758,6 +96477,7 @@
false
false
false
+ 0
10720
@@ -85766,6 +96486,7 @@
false
false
false
+ 0
10721
@@ -85774,6 +96495,7 @@
false
false
false
+ 0
10722
@@ -85782,6 +96504,7 @@
false
false
false
+ 0
10723
@@ -85790,6 +96513,7 @@
false
false
false
+ 0
10724
@@ -85798,6 +96522,7 @@
false
false
false
+ 0
10725
@@ -85806,6 +96531,7 @@
false
false
false
+ 0
10726
@@ -85814,6 +96540,7 @@
false
false
false
+ 0
10727
@@ -85822,6 +96549,7 @@
false
false
false
+ 0
10728
@@ -85830,6 +96558,7 @@
false
false
false
+ 0
10729
@@ -85838,6 +96567,7 @@
false
false
false
+ 0
10730
@@ -85846,6 +96576,7 @@
false
false
false
+ 0
10731
@@ -85854,6 +96585,7 @@
false
false
false
+ 0
10732
@@ -85862,6 +96594,7 @@
false
false
false
+ 0
10733
@@ -85870,6 +96603,7 @@
false
false
false
+ 0
10734
@@ -85878,6 +96612,7 @@
false
false
false
+ 0
10735
@@ -85886,6 +96621,7 @@
false
false
false
+ 0
10736
@@ -85894,6 +96630,7 @@
false
false
false
+ 0
10737
@@ -85902,6 +96639,7 @@
false
false
false
+ 0
10738
@@ -85910,6 +96648,7 @@
false
false
false
+ 0
10739
@@ -85918,6 +96657,7 @@
false
false
false
+ 0
10740
@@ -85926,6 +96666,7 @@
false
false
false
+ 0
10741
@@ -85934,6 +96675,7 @@
false
false
false
+ 0
10742
@@ -85942,6 +96684,7 @@
false
false
false
+ 0
10743
@@ -85950,6 +96693,7 @@
false
false
false
+ 0
10744
@@ -85958,6 +96702,7 @@
false
false
false
+ 0
10745
@@ -85966,6 +96711,7 @@
false
false
false
+ 0
10746
@@ -85974,6 +96720,7 @@
false
false
false
+ 0
10747
@@ -85982,6 +96729,7 @@
false
false
false
+ 0
10748
@@ -85990,6 +96738,7 @@
false
false
false
+ 0
10749
@@ -85998,6 +96747,7 @@
false
false
false
+ 0
10750
@@ -86006,6 +96756,7 @@
false
false
false
+ 0
10751
@@ -86014,6 +96765,7 @@
false
false
false
+ 0
10752
@@ -86022,6 +96774,7 @@
false
false
false
+ 0
10753
@@ -86030,6 +96783,7 @@
false
false
false
+ 0
10754
@@ -86038,6 +96792,7 @@
false
false
false
+ 0
10755
@@ -86046,6 +96801,7 @@
false
false
false
+ 0
10756
@@ -86054,6 +96810,7 @@
false
false
false
+ 0
10757
@@ -86062,6 +96819,7 @@
false
false
false
+ 0
10758
@@ -86070,6 +96828,7 @@
false
false
false
+ 0
10759
@@ -86078,6 +96837,7 @@
false
false
false
+ 0
10760
@@ -86086,6 +96846,7 @@
false
false
false
+ 0
10761
@@ -86094,6 +96855,7 @@
false
false
false
+ 0
10762
@@ -86102,6 +96864,7 @@
false
false
false
+ 0
10763
@@ -86110,6 +96873,7 @@
false
false
false
+ 0
10764
@@ -86118,6 +96882,7 @@
false
false
false
+ 0
10765
@@ -86126,6 +96891,7 @@
false
false
false
+ 0
10766
@@ -86134,6 +96900,7 @@
false
false
false
+ 0
10767
@@ -86142,6 +96909,7 @@
false
false
false
+ 0
10768
@@ -86150,6 +96918,7 @@
false
false
false
+ 0
10769
@@ -86158,6 +96927,7 @@
false
false
false
+ 0
10770
@@ -86166,6 +96936,7 @@
false
false
false
+ 0
10771
@@ -86174,6 +96945,7 @@
false
false
false
+ 0
10772
@@ -86182,6 +96954,7 @@
false
false
false
+ 0
10773
@@ -86190,6 +96963,7 @@
false
false
false
+ 0
10774
@@ -86198,6 +96972,7 @@
false
false
false
+ 0
10775
@@ -86206,6 +96981,7 @@
false
false
false
+ 0
10776
@@ -86214,6 +96990,7 @@
false
false
false
+ 0
10777
@@ -86222,6 +96999,7 @@
false
false
false
+ 0
10778
@@ -86230,6 +97008,7 @@
false
false
false
+ 0
10779
@@ -86238,6 +97017,7 @@
false
false
false
+ 0
10780
@@ -86246,6 +97026,7 @@
false
false
false
+ 0
10781
@@ -86254,6 +97035,7 @@
false
false
false
+ 0
10782
@@ -86262,6 +97044,7 @@
false
false
false
+ 0
10783
@@ -86270,6 +97053,7 @@
false
false
false
+ 0
10784
@@ -86278,6 +97062,7 @@
false
false
false
+ 0
10785
@@ -86286,6 +97071,7 @@
false
false
false
+ 0
10786
@@ -86294,6 +97080,7 @@
false
false
false
+ 0
10787
@@ -86302,6 +97089,7 @@
false
false
false
+ 0
10788
@@ -86310,6 +97098,7 @@
false
false
false
+ 0
10789
@@ -86318,6 +97107,7 @@
false
false
false
+ 0
10790
@@ -86326,6 +97116,7 @@
false
false
false
+ 0
10791
@@ -86334,6 +97125,7 @@
false
false
false
+ 0
10792
@@ -86342,6 +97134,7 @@
false
false
false
+ 0
10793
@@ -86350,6 +97143,7 @@
false
false
false
+ 0
10794
@@ -86358,6 +97152,7 @@
false
false
false
+ 0
10795
@@ -86366,6 +97161,7 @@
false
false
false
+ 0
10796
@@ -86374,6 +97170,7 @@
false
false
false
+ 0
10797
@@ -86382,6 +97179,7 @@
false
false
false
+ 0
10798
@@ -86390,6 +97188,7 @@
false
false
false
+ 0
10799
@@ -86398,6 +97197,7 @@
false
false
false
+ 0
10800
@@ -86406,6 +97206,7 @@
false
false
false
+ 0
10801
@@ -86414,6 +97215,7 @@
false
false
false
+ 0
10802
@@ -86422,6 +97224,7 @@
false
false
false
+ 0
10803
@@ -86430,6 +97233,7 @@
false
false
false
+ 0
10804
@@ -86438,6 +97242,7 @@
false
false
false
+ 0
10805
@@ -86446,6 +97251,7 @@
false
false
false
+ 0
10806
@@ -86454,6 +97260,7 @@
false
false
false
+ 0
10807
@@ -86462,6 +97269,7 @@
false
false
false
+ 0
10808
@@ -86470,6 +97278,7 @@
false
false
false
+ 0
10809
@@ -86478,6 +97287,7 @@
false
false
false
+ 0
10810
@@ -86486,6 +97296,7 @@
false
false
false
+ 0
10811
@@ -86494,6 +97305,7 @@
false
false
false
+ 0
10812
@@ -86502,6 +97314,7 @@
false
false
false
+ 0
10813
@@ -86510,6 +97323,7 @@
false
false
false
+ 0
10814
@@ -86518,6 +97332,7 @@
false
false
false
+ 0
10815
@@ -86526,6 +97341,7 @@
false
false
false
+ 0
10816
@@ -86534,6 +97350,7 @@
false
false
false
+ 0
10817
@@ -86542,6 +97359,7 @@
false
false
false
+ 0
10818
@@ -86550,6 +97368,7 @@
false
false
false
+ 0
10819
@@ -86558,6 +97377,7 @@
false
false
false
+ 0
10820
@@ -86566,6 +97386,7 @@
false
false
false
+ 0
10821
@@ -86574,6 +97395,7 @@
false
false
false
+ 0
10822
@@ -86582,6 +97404,7 @@
false
false
false
+ 0
10823
@@ -86590,6 +97413,7 @@
false
false
false
+ 0
10824
@@ -86598,6 +97422,7 @@
false
false
false
+ 0
10825
@@ -86606,6 +97431,7 @@
false
false
false
+ 0
10826
@@ -86614,6 +97440,7 @@
false
false
false
+ 0
10827
@@ -86622,6 +97449,7 @@
false
false
false
+ 0
10828
@@ -86630,6 +97458,7 @@
false
false
false
+ 0
10829
@@ -86638,6 +97467,7 @@
false
false
false
+ 0
10830
@@ -86646,6 +97476,7 @@
false
false
false
+ 0
10831
@@ -86654,6 +97485,7 @@
false
false
false
+ 0
10832
@@ -86662,6 +97494,7 @@
false
false
false
+ 0
10833
@@ -86670,6 +97503,7 @@
false
false
false
+ 0
10834
@@ -86678,6 +97512,7 @@
false
false
false
+ 0
10835
@@ -86686,6 +97521,7 @@
false
false
false
+ 0
10836
@@ -86694,6 +97530,7 @@
false
false
false
+ 0
10837
@@ -86702,6 +97539,7 @@
false
false
false
+ 0
10838
@@ -86710,6 +97548,7 @@
false
false
false
+ 0
10839
@@ -86718,6 +97557,7 @@
false
false
false
+ 0
10840
@@ -86726,6 +97566,7 @@
false
false
false
+ 0
10841
@@ -86734,6 +97575,7 @@
false
false
false
+ 0
10842
@@ -86742,6 +97584,7 @@
false
false
false
+ 0
10843
@@ -86750,6 +97593,7 @@
false
false
false
+ 0
10844
@@ -86758,6 +97602,7 @@
false
false
false
+ 0
10845
@@ -86766,6 +97611,7 @@
false
false
false
+ 0
10846
@@ -86774,6 +97620,7 @@
false
false
false
+ 0
10847
@@ -86782,6 +97629,7 @@
false
false
false
+ 0
10848
@@ -86790,6 +97638,7 @@
false
false
false
+ 0
10849
@@ -86798,6 +97647,7 @@
false
false
false
+ 0
10850
@@ -86806,6 +97656,7 @@
false
false
false
+ 0
10851
@@ -86814,6 +97665,7 @@
false
false
false
+ 0
10852
@@ -86822,6 +97674,7 @@
false
false
false
+ 0
10853
@@ -86830,6 +97683,7 @@
false
false
false
+ 0
10854
@@ -86838,6 +97692,7 @@
false
false
false
+ 0
10855
@@ -86846,6 +97701,7 @@
false
false
false
+ 0
10856
@@ -86854,6 +97710,7 @@
false
false
false
+ 0
10857
@@ -86862,6 +97719,7 @@
false
false
false
+ 0
10858
@@ -86870,6 +97728,7 @@
false
false
false
+ 0
10859
@@ -86878,6 +97737,7 @@
false
false
false
+ 0
10860
@@ -86886,6 +97746,7 @@
false
false
false
+ 0
10861
@@ -86894,6 +97755,7 @@
false
false
false
+ 0
10862
@@ -86902,6 +97764,7 @@
false
false
false
+ 0
10863
@@ -86910,6 +97773,7 @@
false
false
false
+ 0
10864
@@ -86918,6 +97782,7 @@
false
false
false
+ 0
10865
@@ -86926,6 +97791,7 @@
false
false
false
+ 0
10866
@@ -86934,6 +97800,7 @@
false
false
false
+ 0
10867
@@ -86942,6 +97809,7 @@
false
false
false
+ 0
10868
@@ -86950,6 +97818,7 @@
false
false
false
+ 0
10869
@@ -86958,6 +97827,7 @@
false
false
false
+ 0
10870
@@ -86966,6 +97836,7 @@
false
false
false
+ 0
10871
@@ -86974,6 +97845,7 @@
false
false
false
+ 0
10872
@@ -86982,6 +97854,7 @@
false
false
false
+ 0
10873
@@ -86990,6 +97863,7 @@
false
false
false
+ 0
10874
@@ -86998,6 +97872,7 @@
false
false
false
+ 0
10875
@@ -87006,6 +97881,7 @@
false
false
false
+ 0
10876
@@ -87014,6 +97890,7 @@
false
false
false
+ 0
10877
@@ -87022,6 +97899,7 @@
false
false
false
+ 0
10878
@@ -87030,6 +97908,7 @@
false
false
false
+ 0
10879
@@ -87038,6 +97917,7 @@
false
false
false
+ 0
10880
@@ -87046,6 +97926,7 @@
false
false
false
+ 0
10881
@@ -87054,6 +97935,7 @@
false
false
false
+ 0
10882
@@ -87062,6 +97944,7 @@
false
false
false
+ 0
10883
@@ -87070,6 +97953,7 @@
false
false
false
+ 0
10884
@@ -87078,6 +97962,7 @@
false
false
false
+ 0
10885
@@ -87086,6 +97971,7 @@
false
false
false
+ 0
10886
@@ -87094,6 +97980,7 @@
false
false
false
+ 0
10887
@@ -87102,6 +97989,7 @@
false
false
false
+ 0
10888
@@ -87110,6 +97998,7 @@
false
false
false
+ 0
10889
@@ -87118,6 +98007,7 @@
false
false
false
+ 0
10890
@@ -87126,6 +98016,7 @@
false
false
false
+ 0
10891
@@ -87134,6 +98025,7 @@
false
false
false
+ 0
10892
@@ -87142,6 +98034,7 @@
false
false
false
+ 0
10893
@@ -87150,6 +98043,7 @@
false
false
false
+ 0
10894
@@ -87158,6 +98052,7 @@
false
false
false
+ 0
10895
@@ -87166,6 +98061,7 @@
false
false
false
+ 0
10896
@@ -87174,6 +98070,7 @@
false
false
false
+ 0
10897
@@ -87182,6 +98079,7 @@
false
false
false
+ 0
10898
@@ -87190,6 +98088,7 @@
false
false
false
+ 0
10899
@@ -87198,6 +98097,7 @@
false
false
false
+ 0
10900
@@ -87206,6 +98106,7 @@
false
false
false
+ 0
10901
@@ -87214,6 +98115,7 @@
false
false
false
+ 0
10902
@@ -87222,6 +98124,7 @@
false
false
false
+ 0
10903
@@ -87230,6 +98133,7 @@
false
false
false
+ 0
10904
@@ -87238,6 +98142,7 @@
false
false
false
+ 0
10905
@@ -87246,6 +98151,7 @@
false
false
false
+ 0
10906
@@ -87254,6 +98160,7 @@
false
false
false
+ 0
10907
@@ -87262,6 +98169,7 @@
false
false
false
+ 0
10908
@@ -87270,6 +98178,7 @@
false
false
false
+ 0
10909
@@ -87278,6 +98187,7 @@
false
false
false
+ 0
10910
@@ -87286,6 +98196,7 @@
false
false
false
+ 0
10911
@@ -87294,6 +98205,7 @@
false
false
false
+ 0
10912
@@ -87302,6 +98214,7 @@
false
false
false
+ 0
10913
@@ -87310,6 +98223,7 @@
false
false
false
+ 0
10914
@@ -87318,6 +98232,7 @@
false
false
false
+ 0
10915
@@ -87326,6 +98241,7 @@
false
false
false
+ 0
10916
@@ -87334,6 +98250,7 @@
false
false
false
+ 0
10917
@@ -87342,6 +98259,7 @@
false
false
false
+ 0
10918
@@ -87350,6 +98268,7 @@
false
false
false
+ 0
10919
@@ -87358,6 +98277,7 @@
false
false
false
+ 0
10920
@@ -87366,6 +98286,7 @@
false
false
false
+ 0
10921
@@ -87374,6 +98295,7 @@
false
false
false
+ 0
10922
@@ -87382,6 +98304,7 @@
false
false
false
+ 0
10923
@@ -87390,6 +98313,7 @@
false
false
false
+ 0
10924
@@ -87398,6 +98322,7 @@
false
false
false
+ 0
10925
@@ -87406,6 +98331,7 @@
false
false
false
+ 0
10926
@@ -87414,6 +98340,7 @@
false
false
false
+ 0
10927
@@ -87422,6 +98349,7 @@
false
false
false
+ 0
10928
@@ -87430,6 +98358,7 @@
false
false
false
+ 0
10929
@@ -87438,6 +98367,7 @@
false
false
false
+ 0
10930
@@ -87446,6 +98376,7 @@
false
false
false
+ 0
10931
@@ -87454,6 +98385,7 @@
false
false
false
+ 0
10932
@@ -87462,6 +98394,7 @@
false
false
false
+ 0
10933
@@ -87470,6 +98403,7 @@
false
false
false
+ 0
10934
@@ -87478,6 +98412,7 @@
false
false
false
+ 0
10935
@@ -87486,6 +98421,7 @@
false
false
false
+ 0
10936
@@ -87494,6 +98430,7 @@
false
false
false
+ 0
10937
@@ -87502,6 +98439,7 @@
false
false
false
+ 0
10938
@@ -87510,6 +98448,7 @@
false
false
false
+ 0
10939
@@ -87518,6 +98457,7 @@
false
false
false
+ 0
10940
@@ -87526,6 +98466,7 @@
false
false
false
+ 0
10941
@@ -87534,6 +98475,7 @@
false
false
false
+ 0
10942
@@ -87542,6 +98484,7 @@
false
false
false
+ 0
10943
@@ -87550,6 +98493,7 @@
false
false
false
+ 0
10944
@@ -87558,6 +98502,7 @@
false
false
false
+ 0
10945
@@ -87566,6 +98511,7 @@
false
false
false
+ 0
10946
@@ -87574,6 +98520,7 @@
false
false
false
+ 0
10947
@@ -87582,6 +98529,7 @@
false
false
false
+ 0
10948
@@ -87590,6 +98538,7 @@
false
false
false
+ 0
10949
@@ -87598,6 +98547,7 @@
false
false
false
+ 0
10950
@@ -87606,6 +98556,7 @@
false
false
false
+ 0
10951
@@ -87614,6 +98565,7 @@
false
false
false
+ 0
10952
@@ -87622,6 +98574,7 @@
false
false
false
+ 0
10953
@@ -87630,6 +98583,7 @@
false
false
false
+ 0
10954
@@ -87638,6 +98592,7 @@
false
false
false
+ 0
10955
@@ -87646,6 +98601,7 @@
false
false
false
+ 0
10956
@@ -87654,6 +98610,7 @@
false
false
false
+ 0
10957
@@ -87662,6 +98619,7 @@
false
false
false
+ 0
10958
@@ -87670,6 +98628,7 @@
false
false
false
+ 0
10959
@@ -87678,6 +98637,7 @@
false
false
false
+ 0
10960
@@ -87686,6 +98646,7 @@
false
false
false
+ 0
10961
@@ -87694,6 +98655,7 @@
false
false
false
+ 0
10962
@@ -87702,6 +98664,7 @@
false
false
false
+ 0
10963
@@ -87710,6 +98673,7 @@
false
false
false
+ 0
10964
@@ -87718,6 +98682,7 @@
false
false
false
+ 0
10965
@@ -87726,6 +98691,7 @@
false
false
false
+ 0
10966
@@ -87734,6 +98700,7 @@
false
false
false
+ 0
10967
@@ -87742,6 +98709,7 @@
false
false
false
+ 0
10968
@@ -87750,6 +98718,7 @@
false
false
false
+ 0
10969
@@ -87758,6 +98727,7 @@
false
false
false
+ 0
10970
@@ -87766,6 +98736,7 @@
false
false
false
+ 0
10971
@@ -87774,6 +98745,7 @@
false
false
false
+ 0
10972
@@ -87782,6 +98754,7 @@
false
false
false
+ 0
10973
@@ -87790,6 +98763,7 @@
false
false
false
+ 0
10974
@@ -87798,6 +98772,7 @@
false
false
false
+ 0
10975
@@ -87806,6 +98781,7 @@
false
false
false
+ 0
10976
@@ -87814,6 +98790,7 @@
false
false
false
+ 0
10977
@@ -87822,6 +98799,7 @@
false
false
false
+ 0
10978
@@ -87830,6 +98808,7 @@
false
false
false
+ 0
10979
@@ -87838,6 +98817,7 @@
false
false
false
+ 0
10980
@@ -87846,6 +98826,7 @@
false
false
false
+ 0
10981
@@ -87854,6 +98835,7 @@
false
false
false
+ 0
10982
@@ -87862,6 +98844,7 @@
false
false
false
+ 0
10983
@@ -87870,6 +98853,7 @@
false
false
false
+ 0
10984
@@ -87878,6 +98862,7 @@
false
false
false
+ 0
10985
@@ -87886,6 +98871,7 @@
false
false
false
+ 0
10986
@@ -87894,6 +98880,7 @@
false
false
false
+ 0
10987
@@ -87902,6 +98889,7 @@
false
false
false
+ 0
10988
@@ -87910,6 +98898,7 @@
false
false
false
+ 0
10989
@@ -87918,6 +98907,7 @@
false
false
false
+ 0
10990
@@ -87926,6 +98916,7 @@
false
false
false
+ 0
10991
@@ -87934,6 +98925,7 @@
false
false
false
+ 0
10992
@@ -87942,6 +98934,7 @@
false
false
false
+ 0
10993
@@ -87950,6 +98943,7 @@
false
false
false
+ 0
10994
@@ -87958,6 +98952,7 @@
false
false
false
+ 0
10995
@@ -87966,6 +98961,7 @@
false
false
false
+ 0
10996
@@ -87974,6 +98970,7 @@
false
false
false
+ 0
10997
@@ -87982,6 +98979,7 @@
false
false
false
+ 0
10998
@@ -87990,6 +98988,7 @@
false
false
false
+ 0
10999
@@ -87998,6 +98997,7 @@
false
false
false
+ 0
11000
@@ -88006,6 +99006,7 @@
false
false
false
+ 0
11001
@@ -88014,6 +99015,7 @@
false
false
false
+ 0
11002
@@ -88022,6 +99024,7 @@
false
false
false
+ 0
11003
@@ -88030,6 +99033,7 @@
false
false
false
+ 0
11004
@@ -88038,6 +99042,7 @@
false
false
false
+ 0
11005
@@ -88046,6 +99051,7 @@
false
false
false
+ 0
11006
@@ -88054,6 +99060,7 @@
false
false
false
+ 0
11007
@@ -88062,6 +99069,7 @@
false
false
false
+ 0
11008
@@ -88070,6 +99078,7 @@
false
false
false
+ 0
11009
@@ -88078,6 +99087,7 @@
false
false
false
+ 0
11010
@@ -88086,6 +99096,7 @@
false
false
false
+ 0
11011
@@ -88094,6 +99105,7 @@
false
false
false
+ 0
11012
@@ -88102,6 +99114,7 @@
false
false
false
+ 0
11013
@@ -88110,6 +99123,7 @@
false
false
false
+ 0
11014
@@ -88118,6 +99132,7 @@
false
false
false
+ 0
11015
@@ -88126,6 +99141,7 @@
false
false
false
+ 0
11016
@@ -88134,6 +99150,7 @@
false
false
false
+ 0
11017
@@ -88142,6 +99159,7 @@
false
false
false
+ 0
11018
@@ -88150,6 +99168,7 @@
false
false
false
+ 0
11019
@@ -88158,6 +99177,7 @@
false
false
false
+ 0
11020
@@ -88166,6 +99186,7 @@
false
false
false
+ 0
11021
@@ -88174,6 +99195,7 @@
false
false
false
+ 0
11022
@@ -88182,6 +99204,7 @@
false
false
false
+ 0
11023
@@ -88190,6 +99213,7 @@
false
false
false
+ 0
11024
@@ -88198,6 +99222,7 @@
false
false
false
+ 0
11025
@@ -88206,6 +99231,7 @@
false
false
false
+ 0
11026
@@ -88214,6 +99240,7 @@
false
false
false
+ 0
11027
@@ -88222,6 +99249,7 @@
false
false
false
+ 0
11028
@@ -88230,6 +99258,7 @@
false
false
false
+ 0
11029
@@ -88238,6 +99267,7 @@
false
false
false
+ 0
11030
@@ -88246,6 +99276,7 @@
false
false
false
+ 0
11031
@@ -88254,6 +99285,7 @@
false
false
false
+ 0
11032
@@ -88262,6 +99294,7 @@
false
false
false
+ 0
11033
@@ -88270,6 +99303,7 @@
false
false
false
+ 0
11034
@@ -88278,6 +99312,7 @@
false
false
false
+ 0
11035
@@ -88286,6 +99321,7 @@
false
false
false
+ 0
11036
@@ -88294,6 +99330,7 @@
false
false
false
+ 0
11037
@@ -88302,6 +99339,7 @@
false
false
false
+ 0
11038
@@ -88310,6 +99348,7 @@
false
false
false
+ 0
11039
@@ -88318,6 +99357,7 @@
false
false
false
+ 0
11040
@@ -88326,6 +99366,7 @@
false
false
false
+ 0
11041
@@ -88334,6 +99375,7 @@
false
false
false
+ 0
11042
@@ -88342,6 +99384,7 @@
false
false
false
+ 0
11043
@@ -88350,6 +99393,7 @@
false
false
false
+ 0
11044
@@ -88358,6 +99402,7 @@
false
false
false
+ 0
11045
@@ -88366,6 +99411,7 @@
false
false
false
+ 0
11046
@@ -88374,6 +99420,7 @@
false
false
false
+ 0
11047
@@ -88382,6 +99429,7 @@
false
false
false
+ 0
11048
@@ -88390,6 +99438,7 @@
false
false
false
+ 0
11049
@@ -88398,6 +99447,7 @@
false
false
false
+ 0
11050
@@ -88406,6 +99456,7 @@
false
false
false
+ 0
11051
@@ -88414,6 +99465,7 @@
false
false
false
+ 0
11052
@@ -88422,6 +99474,7 @@
false
false
false
+ 0
11053
@@ -88430,6 +99483,7 @@
false
false
false
+ 0
11054
@@ -88438,6 +99492,7 @@
false
false
false
+ 0
11055
@@ -88446,6 +99501,7 @@
false
false
false
+ 0
11056
@@ -88454,6 +99510,7 @@
false
false
false
+ 0
11057
@@ -88462,6 +99519,7 @@
false
false
false
+ 0
11058
@@ -88470,6 +99528,7 @@
false
false
false
+ 0
11059
@@ -88478,6 +99537,7 @@
false
false
false
+ 0
11060
@@ -88486,6 +99546,7 @@
false
false
false
+ 0
11061
@@ -88494,6 +99555,7 @@
false
false
false
+ 0
11062
@@ -88502,6 +99564,7 @@
false
false
false
+ 0
11063
@@ -88510,6 +99573,7 @@
false
false
false
+ 0
11064
@@ -88518,6 +99582,7 @@
false
false
false
+ 0
11065
@@ -88526,6 +99591,7 @@
false
false
false
+ 0
11066
@@ -88534,6 +99600,7 @@
false
false
false
+ 0
11067
@@ -88542,6 +99609,7 @@
false
false
false
+ 0
11068
@@ -88550,6 +99618,7 @@
false
false
false
+ 0
11069
@@ -88558,6 +99627,7 @@
false
false
false
+ 0
11070
@@ -88566,6 +99636,7 @@
false
false
false
+ 0
11071
@@ -88574,6 +99645,7 @@
false
false
false
+ 0
11072
@@ -88582,6 +99654,7 @@
false
false
false
+ 0
11073
@@ -88590,6 +99663,7 @@
false
false
false
+ 0
11074
@@ -88598,6 +99672,7 @@
false
false
false
+ 0
11075
@@ -88606,6 +99681,7 @@
false
false
false
+ 0
11076
@@ -88614,6 +99690,7 @@
false
false
false
+ 0
11077
@@ -88622,6 +99699,7 @@
false
false
false
+ 0
11078
@@ -88630,6 +99708,7 @@
false
false
false
+ 0
11079
@@ -88638,6 +99717,7 @@
false
false
false
+ 0
11080
@@ -88646,6 +99726,7 @@
false
false
false
+ 0
11081
@@ -88654,6 +99735,7 @@
false
false
false
+ 0
11082
@@ -88662,6 +99744,7 @@
false
false
false
+ 0
11083
@@ -88670,6 +99753,7 @@
false
false
false
+ 0
11084
@@ -88678,6 +99762,7 @@
false
false
false
+ 0
11085
@@ -88686,6 +99771,7 @@
false
false
false
+ 0
11086
@@ -88694,6 +99780,7 @@
false
false
false
+ 0
11087
@@ -88702,6 +99789,7 @@
false
false
false
+ 0
11088
@@ -88710,6 +99798,7 @@
false
false
false
+ 0
11089
@@ -88718,6 +99807,7 @@
false
false
false
+ 0
11090
@@ -88726,6 +99816,7 @@
false
false
false
+ 0
11091
@@ -88734,6 +99825,7 @@
false
false
false
+ 0
11092
@@ -88742,6 +99834,7 @@
false
false
false
+ 0
11093
@@ -88750,6 +99843,7 @@
false
false
false
+ 0
11094
@@ -88758,6 +99852,7 @@
false
false
false
+ 0
11095
@@ -88766,6 +99861,7 @@
false
false
false
+ 0
11096
@@ -88774,6 +99870,7 @@
false
false
false
+ 0
11097
@@ -88782,6 +99879,7 @@
false
false
false
+ 0
11098
@@ -88790,6 +99888,7 @@
false
false
false
+ 0
11099
@@ -88798,6 +99897,7 @@
false
false
false
+ 0
11100
@@ -88806,6 +99906,7 @@
false
false
false
+ 0
11101
@@ -88814,6 +99915,7 @@
false
false
false
+ 0
11102
@@ -88822,6 +99924,7 @@
false
false
false
+ 0
11103
@@ -88830,6 +99933,7 @@
false
false
false
+ 0
11104
@@ -88838,6 +99942,7 @@
false
false
false
+ 0
11105
@@ -88846,6 +99951,7 @@
false
false
false
+ 0
11106
@@ -88854,6 +99960,7 @@
false
false
false
+ 0
11107
@@ -88862,6 +99969,7 @@
false
false
false
+ 0
11108
@@ -88870,6 +99978,7 @@
false
false
false
+ 0
11109
@@ -88878,6 +99987,7 @@
false
false
false
+ 0
11110
@@ -88886,6 +99996,7 @@
false
false
false
+ 0
11111
@@ -88894,6 +100005,7 @@
false
false
false
+ 0
11112
@@ -88902,6 +100014,7 @@
false
false
false
+ 0
11113
@@ -88910,6 +100023,7 @@
false
false
false
+ 0
11114
@@ -88918,6 +100032,7 @@
false
false
false
+ 0
11115
@@ -88926,6 +100041,7 @@
false
false
false
+ 0
11116
@@ -88934,6 +100050,7 @@
false
false
false
+ 0
11117
@@ -88942,6 +100059,7 @@
false
false
false
+ 0
11118
@@ -88950,6 +100068,7 @@
false
false
false
+ 0
11119
@@ -88958,6 +100077,7 @@
false
false
false
+ 0
11120
@@ -88966,6 +100086,7 @@
false
false
false
+ 0
11121
@@ -88974,6 +100095,7 @@
false
false
false
+ 0
11122
@@ -88982,6 +100104,7 @@
false
false
false
+ 0
11123
@@ -88990,6 +100113,7 @@
false
false
false
+ 0
11124
@@ -88998,6 +100122,7 @@
false
false
false
+ 0
11125
@@ -89006,6 +100131,7 @@
false
false
false
+ 0
11126
@@ -89014,6 +100140,7 @@
false
false
false
+ 0
11127
@@ -89022,6 +100149,7 @@
false
false
false
+ 0
11128
@@ -89030,6 +100158,7 @@
false
false
false
+ 0
11129
@@ -89038,6 +100167,7 @@
false
false
false
+ 0
11130
@@ -89046,6 +100176,7 @@
false
false
false
+ 0
11131
@@ -89054,6 +100185,7 @@
false
false
false
+ 0
11132
@@ -89062,6 +100194,7 @@
false
false
false
+ 0
11133
@@ -89070,6 +100203,7 @@
false
false
false
+ 0
11134
@@ -89078,6 +100212,7 @@
false
false
false
+ 0
11135
@@ -89086,6 +100221,7 @@
false
false
false
+ 0
11136
@@ -89094,6 +100230,7 @@
false
false
false
+ 0
11137
@@ -89102,6 +100239,7 @@
false
false
false
+ 0
11138
@@ -89110,6 +100248,7 @@
false
false
false
+ 0
11139
@@ -89118,6 +100257,7 @@
false
false
false
+ 0
11140
@@ -89126,6 +100266,7 @@
false
false
false
+ 0
11141
@@ -89134,6 +100275,7 @@
false
false
false
+ 0
11142
@@ -89142,6 +100284,7 @@
false
false
false
+ 0
11143
@@ -89150,6 +100293,7 @@
false
false
false
+ 0
11144
@@ -89158,6 +100302,7 @@
false
false
false
+ 0
11145
@@ -89166,6 +100311,7 @@
false
false
false
+ 0
11146
@@ -89174,6 +100320,7 @@
false
false
false
+ 0
11147
@@ -89182,6 +100329,7 @@
false
false
false
+ 0
11148
@@ -89190,6 +100338,7 @@
false
false
false
+ 0
11149
@@ -89198,6 +100347,7 @@
false
false
false
+ 0
11150
@@ -89206,6 +100356,7 @@
false
false
false
+ 0
11151
@@ -89214,6 +100365,7 @@
false
false
false
+ 0
11152
@@ -89222,6 +100374,7 @@
false
false
false
+ 0
11153
@@ -89230,6 +100383,7 @@
false
false
false
+ 0
11154
@@ -89238,6 +100392,7 @@
false
false
false
+ 0
11155
@@ -89246,6 +100401,7 @@
false
false
false
+ 0
11156
@@ -89254,6 +100410,7 @@
false
false
false
+ 0
11157
@@ -89262,6 +100419,7 @@
false
false
false
+ 0
11158
@@ -89270,6 +100428,7 @@
false
false
false
+ 0
11159
@@ -89278,6 +100437,7 @@
false
false
false
+ 0
11160
@@ -89286,6 +100446,7 @@
false
false
false
+ 0
11161
@@ -89294,6 +100455,7 @@
false
false
false
+ 0
11162
@@ -89302,6 +100464,7 @@
false
false
false
+ 0
11163
@@ -89310,6 +100473,7 @@
false
false
false
+ 0
11164
@@ -89318,6 +100482,7 @@
false
false
false
+ 0
11165
@@ -89326,6 +100491,7 @@
false
false
false
+ 0
11166
@@ -89334,6 +100500,7 @@
false
false
false
+ 0
11167
@@ -89342,6 +100509,7 @@
false
false
false
+ 0
11168
@@ -89350,6 +100518,7 @@
false
false
false
+ 0
11169
@@ -89358,6 +100527,7 @@
false
false
false
+ 0
11170
@@ -89366,6 +100536,7 @@
false
false
false
+ 0
11171
@@ -89374,6 +100545,7 @@
false
false
false
+ 0
11172
@@ -89382,6 +100554,7 @@
false
false
false
+ 0
11173
@@ -89390,6 +100563,7 @@
false
false
false
+ 0
11174
@@ -89398,6 +100572,7 @@
false
false
false
+ 0
11175
@@ -89406,6 +100581,7 @@
false
false
false
+ 0
11176
@@ -89414,6 +100590,7 @@
false
false
false
+ 0
11177
@@ -89422,6 +100599,7 @@
false
false
false
+ 0
11178
@@ -89430,6 +100608,7 @@
false
false
false
+ 0
11179
@@ -89438,6 +100617,7 @@
false
false
false
+ 0
11180
@@ -89446,6 +100626,7 @@
false
false
false
+ 0
11181
@@ -89454,6 +100635,7 @@
false
false
false
+ 0
11182
@@ -89462,6 +100644,7 @@
false
false
false
+ 0
11183
@@ -89470,6 +100653,7 @@
false
false
false
+ 0
11184
@@ -89478,6 +100662,7 @@
false
false
false
+ 0
11185
@@ -89486,6 +100671,7 @@
false
false
false
+ 0
11186
@@ -89494,6 +100680,7 @@
false
false
false
+ 0
11187
@@ -89502,6 +100689,7 @@
false
false
false
+ 0
11188
@@ -89510,6 +100698,7 @@
false
false
false
+ 0
11189
@@ -89518,6 +100707,7 @@
false
false
false
+ 0
11190
@@ -89526,6 +100716,7 @@
false
false
false
+ 0
11191
@@ -89534,6 +100725,7 @@
false
false
false
+ 0
11192
@@ -89542,6 +100734,7 @@
false
false
false
+ 0
11193
@@ -89550,6 +100743,7 @@
false
false
false
+ 0
11194
@@ -89558,6 +100752,7 @@
false
false
false
+ 0
11195
@@ -89566,6 +100761,7 @@
false
false
false
+ 0
11196
@@ -89574,6 +100770,7 @@
false
false
false
+ 0
11197
@@ -89582,6 +100779,7 @@
false
false
false
+ 0
11198
@@ -89590,6 +100788,7 @@
false
false
false
+ 0
11199
@@ -89598,6 +100797,7 @@
false
false
false
+ 0
11200
@@ -89606,6 +100806,7 @@
false
false
false
+ 0
11201
@@ -89614,6 +100815,7 @@
false
false
false
+ 0
11202
@@ -89622,6 +100824,7 @@
false
false
false
+ 0
11203
@@ -89630,6 +100833,7 @@
false
false
false
+ 0
11204
@@ -89638,6 +100842,7 @@
false
false
false
+ 0
11205
@@ -89646,6 +100851,7 @@
false
false
false
+ 0
11206
@@ -89654,6 +100860,7 @@
false
false
false
+ 0
11207
@@ -89662,6 +100869,7 @@
false
false
false
+ 0
11208
@@ -89670,6 +100878,7 @@
false
false
false
+ 0
11209
@@ -89678,6 +100887,7 @@
false
false
false
+ 0
11210
@@ -89686,6 +100896,7 @@
false
false
false
+ 0
11211
@@ -89694,6 +100905,7 @@
false
false
false
+ 0
11212
@@ -89702,6 +100914,7 @@
false
false
false
+ 0
11213
@@ -89710,6 +100923,7 @@
false
false
false
+ 0
11214
@@ -89718,6 +100932,7 @@
false
false
false
+ 0
11215
@@ -89726,6 +100941,7 @@
false
false
false
+ 0
11216
@@ -89734,6 +100950,7 @@
false
false
false
+ 0
11217
@@ -89742,6 +100959,7 @@
false
false
false
+ 0
11218
@@ -89750,6 +100968,7 @@
false
false
false
+ 0
11219
@@ -89758,6 +100977,7 @@
false
false
false
+ 0
11220
@@ -89766,6 +100986,7 @@
false
false
false
+ 0
11221
@@ -89774,6 +100995,7 @@
false
false
false
+ 0
11222
@@ -89782,6 +101004,7 @@
false
false
false
+ 0
11223
@@ -89790,6 +101013,7 @@
false
false
false
+ 0
11224
@@ -89798,6 +101022,7 @@
false
false
false
+ 0
11225
@@ -89806,6 +101031,7 @@
false
false
false
+ 0
11226
@@ -89814,6 +101040,7 @@
false
false
false
+ 0
11227
@@ -89822,6 +101049,7 @@
false
false
false
+ 0
11228
@@ -89830,6 +101058,7 @@
false
false
false
+ 0
11229
@@ -89838,6 +101067,7 @@
false
false
false
+ 0
11230
@@ -89846,6 +101076,7 @@
false
false
false
+ 0
11231
@@ -89854,6 +101085,7 @@
false
false
false
+ 0
11232
@@ -89862,6 +101094,7 @@
false
false
false
+ 0
11233
@@ -89870,6 +101103,7 @@
false
false
false
+ 0
11234
@@ -89878,6 +101112,7 @@
false
false
false
+ 0
11235
@@ -89886,6 +101121,7 @@
false
false
false
+ 0
11236
@@ -89894,6 +101130,7 @@
false
false
false
+ 0
11237
@@ -89902,6 +101139,7 @@
false
false
false
+ 0
11238
@@ -89910,6 +101148,7 @@
false
false
false
+ 0
11239
@@ -89918,6 +101157,7 @@
false
false
false
+ 0
11240
@@ -89926,6 +101166,7 @@
false
false
false
+ 0
11241
@@ -89934,6 +101175,7 @@
false
false
false
+ 0
11242
@@ -89942,6 +101184,7 @@
false
false
false
+ 0
11243
@@ -89950,6 +101193,7 @@
false
false
false
+ 0
11244
@@ -89958,6 +101202,7 @@
false
false
false
+ 0
11245
@@ -89966,6 +101211,7 @@
false
false
false
+ 0
11246
@@ -89974,6 +101220,7 @@
false
false
false
+ 0
11247
@@ -89982,6 +101229,7 @@
false
false
false
+ 0
11248
@@ -89990,6 +101238,7 @@
false
false
false
+ 0
11249
@@ -89998,6 +101247,7 @@
false
false
false
+ 0
11250
@@ -90006,6 +101256,7 @@
false
false
false
+ 0
11251
@@ -90014,6 +101265,7 @@
false
false
false
+ 0
11252
@@ -90022,6 +101274,7 @@
false
false
false
+ 0
11253
@@ -90030,6 +101283,7 @@
false
false
false
+ 0
11254
@@ -90038,6 +101292,7 @@
false
false
false
+ 0
11255
@@ -90046,6 +101301,7 @@
false
false
false
+ 0
11256
@@ -90054,6 +101310,7 @@
false
false
false
+ 0
11257
@@ -90062,6 +101319,7 @@
false
false
false
+ 0
11258
@@ -90070,6 +101328,7 @@
false
false
false
+ 0
11259
@@ -90078,6 +101337,7 @@
false
false
false
+ 0
11260
@@ -90086,6 +101346,7 @@
false
false
false
+ 0
11261
@@ -90094,6 +101355,7 @@
false
false
false
+ 0
11262
@@ -90102,6 +101364,7 @@
false
false
false
+ 0
11263
@@ -90110,6 +101373,7 @@
false
false
false
+ 0
11264
@@ -90118,6 +101382,7 @@
false
false
false
+ 0
11265
@@ -90126,6 +101391,7 @@
false
false
false
+ 0
11266
@@ -90134,6 +101400,7 @@
false
false
false
+ 0
11267
@@ -90142,6 +101409,7 @@
false
false
false
+ 0
11268
@@ -90150,6 +101418,7 @@
false
false
false
+ 0
11269
@@ -90158,6 +101427,7 @@
false
false
false
+ 0
11270
@@ -90166,6 +101436,7 @@
false
false
false
+ 0
11271
@@ -90174,6 +101445,7 @@
false
false
false
+ 0
11272
@@ -90182,6 +101454,7 @@
false
false
false
+ 0
11273
@@ -90190,6 +101463,7 @@
false
false
false
+ 0
11274
@@ -90198,6 +101472,7 @@
false
false
false
+ 0
11275
@@ -90206,6 +101481,7 @@
false
false
false
+ 0
11276
@@ -90214,6 +101490,7 @@
false
false
false
+ 0
11277
@@ -90222,6 +101499,7 @@
false
false
false
+ 0
11278
@@ -90230,6 +101508,7 @@
false
false
false
+ 0
11279
@@ -90238,6 +101517,7 @@
false
false
false
+ 0
11280
@@ -90246,6 +101526,7 @@
false
false
false
+ 0
11281
@@ -90254,6 +101535,7 @@
false
false
false
+ 0
11282
@@ -90262,6 +101544,7 @@
false
false
false
+ 0
11283
@@ -90270,6 +101553,7 @@
false
false
false
+ 0
11284
@@ -90278,6 +101562,7 @@
false
false
false
+ 0
11285
@@ -90286,6 +101571,7 @@
false
false
false
+ 0
11286
@@ -90294,6 +101580,7 @@
false
false
false
+ 0
11287
@@ -90302,6 +101589,7 @@
false
false
false
+ 0
11288
@@ -90310,6 +101598,7 @@
false
false
false
+ 0
11289
@@ -90318,6 +101607,7 @@
false
false
false
+ 0
11290
@@ -90326,6 +101616,7 @@
false
false
false
+ 0
11291
@@ -90334,6 +101625,7 @@
false
false
false
+ 0
11292
@@ -90342,6 +101634,7 @@
false
false
false
+ 0
11293
@@ -90350,6 +101643,7 @@
false
false
false
+ 0
11294
@@ -90358,6 +101652,7 @@
false
false
false
+ 0
11295
@@ -90366,6 +101661,7 @@
false
false
false
+ 0
11296
@@ -90374,6 +101670,7 @@
false
false
false
+ 0
11297
@@ -90382,6 +101679,7 @@
false
false
false
+ 0
11298
@@ -90390,6 +101688,7 @@
false
false
false
+ 0
11299
@@ -90398,6 +101697,7 @@
false
false
false
+ 0
11300
@@ -90406,6 +101706,7 @@
false
false
false
+ 0
11301
@@ -90414,6 +101715,7 @@
false
false
false
+ 0
11302
@@ -90422,6 +101724,7 @@
false
false
false
+ 0
11303
@@ -90430,6 +101733,7 @@
false
false
false
+ 0
11304
@@ -90438,6 +101742,7 @@
false
false
false
+ 0
11305
@@ -90446,6 +101751,7 @@
false
false
false
+ 0
11306
@@ -90454,6 +101760,7 @@
false
false
false
+ 0
11307
@@ -90462,6 +101769,7 @@
false
false
false
+ 0
11308
@@ -90470,6 +101778,7 @@
false
false
false
+ 0
11309
@@ -90478,6 +101787,7 @@
false
false
false
+ 0
11310
@@ -90486,6 +101796,7 @@
false
false
false
+ 0
11311
@@ -90494,6 +101805,7 @@
false
false
false
+ 0
11312
@@ -90502,6 +101814,7 @@
false
false
false
+ 0
11313
@@ -90510,6 +101823,7 @@
false
false
false
+ 0
11314
@@ -90518,6 +101832,7 @@
false
false
false
+ 0
11315
@@ -90526,6 +101841,7 @@
false
false
false
+ 0
11316
@@ -90534,6 +101850,7 @@
false
false
false
+ 0
11317
@@ -90542,6 +101859,7 @@
false
false
false
+ 0
11318
@@ -90550,6 +101868,7 @@
false
false
false
+ 0
11319
@@ -90558,6 +101877,7 @@
false
false
false
+ 0
11320
@@ -90566,6 +101886,7 @@
false
false
false
+ 0
11321
@@ -90574,6 +101895,7 @@
false
false
false
+ 0
11322
@@ -90582,6 +101904,7 @@
false
false
false
+ 0
11323
@@ -90590,6 +101913,7 @@
false
false
false
+ 0
11324
@@ -90598,6 +101922,7 @@
false
false
false
+ 0
11325
@@ -90606,6 +101931,7 @@
false
false
false
+ 0
11326
@@ -90614,6 +101940,7 @@
false
false
false
+ 0
11327
@@ -90622,6 +101949,7 @@
false
false
false
+ 0
11328
@@ -90630,6 +101958,7 @@
false
false
false
+ 0
11329
@@ -90638,6 +101967,7 @@
false
false
false
+ 0
11330
@@ -90646,6 +101976,7 @@
false
false
false
+ 0
11331
@@ -90654,6 +101985,7 @@
false
false
false
+ 0
11332
@@ -90662,6 +101994,7 @@
false
false
false
+ 0
11333
@@ -90670,6 +102003,7 @@
false
false
false
+ 0
11334
@@ -90678,6 +102012,7 @@
false
false
false
+ 0
11335
@@ -90686,6 +102021,7 @@
false
false
false
+ 0
11336
@@ -90694,6 +102030,7 @@
false
false
false
+ 0
11337
@@ -90702,6 +102039,7 @@
false
false
false
+ 0
11338
@@ -90710,6 +102048,7 @@
false
false
false
+ 0
11339
@@ -90718,6 +102057,7 @@
false
false
false
+ 0
11340
@@ -90726,6 +102066,7 @@
false
false
false
+ 0
11341
@@ -90734,6 +102075,7 @@
false
false
false
+ 0
11342
@@ -90742,6 +102084,7 @@
false
false
false
+ 0
11343
@@ -90750,6 +102093,7 @@
false
false
false
+ 0
11344
@@ -90758,6 +102102,7 @@
false
false
false
+ 0
11345
@@ -90766,6 +102111,7 @@
false
false
false
+ 0
11346
@@ -90774,6 +102120,7 @@
false
false
false
+ 0
11347
@@ -90782,6 +102129,7 @@
false
false
false
+ 0
11348
@@ -90790,6 +102138,7 @@
false
false
false
+ 0
11349
@@ -90798,6 +102147,7 @@
false
false
false
+ 0
11350
@@ -90806,6 +102156,7 @@
false
false
false
+ 0
11351
@@ -90814,6 +102165,7 @@
false
false
false
+ 0
11352
@@ -90822,6 +102174,7 @@
false
false
false
+ 0
11353
@@ -90830,6 +102183,7 @@
false
false
false
+ 0
11354
@@ -90838,6 +102192,7 @@
false
false
false
+ 0
11355
@@ -90846,6 +102201,7 @@
false
false
false
+ 0
11356
@@ -90854,6 +102210,7 @@
false
false
false
+ 0
11357
@@ -90862,6 +102219,7 @@
false
false
false
+ 0
11358
@@ -90870,6 +102228,7 @@
false
false
false
+ 0
11359
@@ -90878,6 +102237,7 @@
false
false
false
+ 0
11360
@@ -90886,6 +102246,7 @@
false
false
false
+ 0
11361
@@ -90894,6 +102255,7 @@
false
false
false
+ 0
11362
@@ -90902,6 +102264,7 @@
false
false
false
+ 0
11363
@@ -90910,6 +102273,7 @@
false
false
false
+ 0
11364
@@ -90918,6 +102282,7 @@
false
false
false
+ 0
11365
@@ -90926,6 +102291,7 @@
false
false
false
+ 0
11366
@@ -90934,6 +102300,7 @@
false
false
false
+ 0
11367
@@ -90942,6 +102309,7 @@
false
false
false
+ 0
11368
@@ -90950,6 +102318,7 @@
false
false
false
+ 0
11369
@@ -90958,6 +102327,7 @@
false
false
false
+ 0
11370
@@ -90966,6 +102336,7 @@
false
false
false
+ 0
11371
@@ -90974,6 +102345,7 @@
false
false
false
+ 0
11372
@@ -90982,6 +102354,7 @@
false
false
false
+ 0
11373
@@ -90990,6 +102363,7 @@
false
false
false
+ 0
11374
@@ -90998,6 +102372,7 @@
false
false
false
+ 0
11375
@@ -91006,6 +102381,7 @@
false
false
false
+ 0
11376
@@ -91014,6 +102390,7 @@
false
false
false
+ 0
11377
@@ -91022,6 +102399,7 @@
false
false
false
+ 0
11378
@@ -91030,6 +102408,7 @@
false
false
false
+ 0
11379
@@ -91038,6 +102417,7 @@
false
false
false
+ 0
11380
@@ -91046,6 +102426,7 @@
false
false
false
+ 0
11381
@@ -91054,6 +102435,7 @@
false
false
false
+ 0
11382
@@ -91062,6 +102444,7 @@
false
false
false
+ 0
11383
@@ -91070,6 +102453,7 @@
false
false
false
+ 0
11384
@@ -91078,6 +102462,7 @@
false
false
false
+ 0
11385
@@ -91086,6 +102471,7 @@
false
false
false
+ 0
11386
@@ -91094,6 +102480,7 @@
false
false
false
+ 0
11387
@@ -91102,6 +102489,7 @@
false
false
false
+ 0
11388
@@ -91110,6 +102498,7 @@
false
false
false
+ 0
11389
@@ -91118,6 +102507,7 @@
false
false
false
+ 0
11390
@@ -91126,6 +102516,7 @@
false
false
false
+ 0
11391
@@ -91134,6 +102525,7 @@
false
false
false
+ 0
11392
@@ -91142,6 +102534,7 @@
false
false
false
+ 0
11393
@@ -91150,6 +102543,7 @@
false
false
false
+ 0
11394
@@ -91158,6 +102552,7 @@
false
false
false
+ 0
11395
@@ -91166,6 +102561,7 @@
false
false
false
+ 0
11396
@@ -91174,6 +102570,7 @@
false
false
false
+ 0
11397
@@ -91182,6 +102579,7 @@
false
false
false
+ 0
11398
@@ -91190,6 +102588,7 @@
false
false
false
+ 0
11399
@@ -91198,6 +102597,7 @@
false
false
false
+ 0
11400
@@ -91206,6 +102606,7 @@
false
false
false
+ 0
11401
@@ -91214,6 +102615,7 @@
false
false
false
+ 0
11402
@@ -91222,6 +102624,7 @@
false
false
false
+ 0
11403
@@ -91230,6 +102633,7 @@
false
false
false
+ 0
11404
@@ -91238,6 +102642,7 @@
false
false
false
+ 0
11405
@@ -91246,6 +102651,7 @@
false
false
false
+ 0
11406
@@ -91254,6 +102660,7 @@
false
false
false
+ 0
11407
@@ -91262,6 +102669,7 @@
false
false
false
+ 0
11408
@@ -91270,6 +102678,7 @@
false
false
false
+ 0
11409
@@ -91278,6 +102687,7 @@
false
false
false
+ 0
11410
@@ -91286,6 +102696,7 @@
false
false
false
+ 0
11411
@@ -91294,6 +102705,7 @@
false
false
false
+ 0
11412
@@ -91302,6 +102714,7 @@
false
false
false
+ 0
11413
@@ -91310,6 +102723,7 @@
false
false
false
+ 0
11414
@@ -91318,6 +102732,7 @@
false
false
false
+ 0
11415
@@ -91326,6 +102741,7 @@
false
false
false
+ 0
11416
@@ -91334,6 +102750,7 @@
false
false
false
+ 0
11417
@@ -91342,6 +102759,7 @@
false
false
false
+ 0
11418
@@ -91350,6 +102768,7 @@
false
false
false
+ 0
11419
@@ -91358,6 +102777,7 @@
false
false
false
+ 0
11420
@@ -91366,6 +102786,7 @@
false
false
false
+ 0
11421
@@ -91374,6 +102795,7 @@
false
false
false
+ 0
11422
@@ -91382,6 +102804,7 @@
false
false
false
+ 0
11423
@@ -91390,6 +102813,7 @@
false
false
false
+ 0
11424
@@ -91398,6 +102822,7 @@
false
false
false
+ 0
11425
@@ -91406,6 +102831,7 @@
false
false
false
+ 0
11426
@@ -91414,6 +102840,7 @@
false
false
false
+ 0
11427
@@ -91422,6 +102849,7 @@
false
false
false
+ 0
11428
@@ -91430,6 +102858,7 @@
false
false
false
+ 0
11429
@@ -91438,6 +102867,7 @@
false
false
false
+ 0
11430
@@ -91446,6 +102876,7 @@
false
false
false
+ 0
11431
@@ -91454,6 +102885,7 @@
false
false
false
+ 0
11432
@@ -91462,6 +102894,7 @@
false
false
false
+ 0
11433
@@ -91470,6 +102903,7 @@
false
false
false
+ 0
11434
@@ -91478,6 +102912,7 @@
false
false
false
+ 0
11435
@@ -91486,6 +102921,7 @@
false
false
false
+ 0
11436
@@ -91494,6 +102930,7 @@
false
false
false
+ 0
11437
@@ -91502,6 +102939,7 @@
false
false
false
+ 0
11438
@@ -91510,6 +102948,7 @@
false
false
false
+ 0
11439
@@ -91518,6 +102957,7 @@
false
false
false
+ 0
11440
@@ -91526,6 +102966,7 @@
false
false
false
+ 0
11441
@@ -91534,6 +102975,7 @@
false
false
false
+ 0
11442
@@ -91542,6 +102984,7 @@
false
false
false
+ 0
11443
@@ -91550,6 +102993,7 @@
false
false
false
+ 0
11444
@@ -91558,6 +103002,7 @@
false
false
false
+ 0
11445
@@ -91566,6 +103011,7 @@
false
false
false
+ 0
11446
@@ -91574,6 +103020,7 @@
false
false
false
+ 0
11447
@@ -91582,6 +103029,7 @@
false
false
false
+ 0
11448
@@ -91590,6 +103038,7 @@
false
false
false
+ 0
11449
@@ -91598,6 +103047,7 @@
false
false
false
+ 0
11450
@@ -91606,6 +103056,7 @@
false
false
false
+ 0
11451
@@ -91614,6 +103065,7 @@
false
false
false
+ 0
11452
@@ -91622,6 +103074,7 @@
false
false
false
+ 0
11453
@@ -91630,6 +103083,7 @@
false
false
false
+ 0
11454
@@ -91638,6 +103092,7 @@
false
false
false
+ 0
11455
@@ -91646,6 +103101,7 @@
false
false
false
+ 0
11456
@@ -91654,6 +103110,7 @@
false
false
false
+ 0
11457
@@ -91662,6 +103119,7 @@
false
false
false
+ 0
11458
@@ -91670,6 +103128,7 @@
false
false
false
+ 0
11459
@@ -91678,6 +103137,7 @@
false
false
false
+ 0
11460
@@ -91686,6 +103146,7 @@
false
false
false
+ 0
11461
@@ -91694,6 +103155,7 @@
false
false
false
+ 0
11462
@@ -91702,6 +103164,7 @@
false
false
false
+ 0
11463
@@ -91710,6 +103173,7 @@
false
false
false
+ 0
11464
@@ -91718,6 +103182,7 @@
false
false
false
+ 0
11465
@@ -91726,6 +103191,7 @@
false
false
false
+ 0
11466
@@ -91734,6 +103200,7 @@
false
false
false
+ 0
11467
@@ -91742,6 +103209,7 @@
false
false
false
+ 0
11468
@@ -91750,6 +103218,7 @@
false
false
false
+ 0
11469
@@ -91758,6 +103227,7 @@
false
false
false
+ 0
11470
@@ -91766,6 +103236,7 @@
false
false
false
+ 0
11471
@@ -91774,6 +103245,7 @@
false
false
false
+ 0
11472
@@ -91782,6 +103254,7 @@
false
false
false
+ 0
11473
@@ -91790,6 +103263,7 @@
false
false
false
+ 0
11474
@@ -91798,6 +103272,7 @@
false
false
false
+ 0
11475
@@ -91806,6 +103281,7 @@
false
false
false
+ 0
11476
@@ -91814,6 +103290,7 @@
false
false
false
+ 0
11477
@@ -91822,6 +103299,7 @@
false
false
false
+ 0
11478
@@ -91830,6 +103308,7 @@
false
false
false
+ 0
11479
@@ -91838,6 +103317,7 @@
false
false
false
+ 0
11480
@@ -91846,6 +103326,7 @@
false
false
false
+ 0
11481
@@ -91854,6 +103335,7 @@
false
false
false
+ 0
11482
@@ -91862,6 +103344,7 @@
false
false
false
+ 0
11483
@@ -91870,6 +103353,7 @@
false
false
false
+ 0
11484
@@ -91878,6 +103362,7 @@
false
false
false
+ 0
11485
@@ -91886,6 +103371,7 @@
false
false
false
+ 0
11486
@@ -91894,6 +103380,7 @@
false
false
false
+ 0
11487
@@ -91902,6 +103389,7 @@
false
false
false
+ 0
11488
@@ -91910,6 +103398,7 @@
false
false
false
+ 0
11489
@@ -91918,6 +103407,7 @@
false
false
false
+ 0
11490
@@ -91926,6 +103416,7 @@
false
false
false
+ 0
11491
@@ -91934,6 +103425,7 @@
false
false
false
+ 0
11492
@@ -91942,6 +103434,7 @@
false
false
false
+ 0
11493
@@ -91950,6 +103443,7 @@
false
false
false
+ 0
11494
@@ -91958,6 +103452,7 @@
false
false
false
+ 0
11495
@@ -91966,6 +103461,7 @@
false
false
false
+ 0
11496
@@ -91974,6 +103470,7 @@
false
false
false
+ 0
11497
@@ -91982,6 +103479,7 @@
false
false
false
+ 0
11498
@@ -91990,6 +103488,7 @@
false
false
false
+ 0
11499
@@ -91998,6 +103497,7 @@
false
false
false
+ 0
11500
@@ -92006,6 +103506,7 @@
false
false
false
+ 0
11501
@@ -92014,6 +103515,7 @@
false
false
false
+ 0
11502
@@ -92022,6 +103524,7 @@
false
false
false
+ 0
11503
@@ -92030,6 +103533,7 @@
false
false
false
+ 0
11504
@@ -92038,6 +103542,7 @@
false
false
false
+ 0
11505
@@ -92046,6 +103551,7 @@
false
false
false
+ 0
11506
@@ -92054,6 +103560,7 @@
false
false
false
+ 0
11507
@@ -92062,6 +103569,7 @@
false
false
false
+ 0
11508
@@ -92070,6 +103578,7 @@
false
false
false
+ 0
11509
@@ -92078,6 +103587,7 @@
false
false
false
+ 0
11510
@@ -92086,6 +103596,7 @@
false
false
false
+ 0
11511
@@ -92094,6 +103605,7 @@
false
false
false
+ 0
11512
@@ -92102,6 +103614,7 @@
false
false
false
+ 0
11513
@@ -92110,6 +103623,7 @@
false
false
false
+ 0
11514
@@ -92118,6 +103632,7 @@
false
false
false
+ 0
11515
@@ -92126,6 +103641,7 @@
false
false
false
+ 0
11516
@@ -92134,6 +103650,7 @@
false
false
false
+ 0
11517
@@ -92142,6 +103659,7 @@
false
false
false
+ 0
11518
@@ -92150,6 +103668,7 @@
false
false
false
+ 0
11519
@@ -92158,6 +103677,7 @@
false
false
false
+ 0
11520
@@ -92166,6 +103686,7 @@
false
false
false
+ 0
11521
@@ -92174,6 +103695,7 @@
false
false
false
+ 0
11522
@@ -92182,6 +103704,7 @@
false
false
false
+ 0
11523
@@ -92190,6 +103713,7 @@
false
false
false
+ 0
11524
@@ -92198,6 +103722,7 @@
false
false
false
+ 0
11525
@@ -92206,6 +103731,7 @@
false
false
false
+ 0
11526
@@ -92214,6 +103740,7 @@
false
false
false
+ 0
11527
@@ -92222,6 +103749,7 @@
false
false
false
+ 0
11528
@@ -92230,6 +103758,7 @@
false
false
false
+ 0
11529
@@ -92238,6 +103767,7 @@
false
false
false
+ 0
11530
@@ -92246,6 +103776,7 @@
false
false
false
+ 0
11531
@@ -92254,6 +103785,7 @@
false
false
false
+ 0
11532
@@ -92262,6 +103794,7 @@
false
false
false
+ 0
11533
@@ -92270,6 +103803,7 @@
false
false
false
+ 0
11534
@@ -92278,6 +103812,7 @@
false
false
false
+ 0
11535
@@ -92286,6 +103821,7 @@
false
false
false
+ 0
11536
@@ -92294,6 +103830,7 @@
false
false
false
+ 0
11537
@@ -92302,6 +103839,7 @@
false
false
false
+ 0
11538
@@ -92310,6 +103848,7 @@
false
false
false
+ 0
11539
@@ -92318,6 +103857,7 @@
false
false
false
+ 0
11540
@@ -92326,6 +103866,7 @@
false
false
false
+ 0
11541
@@ -92334,6 +103875,7 @@
false
false
false
+ 0
11542
@@ -92342,6 +103884,7 @@
false
false
false
+ 0
11543
@@ -92350,6 +103893,7 @@
false
false
false
+ 0
11544
@@ -92358,6 +103902,7 @@
false
false
false
+ 0
11545
@@ -92366,6 +103911,7 @@
false
false
false
+ 0
11546
@@ -92374,6 +103920,7 @@
false
false
false
+ 0
11547
@@ -92382,6 +103929,7 @@
false
false
false
+ 0
11548
@@ -92390,6 +103938,7 @@
false
false
false
+ 0
11549
@@ -92398,6 +103947,7 @@
false
false
false
+ 0
11550
@@ -92406,6 +103956,7 @@
false
false
false
+ 0
11551
@@ -92414,6 +103965,7 @@
false
false
false
+ 0
11552
@@ -92422,6 +103974,7 @@
false
false
false
+ 0
11553
@@ -92430,6 +103983,7 @@
false
false
false
+ 0
11554
@@ -92438,6 +103992,7 @@
false
false
false
+ 0
11555
@@ -92446,6 +104001,7 @@
false
false
false
+ 0
11556
@@ -92454,6 +104010,7 @@
false
false
false
+ 0
11557
@@ -92462,6 +104019,7 @@
false
false
false
+ 0
11558
@@ -92470,6 +104028,7 @@
false
false
false
+ 0
11559
@@ -92478,6 +104037,7 @@
false
false
false
+ 0
11560
@@ -92486,6 +104046,7 @@
false
false
false
+ 0
11561
@@ -92494,6 +104055,7 @@
false
false
false
+ 0
11562
@@ -92502,6 +104064,7 @@
false
false
false
+ 0
11563
@@ -92510,6 +104073,7 @@
false
false
false
+ 0
11564
@@ -92518,6 +104082,7 @@
false
false
false
+ 0
11565
@@ -92526,6 +104091,7 @@
false
false
false
+ 0
11566
@@ -92534,6 +104100,7 @@
false
false
false
+ 0
11567
@@ -92542,6 +104109,7 @@
false
false
false
+ 0
11568
@@ -92550,6 +104118,7 @@
false
false
false
+ 0
11569
@@ -92558,6 +104127,7 @@
false
false
false
+ 0
11570
@@ -92566,6 +104136,7 @@
false
false
false
+ 0
11571
@@ -92574,6 +104145,7 @@
false
false
false
+ 0
11572
@@ -92582,6 +104154,7 @@
false
false
false
+ 0
11573
@@ -92590,6 +104163,7 @@
false
false
false
+ 0
11574
@@ -92598,6 +104172,7 @@
false
false
false
+ 0
11575
@@ -92606,6 +104181,7 @@
false
false
false
+ 0
11576
@@ -92614,6 +104190,7 @@
false
false
false
+ 0
11577
@@ -92622,6 +104199,7 @@
false
false
false
+ 0
11578
@@ -92630,6 +104208,7 @@
false
false
false
+ 0
11579
@@ -92638,6 +104217,7 @@
false
false
false
+ 0
11580
@@ -92646,6 +104226,7 @@
false
false
false
+ 0
11581
@@ -92654,6 +104235,7 @@
false
false
false
+ 0
11582
@@ -92662,6 +104244,7 @@
false
false
false
+ 0
11583
@@ -92670,6 +104253,7 @@
false
false
false
+ 0
11584
@@ -92678,6 +104262,7 @@
false
false
false
+ 0
11585
@@ -92686,6 +104271,7 @@
false
false
false
+ 0
11586
@@ -92694,6 +104280,7 @@
false
false
false
+ 0
11587
@@ -92702,6 +104289,7 @@
false
false
false
+ 0
11588
@@ -92710,6 +104298,7 @@
false
false
false
+ 0
11589
@@ -92718,6 +104307,7 @@
false
false
false
+ 0
11590
@@ -92726,6 +104316,7 @@
false
false
false
+ 0
11591
@@ -92734,6 +104325,7 @@
false
false
false
+ 0
11592
@@ -92742,6 +104334,7 @@
false
false
false
+ 0
11593
@@ -92750,6 +104343,7 @@
false
false
false
+ 0
11594
@@ -92758,6 +104352,7 @@
false
false
false
+ 0
11595
@@ -92766,6 +104361,7 @@
false
false
false
+ 0
11596
@@ -92774,6 +104370,7 @@
false
false
false
+ 0
11597
@@ -92782,6 +104379,7 @@
false
false
false
+ 0
11598
@@ -92790,6 +104388,7 @@
false
false
false
+ 0
11599
@@ -92798,6 +104397,7 @@
false
false
false
+ 0
11600
@@ -92806,6 +104406,7 @@
false
false
false
+ 0
11601
@@ -92814,6 +104415,7 @@
false
false
false
+ 0
11602
@@ -92822,6 +104424,7 @@
false
false
false
+ 0
11603
@@ -92830,6 +104433,7 @@
false
false
false
+ 0
11604
@@ -92838,6 +104442,7 @@
false
false
false
+ 0
11605
@@ -92846,6 +104451,7 @@
false
false
false
+ 0
11606
@@ -92854,6 +104460,7 @@
false
false
false
+ 0
11607
@@ -92862,6 +104469,7 @@
false
false
false
+ 0
11608
@@ -92870,6 +104478,7 @@
false
false
false
+ 0
11609
@@ -92878,6 +104487,7 @@
false
false
false
+ 0
11610
@@ -92886,6 +104496,7 @@
false
false
false
+ 0
11611
@@ -92894,6 +104505,7 @@
false
false
false
+ 0
11612
@@ -92902,6 +104514,7 @@
false
false
false
+ 0
11613
@@ -92910,6 +104523,7 @@
false
false
false
+ 0
11614
@@ -92918,6 +104532,7 @@
false
false
false
+ 0
11615
@@ -92926,6 +104541,7 @@
false
false
false
+ 0
11616
@@ -92934,6 +104550,7 @@
false
false
false
+ 0
11617
@@ -92942,6 +104559,7 @@
false
false
false
+ 0
11618
@@ -92950,6 +104568,7 @@
false
false
false
+ 0
11619
@@ -92958,6 +104577,7 @@
false
false
false
+ 0
11620
@@ -92966,6 +104586,7 @@
false
false
false
+ 0
11621
@@ -92974,6 +104595,7 @@
false
false
false
+ 0
11622
@@ -92982,6 +104604,7 @@
false
false
false
+ 0
11623
@@ -92990,6 +104613,7 @@
false
false
false
+ 0
11624
@@ -92998,6 +104622,7 @@
false
false
false
+ 0
11625
@@ -93006,6 +104631,7 @@
false
false
false
+ 0
11626
@@ -93014,6 +104640,7 @@
false
false
false
+ 0
11627
@@ -93022,6 +104649,7 @@
false
false
false
+ 0
11628
@@ -93030,6 +104658,7 @@
false
false
false
+ 0
11629
@@ -93038,6 +104667,7 @@
false
false
false
+ 0
11630
@@ -93046,6 +104676,7 @@
false
false
false
+ 0
11631
@@ -93054,6 +104685,7 @@
false
false
false
+ 0
11632
@@ -93062,6 +104694,7 @@
false
false
false
+ 0
11633
@@ -93070,6 +104703,7 @@
false
false
false
+ 0
11634
@@ -93078,6 +104712,7 @@
false
false
false
+ 0
11635
@@ -93086,6 +104721,7 @@
false
false
false
+ 0
11636
@@ -93094,6 +104730,7 @@
false
false
false
+ 0
11637
@@ -93102,6 +104739,7 @@
false
false
false
+ 0
11638
@@ -93110,6 +104748,7 @@
false
false
false
+ 0
11639
@@ -93118,6 +104757,7 @@
false
false
false
+ 0
11640
@@ -93126,6 +104766,7 @@
false
false
false
+ 0
11641
@@ -93134,6 +104775,7 @@
false
false
false
+ 0
11642
@@ -93142,6 +104784,7 @@
false
false
false
+ 0
11643
@@ -93150,6 +104793,7 @@
false
false
false
+ 0
11644
@@ -93158,6 +104802,7 @@
false
false
false
+ 0
11645
@@ -93166,6 +104811,7 @@
false
false
false
+ 0
11646
@@ -93174,6 +104820,7 @@
false
false
false
+ 0
11647
@@ -93182,6 +104829,7 @@
false
false
false
+ 0
11648
@@ -93190,6 +104838,7 @@
false
false
false
+ 0
11649
@@ -93198,6 +104847,7 @@
false
false
false
+ 0
11650
@@ -93206,6 +104856,7 @@
false
false
false
+ 0
11651
@@ -93214,6 +104865,7 @@
false
false
false
+ 0
11652
@@ -93222,6 +104874,7 @@
false
false
false
+ 0
11653
@@ -93230,6 +104883,7 @@
false
false
false
+ 0
11654
@@ -93238,6 +104892,7 @@
false
false
false
+ 0
11655
@@ -93246,6 +104901,7 @@
false
false
false
+ 0
11656
@@ -93254,6 +104910,7 @@
false
false
false
+ 0
11657
@@ -93262,6 +104919,7 @@
false
false
false
+ 0
11658
@@ -93270,6 +104928,7 @@
false
false
false
+ 0
11659
@@ -93278,6 +104937,7 @@
false
false
false
+ 0
11660
@@ -93286,6 +104946,7 @@
false
false
false
+ 0
11661
@@ -93294,6 +104955,7 @@
false
false
false
+ 0
11662
@@ -93302,6 +104964,7 @@
false
false
false
+ 0
11663
@@ -93310,6 +104973,7 @@
false
false
false
+ 0
11664
@@ -93318,6 +104982,7 @@
false
false
false
+ 0
11665
@@ -93326,6 +104991,7 @@
false
false
false
+ 0
11666
@@ -93334,6 +105000,7 @@
false
false
false
+ 0
11667
@@ -93342,6 +105009,7 @@
false
false
false
+ 0
11668
@@ -93350,6 +105018,7 @@
false
false
false
+ 0
11669
@@ -93358,6 +105027,7 @@
false
false
false
+ 0
11670
@@ -93366,6 +105036,7 @@
false
false
false
+ 0
11671
@@ -93374,6 +105045,7 @@
false
false
false
+ 0
11672
@@ -93382,6 +105054,7 @@
false
false
false
+ 0
11673
@@ -93390,6 +105063,7 @@
false
false
false
+ 0
11674
@@ -93398,6 +105072,7 @@
false
false
false
+ 0
11675
@@ -93406,6 +105081,7 @@
false
false
false
+ 0
11676
@@ -93414,6 +105090,7 @@
false
false
false
+ 0
11677
@@ -93422,6 +105099,7 @@
false
false
false
+ 0
11678
@@ -93430,6 +105108,7 @@
false
false
false
+ 0
11679
@@ -93438,6 +105117,7 @@
false
false
false
+ 0
11680
@@ -93446,6 +105126,7 @@
false
false
false
+ 0
11681
@@ -93454,6 +105135,7 @@
false
false
false
+ 0
11682
@@ -93462,6 +105144,7 @@
false
false
false
+ 0
11683
@@ -93470,6 +105153,7 @@
false
false
false
+ 0
11684
@@ -93478,6 +105162,7 @@
false
false
false
+ 0
11685
@@ -93486,6 +105171,7 @@
false
false
false
+ 0
11686
@@ -93494,6 +105180,7 @@
false
false
false
+ 0
11687
@@ -93502,6 +105189,7 @@
false
false
false
+ 0
11688
@@ -93510,6 +105198,7 @@
false
false
false
+ 0
11689
@@ -93518,6 +105207,7 @@
false
false
false
+ 0
11690
@@ -93526,6 +105216,7 @@
false
false
false
+ 0
11691
@@ -93534,6 +105225,7 @@
false
false
false
+ 0
11692
@@ -93542,6 +105234,7 @@
false
false
false
+ 0
11693
@@ -93550,6 +105243,7 @@
false
false
false
+ 0
11694
@@ -93558,6 +105252,7 @@
false
false
false
+ 0
11695
@@ -93566,6 +105261,7 @@
false
false
false
+ 0
11696
@@ -93574,6 +105270,7 @@
false
false
false
+ 0
11697
@@ -93582,6 +105279,7 @@
false
false
false
+ 0
11698
@@ -93590,6 +105288,7 @@
false
false
false
+ 0
11699
@@ -93598,6 +105297,7 @@
false
false
false
+ 0
11700
@@ -93606,6 +105306,7 @@
false
false
false
+ 0
11701
@@ -93614,6 +105315,7 @@
false
false
false
+ 0
11702
@@ -93622,6 +105324,7 @@
false
false
false
+ 0
11703
@@ -93630,6 +105333,7 @@
false
false
false
+ 0
11704
@@ -93638,6 +105342,7 @@
false
false
false
+ 0
11705
@@ -93646,6 +105351,7 @@
false
false
false
+ 0
11706
@@ -93654,6 +105360,7 @@
false
false
false
+ 0
11707
@@ -93662,6 +105369,7 @@
false
false
false
+ 0
11708
@@ -93670,6 +105378,7 @@
false
false
false
+ 0
11709
@@ -93678,6 +105387,7 @@
false
false
false
+ 0
11710
@@ -93686,6 +105396,7 @@
false
false
false
+ 0
11711
@@ -93694,6 +105405,7 @@
false
false
false
+ 0
11712
@@ -93702,6 +105414,7 @@
false
false
false
+ 0
11713
@@ -93710,6 +105423,7 @@
false
false
false
+ 0
11714
@@ -93718,6 +105432,7 @@
false
false
false
+ 0
11715
@@ -93726,6 +105441,7 @@
false
false
false
+ 0
11716
@@ -93734,6 +105450,7 @@
false
false
false
+ 0
11717
@@ -93742,6 +105459,7 @@
false
false
false
+ 0
11718
@@ -93750,6 +105468,7 @@
false
false
false
+ 0
11719
@@ -93758,6 +105477,7 @@
false
false
false
+ 0
11720
@@ -93766,6 +105486,7 @@
false
false
false
+ 0
11721
@@ -93774,6 +105495,7 @@
false
false
false
+ 0
11722
@@ -93782,6 +105504,7 @@
false
false
false
+ 0
11723
@@ -93790,6 +105513,7 @@
false
false
false
+ 0
11724
@@ -93798,6 +105522,7 @@
false
false
false
+ 0
11725
@@ -93806,6 +105531,7 @@
false
false
false
+ 0
11726
@@ -93814,6 +105540,7 @@
false
false
false
+ 0
11727
@@ -93822,6 +105549,7 @@
false
false
false
+ 0
11728
@@ -93830,6 +105558,7 @@
false
false
false
+ 0
11729
@@ -93838,6 +105567,7 @@
false
false
false
+ 0
11730
@@ -93846,6 +105576,7 @@
false
false
false
+ 0
11731
@@ -93854,6 +105585,7 @@
false
false
false
+ 0
11732
@@ -93862,6 +105594,7 @@
false
false
false
+ 0
11733
@@ -93870,6 +105603,7 @@
false
false
false
+ 0
11734
@@ -93878,6 +105612,7 @@
false
false
false
+ 0
11735
@@ -93886,6 +105621,7 @@
false
false
false
+ 0
11736
@@ -93894,6 +105630,7 @@
false
false
false
+ 0
11737
@@ -93902,6 +105639,7 @@
false
false
false
+ 0
11738
@@ -93910,6 +105648,7 @@
false
false
false
+ 0
11739
@@ -93918,6 +105657,7 @@
false
false
false
+ 0
11740
@@ -93926,6 +105666,7 @@
false
false
false
+ 0
11741
@@ -93934,6 +105675,7 @@
false
false
false
+ 0
11742
@@ -93942,6 +105684,7 @@
false
false
false
+ 0
11743
@@ -93950,6 +105693,7 @@
false
false
false
+ 0
11744
@@ -93958,6 +105702,7 @@
false
false
false
+ 0
11745
@@ -93966,6 +105711,7 @@
false
false
false
+ 0
11746
@@ -93974,6 +105720,7 @@
false
false
false
+ 0
11747
@@ -93982,6 +105729,7 @@
false
false
false
+ 0
11748
@@ -93990,6 +105738,7 @@
false
false
false
+ 0
11749
@@ -93998,6 +105747,7 @@
false
false
false
+ 0
11750
@@ -94006,6 +105756,7 @@
false
false
false
+ 0
11751
@@ -94014,6 +105765,7 @@
false
false
false
+ 0
11752
@@ -94022,6 +105774,7 @@
false
false
false
+ 0
11753
@@ -94030,6 +105783,7 @@
false
false
false
+ 0
11754
@@ -94038,6 +105792,7 @@
false
false
false
+ 0
11755
@@ -94046,6 +105801,7 @@
false
false
false
+ 0
11756
@@ -94054,6 +105810,7 @@
false
false
false
+ 0
11757
@@ -94062,6 +105819,7 @@
false
false
false
+ 0
11758
@@ -94070,6 +105828,7 @@
false
false
false
+ 0
11759
@@ -94078,6 +105837,7 @@
false
false
false
+ 0
11760
@@ -94086,6 +105846,7 @@
false
false
false
+ 0
11761
@@ -94094,6 +105855,7 @@
false
false
false
+ 0
11762
@@ -94102,6 +105864,7 @@
false
false
false
+ 0
11763
@@ -94110,6 +105873,7 @@
false
false
false
+ 0
11764
@@ -94118,6 +105882,7 @@
false
false
false
+ 0
11765
@@ -94126,6 +105891,7 @@
false
false
false
+ 0
11766
@@ -94134,6 +105900,7 @@
false
false
false
+ 0
11767
@@ -94142,6 +105909,7 @@
false
false
false
+ 0
11768
@@ -94150,6 +105918,7 @@
false
false
false
+ 0
11769
@@ -94158,6 +105927,7 @@
false
false
false
+ 0
11770
@@ -94166,6 +105936,7 @@
false
false
false
+ 0
11771
@@ -94174,6 +105945,7 @@
false
false
false
+ 0
11772
@@ -94182,6 +105954,7 @@
false
false
false
+ 0
11773
@@ -94190,6 +105963,7 @@
false
false
false
+ 0
11774
@@ -94198,6 +105972,7 @@
false
false
false
+ 0
11775
@@ -94206,6 +105981,7 @@
false
false
false
+ 0
11776
@@ -94214,6 +105990,7 @@
false
false
false
+ 0
11777
@@ -94222,6 +105999,7 @@
false
false
false
+ 0
11778
@@ -94230,6 +106008,7 @@
false
false
false
+ 0
11779
@@ -94238,6 +106017,7 @@
false
false
false
+ 0
11780
@@ -94246,6 +106026,7 @@
false
false
false
+ 0
11781
@@ -94254,6 +106035,7 @@
false
false
false
+ 0
11782
@@ -94262,6 +106044,7 @@
false
false
false
+ 0
11783
@@ -94270,6 +106053,7 @@
false
false
false
+ 0
11784
@@ -94278,6 +106062,7 @@
false
false
false
+ 0
11785
@@ -94286,6 +106071,7 @@
false
false
false
+ 0
11786
@@ -94294,6 +106080,7 @@
false
false
false
+ 0
11787
@@ -94302,6 +106089,7 @@
false
false
false
+ 0
11788
@@ -94310,6 +106098,7 @@
false
false
false
+ 0
11789
@@ -94318,6 +106107,7 @@
false
false
false
+ 0
11790
@@ -94326,6 +106116,7 @@
false
false
false
+ 0
11791
@@ -94334,6 +106125,7 @@
false
false
false
+ 0
11792
@@ -94342,6 +106134,7 @@
false
false
false
+ 0
11793
@@ -94350,6 +106143,7 @@
false
false
false
+ 0
11794
@@ -94358,6 +106152,7 @@
false
false
false
+ 0
11795
@@ -94366,6 +106161,7 @@
false
false
false
+ 0
11796
@@ -94374,6 +106170,7 @@
false
false
false
+ 0
11797
@@ -94382,6 +106179,7 @@
false
false
false
+ 0
11798
@@ -94390,6 +106188,7 @@
false
false
false
+ 0
11799
@@ -94398,6 +106197,7 @@
false
false
false
+ 0
11800
@@ -94406,6 +106206,7 @@
false
false
false
+ 0
11801
@@ -94414,6 +106215,7 @@
false
false
false
+ 0
11802
@@ -94422,6 +106224,7 @@
false
false
false
+ 0
11803
@@ -94430,6 +106233,7 @@
false
false
false
+ 0
11804
@@ -94438,6 +106242,7 @@
false
false
false
+ 0
11805
@@ -94446,6 +106251,7 @@
false
false
false
+ 0
11806
@@ -94454,6 +106260,7 @@
false
false
false
+ 0
11807
@@ -94462,6 +106269,7 @@
false
false
false
+ 0
11808
@@ -94470,6 +106278,7 @@
false
false
false
+ 0
11809
@@ -94478,6 +106287,7 @@
false
false
false
+ 0
11810
@@ -94486,6 +106296,7 @@
false
false
false
+ 0
11811
@@ -94494,6 +106305,7 @@
false
false
false
+ 0
11812
@@ -94502,6 +106314,7 @@
false
false
false
+ 0
11813
@@ -94510,6 +106323,7 @@
false
false
false
+ 0
11814
@@ -94518,6 +106332,7 @@
false
false
false
+ 0
11815
@@ -94526,6 +106341,7 @@
false
false
false
+ 0
11816
@@ -94534,6 +106350,7 @@
false
false
false
+ 0
11817
@@ -94542,6 +106359,7 @@
false
false
false
+ 0
11818
@@ -94550,6 +106368,7 @@
false
false
false
+ 0
11819
@@ -94558,6 +106377,7 @@
false
false
false
+ 0
11820
@@ -94566,6 +106386,7 @@
false
false
false
+ 0
11821
@@ -94574,6 +106395,7 @@
false
false
false
+ 0
11822
@@ -94582,6 +106404,7 @@
false
false
false
+ 0
11823
@@ -94590,6 +106413,7 @@
false
false
false
+ 0
11824
@@ -94598,6 +106422,7 @@
false
false
false
+ 0
11825
@@ -94606,6 +106431,7 @@
false
false
false
+ 0
11826
@@ -94614,6 +106440,7 @@
false
false
false
+ 0
11827
@@ -94622,6 +106449,7 @@
false
false
false
+ 0
11828
@@ -94630,6 +106458,7 @@
false
false
false
+ 0
11829
@@ -94638,6 +106467,7 @@
false
false
false
+ 0
11830
@@ -94646,6 +106476,7 @@
false
false
false
+ 0
11831
@@ -94654,6 +106485,7 @@
false
false
false
+ 0
11832
@@ -94662,6 +106494,7 @@
false
false
false
+ 0
11833
@@ -94670,6 +106503,7 @@
false
false
false
+ 0
11834
@@ -94678,6 +106512,7 @@
false
false
false
+ 0
11835
@@ -94686,6 +106521,7 @@
false
false
false
+ 0
11836
@@ -94694,6 +106530,7 @@
false
false
false
+ 0
11837
@@ -94702,6 +106539,7 @@
false
false
false
+ 0
11838
@@ -94710,6 +106548,7 @@
false
false
false
+ 0
11839
@@ -94718,6 +106557,7 @@
false
false
false
+ 0
11840
@@ -94726,6 +106566,7 @@
false
false
false
+ 0
11841
@@ -94734,6 +106575,7 @@
false
false
false
+ 0
11842
@@ -94742,6 +106584,7 @@
false
false
false
+ 0
11843
@@ -94750,6 +106593,7 @@
false
false
false
+ 0
11844
@@ -94758,6 +106602,7 @@
false
false
false
+ 0
11845
@@ -94766,6 +106611,7 @@
false
false
false
+ 0
11846
@@ -94774,6 +106620,7 @@
false
false
false
+ 0
11847
@@ -94782,6 +106629,7 @@
false
false
false
+ 0
11848
@@ -94790,6 +106638,7 @@
false
false
false
+ 0
11849
@@ -94798,6 +106647,7 @@
false
false
false
+ 0
11850
@@ -94806,6 +106656,7 @@
false
false
false
+ 0
11851
@@ -94814,6 +106665,7 @@
false
false
false
+ 0
11852
@@ -94822,6 +106674,7 @@
false
false
false
+ 0
11853
@@ -94830,6 +106683,7 @@
false
false
false
+ 0
11854
@@ -94838,6 +106692,7 @@
false
false
false
+ 0
11855
@@ -94846,6 +106701,7 @@
false
false
false
+ 0
11856
@@ -94854,6 +106710,7 @@
false
false
false
+ 0
11857
@@ -94862,6 +106719,7 @@
false
false
false
+ 0
11858
@@ -94870,6 +106728,7 @@
false
false
false
+ 0
11859
@@ -94878,6 +106737,7 @@
false
false
false
+ 0
11860
@@ -94886,6 +106746,7 @@
false
false
false
+ 0
11861
@@ -94894,6 +106755,7 @@
false
false
false
+ 0
11862
@@ -94902,6 +106764,7 @@
false
false
false
+ 0
11863
@@ -94910,6 +106773,7 @@
false
false
false
+ 0
11864
@@ -94918,6 +106782,7 @@
false
false
false
+ 0
11865
@@ -94926,6 +106791,7 @@
false
false
false
+ 0
11866
@@ -94934,6 +106800,7 @@
false
false
false
+ 0
11867
@@ -94942,6 +106809,7 @@
false
false
false
+ 0
11868
@@ -94950,6 +106818,7 @@
false
false
false
+ 0
11869
@@ -94958,6 +106827,7 @@
false
false
false
+ 0
11870
@@ -94966,6 +106836,7 @@
false
false
false
+ 0
11871
@@ -94974,6 +106845,7 @@
false
false
false
+ 0
11872
@@ -94982,6 +106854,7 @@
false
false
false
+ 0
11873
@@ -94990,6 +106863,7 @@
false
false
false
+ 0
11874
@@ -94998,6 +106872,7 @@
false
false
false
+ 0
11875
@@ -95006,6 +106881,7 @@
false
false
false
+ 0
11876
@@ -95014,6 +106890,7 @@
false
false
false
+ 0
11877
@@ -95022,6 +106899,7 @@
false
false
false
+ 0
11878
@@ -95030,6 +106908,7 @@
false
false
false
+ 0
11879
@@ -95038,6 +106917,7 @@
false
false
false
+ 0
11880
@@ -95046,6 +106926,7 @@
false
false
false
+ 0
11881
@@ -95054,6 +106935,7 @@
false
false
false
+ 0
11882
@@ -95062,6 +106944,7 @@
false
false
false
+ 0
11883
@@ -95070,6 +106953,7 @@
false
false
false
+ 0
11884
@@ -95078,6 +106962,7 @@
false
false
false
+ 0
11885
@@ -95086,6 +106971,7 @@
false
false
false
+ 0
11886
@@ -95094,6 +106980,7 @@
false
false
false
+ 0
11887
@@ -95102,6 +106989,7 @@
false
false
false
+ 0
11888
@@ -95110,6 +106998,7 @@
false
false
false
+ 0
11889
@@ -95118,6 +107007,7 @@
false
false
false
+ 0
11890
@@ -95126,6 +107016,7 @@
false
false
false
+ 0
11891
@@ -95134,6 +107025,7 @@
false
false
false
+ 0
11892
@@ -95142,6 +107034,7 @@
false
false
false
+ 0
11893
@@ -95150,6 +107043,7 @@
false
false
false
+ 0
11894
@@ -95158,6 +107052,7 @@
false
false
false
+ 0
11895
@@ -95166,6 +107061,7 @@
false
false
false
+ 0
11896
@@ -95174,6 +107070,7 @@
false
false
false
+ 0
11897
@@ -95182,6 +107079,7 @@
false
false
false
+ 0
11898
@@ -95190,6 +107088,7 @@
false
false
false
+ 0
11899
@@ -95198,6 +107097,7 @@
false
false
false
+ 0
11900
@@ -95206,6 +107106,7 @@
false
false
false
+ 0
11901
@@ -95214,6 +107115,7 @@
false
false
false
+ 0
11902
@@ -95222,6 +107124,7 @@
false
false
false
+ 0
11903
@@ -95230,6 +107133,7 @@
false
false
false
+ 0
11904
@@ -95238,6 +107142,7 @@
false
false
false
+ 0
11905
@@ -95246,6 +107151,7 @@
false
false
false
+ 0
11906
@@ -95254,6 +107160,7 @@
false
false
false
+ 0
11907
@@ -95262,6 +107169,7 @@
false
false
false
+ 0
11908
@@ -95270,6 +107178,7 @@
false
false
false
+ 0
11909
@@ -95278,6 +107187,7 @@
false
false
false
+ 0
11910
@@ -95286,6 +107196,7 @@
false
false
false
+ 0
11911
@@ -95294,6 +107205,7 @@
false
false
false
+ 0
11912
@@ -95302,6 +107214,7 @@
false
false
false
+ 0
11913
@@ -95310,6 +107223,7 @@
false
false
false
+ 0
11914
@@ -95318,6 +107232,7 @@
false
false
false
+ 0
11915
@@ -95326,6 +107241,7 @@
false
false
false
+ 0
11916
@@ -95334,6 +107250,7 @@
false
false
false
+ 0
11917
@@ -95342,6 +107259,7 @@
false
false
false
+ 0
11918
@@ -95350,6 +107268,7 @@
false
false
false
+ 0
11919
@@ -95358,6 +107277,7 @@
false
false
false
+ 0
11920
@@ -95366,6 +107286,7 @@
false
false
false
+ 0
11921
@@ -95374,6 +107295,7 @@
false
false
false
+ 0
11922
@@ -95382,6 +107304,7 @@
false
false
false
+ 0
11923
@@ -95390,6 +107313,7 @@
false
false
false
+ 0
11924
@@ -95398,6 +107322,7 @@
false
false
false
+ 0
11925
@@ -95406,6 +107331,7 @@
false
false
false
+ 0
11926
@@ -95414,6 +107340,7 @@
false
false
false
+ 0
11927
@@ -95422,6 +107349,7 @@
false
false
false
+ 0
11928
@@ -95430,6 +107358,7 @@
false
false
false
+ 0
11929
@@ -95438,6 +107367,7 @@
false
false
false
+ 0
11930
@@ -95446,6 +107376,7 @@
false
false
false
+ 0
11931
@@ -95454,6 +107385,7 @@
false
false
false
+ 0
11932
@@ -95462,6 +107394,7 @@
false
false
false
+ 0
11933
@@ -95470,6 +107403,7 @@
false
false
false
+ 0
11934
@@ -95478,6 +107412,7 @@
false
false
false
+ 0
11935
@@ -95486,6 +107421,7 @@
false
false
false
+ 0
11936
@@ -95494,6 +107430,7 @@
false
false
false
+ 0
11937
@@ -95502,6 +107439,7 @@
false
false
false
+ 0
11938
@@ -95510,6 +107448,7 @@
false
false
false
+ 0
11939
@@ -95518,6 +107457,7 @@
false
false
false
+ 0
11940
@@ -95526,6 +107466,7 @@
false
false
false
+ 0
11941
@@ -95534,6 +107475,7 @@
false
false
false
+ 0
11942
@@ -95542,6 +107484,7 @@
false
false
false
+ 0
11943
@@ -95550,6 +107493,7 @@
false
false
false
+ 0
11944
@@ -95558,6 +107502,7 @@
false
false
false
+ 0
11945
@@ -95566,6 +107511,7 @@
false
false
false
+ 0
11946
@@ -95574,6 +107520,7 @@
false
false
false
+ 0
11947
@@ -95582,6 +107529,7 @@
false
false
false
+ 0
11948
@@ -95590,6 +107538,7 @@
false
false
false
+ 0
11949
@@ -95598,6 +107547,7 @@
false
false
false
+ 0
11950
@@ -95606,6 +107556,7 @@
false
false
false
+ 0
11951
@@ -95614,6 +107565,7 @@
false
false
false
+ 0
11952
@@ -95622,6 +107574,7 @@
false
false
false
+ 0
11953
@@ -95630,6 +107583,7 @@
false
false
false
+ 0
11954
@@ -95638,6 +107592,7 @@
false
false
false
+ 0
11955
@@ -95646,6 +107601,7 @@
false
false
false
+ 0
11956
@@ -95654,6 +107610,7 @@
false
false
false
+ 0
11957
@@ -95662,6 +107619,7 @@
false
false
false
+ 0
11958
@@ -95670,6 +107628,7 @@
false
false
false
+ 0
11959
@@ -95678,6 +107637,7 @@
false
false
false
+ 0
11960
@@ -95686,6 +107646,7 @@
false
false
false
+ 0
11961
@@ -95694,6 +107655,7 @@
false
false
false
+ 0
11962
@@ -95702,6 +107664,7 @@
false
false
false
+ 0
11963
@@ -95710,6 +107673,7 @@
false
false
false
+ 0
11964
@@ -95718,6 +107682,7 @@
false
false
false
+ 0
11965
@@ -95726,6 +107691,7 @@
false
false
false
+ 0
11966
@@ -95734,6 +107700,7 @@
false
false
false
+ 0
11967
@@ -95742,6 +107709,7 @@
false
false
false
+ 0
11968
@@ -95750,6 +107718,7 @@
false
false
false
+ 0
11969
@@ -95758,6 +107727,7 @@
false
false
false
+ 0
11970
@@ -95766,6 +107736,7 @@
false
false
false
+ 0
11971
@@ -95774,6 +107745,7 @@
false
false
false
+ 0
11972
@@ -95782,6 +107754,7 @@
false
false
false
+ 0
11973
@@ -95790,6 +107763,7 @@
false
false
false
+ 0
11974
@@ -95798,6 +107772,7 @@
false
false
false
+ 0
11975
@@ -95806,6 +107781,7 @@
false
false
false
+ 0
11976
@@ -95814,6 +107790,7 @@
false
false
false
+ 0
11977
@@ -95822,6 +107799,7 @@
false
false
false
+ 0
11978
@@ -95830,6 +107808,7 @@
false
false
false
+ 0
11979
@@ -95838,6 +107817,7 @@
false
false
false
+ 0
11980
@@ -95846,6 +107826,7 @@
false
false
false
+ 0
11981
@@ -95854,6 +107835,7 @@
false
false
false
+ 0
11982
@@ -95862,6 +107844,7 @@
false
false
false
+ 0
11983
@@ -95870,6 +107853,7 @@
false
false
false
+ 0
11984
@@ -95878,6 +107862,7 @@
false
false
false
+ 0
11985
@@ -95886,6 +107871,7 @@
false
false
false
+ 0
11986
@@ -95894,6 +107880,7 @@
false
false
false
+ 0
11987
@@ -95902,6 +107889,7 @@
false
false
false
+ 0
11988
@@ -95910,6 +107898,7 @@
false
false
false
+ 0
11989
@@ -95918,6 +107907,7 @@
false
false
false
+ 0
11990
@@ -95926,6 +107916,7 @@
false
false
false
+ 0
11991
@@ -95934,6 +107925,7 @@
false
false
false
+ 0
11992
@@ -95942,6 +107934,7 @@
false
false
false
+ 0
11993
@@ -95950,6 +107943,7 @@
false
false
false
+ 0
11994
@@ -95958,6 +107952,7 @@
false
false
false
+ 0
11995
@@ -95966,6 +107961,7 @@
false
false
false
+ 0
11996
@@ -95974,6 +107970,7 @@
false
false
false
+ 0
11997
@@ -95982,6 +107979,7 @@
false
false
false
+ 0
11998
@@ -95990,6 +107988,7 @@
false
false
false
+ 0
11999
@@ -95998,6 +107997,7 @@
false
false
false
+ 0
12000
@@ -96006,6 +108006,7 @@
false
false
false
+ 0
12001
@@ -96014,6 +108015,7 @@
false
false
false
+ 0
12002
@@ -96022,6 +108024,7 @@
false
false
false
+ 0
12003
@@ -96030,6 +108033,7 @@
false
false
false
+ 0
12004
@@ -96038,6 +108042,7 @@
false
false
false
+ 0
12005
@@ -96046,6 +108051,7 @@
false
false
false
+ 0
12006
@@ -96054,6 +108060,7 @@
false
false
false
+ 0
12007
@@ -96062,6 +108069,7 @@
false
false
false
+ 0
12008
@@ -96070,6 +108078,7 @@
false
false
false
+ 0
12009
@@ -96078,6 +108087,7 @@
false
false
false
+ 0
12010
@@ -96086,6 +108096,7 @@
false
false
false
+ 0
12011
@@ -96094,6 +108105,7 @@
false
false
false
+ 0
12012
@@ -96102,6 +108114,7 @@
false
false
false
+ 0
12013
@@ -96110,6 +108123,7 @@
false
false
false
+ 0
12014
@@ -96118,6 +108132,7 @@
false
false
false
+ 0
12015
@@ -96126,6 +108141,7 @@
false
false
false
+ 0
12016
@@ -96134,6 +108150,7 @@
false
false
false
+ 0
12017
@@ -96142,6 +108159,7 @@
false
false
false
+ 0
12018
@@ -96150,6 +108168,7 @@
false
false
false
+ 0
12019
@@ -96158,6 +108177,7 @@
false
false
false
+ 0
12020
@@ -96166,6 +108186,7 @@
false
false
false
+ 0
12021
@@ -96174,6 +108195,7 @@
false
false
false
+ 0
12022
@@ -96182,6 +108204,7 @@
false
false
false
+ 0
12023
@@ -96190,6 +108213,7 @@
false
false
false
+ 0
12024
@@ -96198,6 +108222,7 @@
false
false
false
+ 0
12025
@@ -96206,6 +108231,7 @@
false
false
false
+ 0
12026
@@ -96214,6 +108240,7 @@
false
false
false
+ 0
12027
@@ -96222,6 +108249,7 @@
false
false
false
+ 0
12028
@@ -96230,6 +108258,7 @@
false
false
false
+ 0
12029
@@ -96238,6 +108267,7 @@
false
false
false
+ 0
12030
@@ -96246,6 +108276,7 @@
false
false
false
+ 0
12031
@@ -96254,6 +108285,7 @@
false
false
false
+ 0
12032
@@ -96262,6 +108294,7 @@
false
false
false
+ 0
12033
@@ -96270,6 +108303,7 @@
false
false
false
+ 0
12034
@@ -96278,6 +108312,7 @@
false
false
false
+ 0
12035
@@ -96286,6 +108321,7 @@
false
false
false
+ 0
12036
@@ -96294,6 +108330,7 @@
false
false
false
+ 0
12037
@@ -96302,6 +108339,7 @@
false
false
false
+ 0
12038
@@ -96310,6 +108348,7 @@
false
false
false
+ 0
12039
@@ -96318,6 +108357,7 @@
false
false
false
+ 0
12040
@@ -96326,6 +108366,7 @@
false
false
false
+ 0
12041
@@ -96334,6 +108375,7 @@
false
false
false
+ 0
12042
@@ -96342,6 +108384,7 @@
false
false
false
+ 0
12043
@@ -96350,6 +108393,7 @@
false
false
false
+ 0
12044
@@ -96358,6 +108402,7 @@
false
false
false
+ 0
12045
@@ -96366,6 +108411,7 @@
false
false
false
+ 0
12046
@@ -96374,6 +108420,7 @@
false
false
false
+ 0
12047
@@ -96382,6 +108429,7 @@
false
false
false
+ 0
12048
@@ -96390,6 +108438,7 @@
false
false
false
+ 0
12049
@@ -96398,6 +108447,7 @@
false
false
false
+ 0
12050
@@ -96406,6 +108456,7 @@
false
false
false
+ 0
12051
@@ -96414,6 +108465,7 @@
false
false
false
+ 0
12052
@@ -96422,6 +108474,7 @@
false
false
false
+ 0
12053
@@ -96430,6 +108483,7 @@
false
false
false
+ 0
12054
@@ -96438,6 +108492,7 @@
false
false
false
+ 0
12055
@@ -96446,6 +108501,7 @@
false
false
false
+ 0
12056
@@ -96454,6 +108510,7 @@
false
false
false
+ 0
12057
@@ -96462,6 +108519,7 @@
false
false
false
+ 0
12058
@@ -96470,6 +108528,7 @@
false
false
false
+ 0
12059
@@ -96478,6 +108537,7 @@
false
false
false
+ 0
12060
@@ -96486,6 +108546,7 @@
false
false
false
+ 0
12061
@@ -96494,6 +108555,7 @@
false
false
false
+ 0
12062
@@ -96502,6 +108564,7 @@
false
false
false
+ 0
12063
@@ -96510,6 +108573,7 @@
false
false
false
+ 0
12064
@@ -96518,6 +108582,7 @@
false
false
false
+ 0
12065
@@ -96526,6 +108591,7 @@
false
false
false
+ 0
12066
@@ -96534,6 +108600,7 @@
false
false
false
+ 0
12067
@@ -96542,6 +108609,7 @@
false
false
false
+ 0
12068
@@ -96550,6 +108618,7 @@
false
false
false
+ 0
12069
@@ -96558,6 +108627,7 @@
false
false
false
+ 0
12070
@@ -96566,6 +108636,7 @@
false
false
false
+ 0
12071
@@ -96574,6 +108645,7 @@
false
false
false
+ 0
12072
@@ -96582,6 +108654,7 @@
false
false
false
+ 0
12073
@@ -96590,6 +108663,7 @@
false
false
false
+ 0
12074
@@ -96598,6 +108672,7 @@
false
false
false
+ 0
12075
@@ -96606,6 +108681,7 @@
false
false
false
+ 0
12076
@@ -96614,6 +108690,7 @@
false
false
false
+ 0
12077
@@ -96622,6 +108699,7 @@
false
false
false
+ 0
12078
@@ -96630,6 +108708,7 @@
false
false
false
+ 0
12079
@@ -96638,6 +108717,7 @@
false
false
false
+ 0
12080
@@ -96646,6 +108726,7 @@
false
false
false
+ 0
12081
@@ -96654,6 +108735,7 @@
false
false
false
+ 0
12082
@@ -96662,6 +108744,7 @@
false
false
false
+ 0
12083
@@ -96670,6 +108753,7 @@
false
false
false
+ 0
12084
@@ -96678,6 +108762,7 @@
false
false
false
+ 0
12085
@@ -96686,6 +108771,7 @@
false
false
false
+ 0
12086
@@ -96694,6 +108780,7 @@
false
false
false
+ 0
12087
@@ -96702,6 +108789,7 @@
false
false
false
+ 0
12088
@@ -96710,6 +108798,7 @@
false
false
false
+ 0
12089
@@ -96718,6 +108807,7 @@
false
false
false
+ 0
12090
@@ -96726,6 +108816,7 @@
false
false
false
+ 0
12091
@@ -96734,6 +108825,7 @@
false
false
false
+ 0
12092
@@ -96742,6 +108834,7 @@
false
false
false
+ 0
12093
@@ -96750,6 +108843,7 @@
false
false
false
+ 0
12094
@@ -96758,6 +108852,7 @@
false
false
false
+ 0
12095
@@ -96766,6 +108861,7 @@
false
false
false
+ 0
12096
@@ -96774,6 +108870,7 @@
false
false
false
+ 0
12097
@@ -96782,6 +108879,7 @@
false
false
false
+ 0
12098
@@ -96790,6 +108888,7 @@
false
false
false
+ 0
12099
@@ -96798,6 +108897,7 @@
false
false
false
+ 0
12100
@@ -96806,6 +108906,7 @@
false
false
false
+ 0
12101
@@ -96814,6 +108915,7 @@
false
false
false
+ 0
12102
@@ -96822,6 +108924,7 @@
false
false
false
+ 0
12103
@@ -96830,6 +108933,7 @@
false
false
false
+ 0
12104
@@ -96838,6 +108942,7 @@
false
false
false
+ 0
12105
@@ -96846,6 +108951,7 @@
false
false
false
+ 0
12106
@@ -96854,6 +108960,7 @@
false
false
false
+ 0
12107
@@ -96862,6 +108969,7 @@
false
false
false
+ 0
12108
@@ -96870,6 +108978,7 @@
false
false
false
+ 0
12109
@@ -96878,6 +108987,7 @@
false
false
false
+ 0
12110
@@ -96886,6 +108996,7 @@
false
false
false
+ 0
12111
@@ -96894,6 +109005,7 @@
false
false
false
+ 0
12112
@@ -96902,6 +109014,7 @@
false
false
false
+ 0
12113
@@ -96910,6 +109023,7 @@
false
false
false
+ 0
12114
@@ -96918,6 +109032,7 @@
false
false
false
+ 0
12115
@@ -96926,6 +109041,7 @@
false
false
false
+ 0
12116
@@ -96934,6 +109050,7 @@
false
false
false
+ 0
12117
@@ -96942,6 +109059,7 @@
false
false
false
+ 0
12118
@@ -96950,6 +109068,7 @@
false
false
false
+ 0
12119
@@ -96958,6 +109077,7 @@
false
false
false
+ 0
12120
@@ -96966,6 +109086,7 @@
false
false
false
+ 0
12121
@@ -96974,6 +109095,7 @@
false
false
false
+ 0
12122
@@ -96982,6 +109104,7 @@
false
false
false
+ 0
12123
@@ -96990,6 +109113,7 @@
false
false
false
+ 0
12124
@@ -96998,6 +109122,7 @@
false
false
false
+ 0
12125
@@ -97006,6 +109131,7 @@
false
false
false
+ 0
12126
@@ -97014,6 +109140,7 @@
false
false
false
+ 0
12127
@@ -97022,6 +109149,7 @@
false
false
false
+ 0
12128
@@ -97030,6 +109158,7 @@
false
false
false
+ 0
12129
@@ -97038,6 +109167,7 @@
false
false
false
+ 0
12130
@@ -97046,6 +109176,7 @@
false
false
false
+ 0
12131
@@ -97054,6 +109185,7 @@
false
false
false
+ 0
12132
@@ -97062,6 +109194,7 @@
false
false
false
+ 0
12133
@@ -97070,6 +109203,7 @@
false
false
false
+ 0
12134
@@ -97078,6 +109212,7 @@
false
false
false
+ 0
12135
@@ -97086,6 +109221,7 @@
false
false
false
+ 0
12136
@@ -97094,6 +109230,7 @@
false
false
false
+ 0
12137
@@ -97102,6 +109239,7 @@
false
false
false
+ 0
12138
@@ -97110,6 +109248,7 @@
false
false
false
+ 0
12139
@@ -97118,6 +109257,7 @@
false
false
false
+ 0
12140
@@ -97126,6 +109266,7 @@
false
false
false
+ 0
12141
@@ -97134,6 +109275,7 @@
false
false
false
+ 0
12142
@@ -97142,6 +109284,7 @@
false
false
false
+ 0
12143
@@ -97150,6 +109293,7 @@
false
false
false
+ 0
12144
@@ -97158,6 +109302,7 @@
false
false
false
+ 0
12145
@@ -97166,6 +109311,7 @@
false
false
false
+ 0
12146
@@ -97174,6 +109320,7 @@
false
false
false
+ 0
12147
@@ -97182,6 +109329,7 @@
false
false
false
+ 0
12148
@@ -97190,6 +109338,7 @@
false
false
false
+ 0
12149
@@ -97198,6 +109347,7 @@
false
false
false
+ 0
12150
@@ -97206,6 +109356,7 @@
false
false
false
+ 0
12151
@@ -97214,6 +109365,7 @@
false
false
false
+ 0
12152
@@ -97222,6 +109374,7 @@
false
false
false
+ 0
12153
@@ -97230,6 +109383,7 @@
false
false
false
+ 0
12154
@@ -97238,6 +109392,7 @@
false
false
false
+ 0
12155
@@ -97246,6 +109401,7 @@
false
false
false
+ 0
12156
@@ -97254,6 +109410,7 @@
false
false
false
+ 0
12157
@@ -97262,6 +109419,7 @@
false
false
false
+ 0
12158
@@ -97270,6 +109428,7 @@
false
false
false
+ 0
12159
@@ -97278,6 +109437,7 @@
false
false
false
+ 0
12160
@@ -97286,6 +109446,7 @@
false
false
false
+ 0
12161
@@ -97294,6 +109455,7 @@
false
false
false
+ 0
12162
@@ -97302,6 +109464,7 @@
false
false
false
+ 0
12163
@@ -97310,6 +109473,7 @@
false
false
false
+ 0
12164
@@ -97318,6 +109482,7 @@
false
false
false
+ 0
12165
@@ -97326,6 +109491,7 @@
false
false
false
+ 0
12166
@@ -97334,6 +109500,7 @@
false
false
false
+ 0
12167
@@ -97342,6 +109509,7 @@
false
false
false
+ 0
12168
@@ -97350,6 +109518,7 @@
false
false
false
+ 0
12169
@@ -97358,6 +109527,7 @@
false
false
false
+ 0
12170
@@ -97366,6 +109536,7 @@
false
false
false
+ 0
12171
@@ -97374,6 +109545,7 @@
false
false
false
+ 0
12172
@@ -97382,6 +109554,7 @@
false
false
false
+ 0
12173
@@ -97390,6 +109563,7 @@
false
false
false
+ 0
12174
@@ -97398,6 +109572,7 @@
false
false
false
+ 0
12175
@@ -97406,6 +109581,7 @@
false
false
false
+ 0
12176
@@ -97414,6 +109590,7 @@
false
false
false
+ 0
12177
@@ -97422,6 +109599,7 @@
false
false
false
+ 0
12178
@@ -97430,6 +109608,7 @@
false
false
false
+ 0
12179
@@ -97438,6 +109617,7 @@
false
false
false
+ 0
12180
@@ -97446,6 +109626,7 @@
false
false
false
+ 0
12181
@@ -97454,6 +109635,7 @@
false
false
false
+ 0
12182
@@ -97462,6 +109644,7 @@
false
false
false
+ 0
12183
@@ -97470,6 +109653,7 @@
false
false
false
+ 0
12184
@@ -97478,6 +109662,7 @@
false
false
false
+ 0
12185
@@ -97486,6 +109671,7 @@
false
false
false
+ 0
12186
@@ -97494,6 +109680,7 @@
false
false
false
+ 0
12187
@@ -97502,6 +109689,7 @@
false
false
false
+ 0
12188
@@ -97510,6 +109698,7 @@
false
false
false
+ 0
12189
@@ -97518,6 +109707,7 @@
false
false
false
+ 0
12190
@@ -97526,6 +109716,7 @@
false
false
false
+ 0
12191
@@ -97534,6 +109725,7 @@
false
false
false
+ 0
12192
@@ -97542,6 +109734,7 @@
false
false
false
+ 0
12193
@@ -97550,6 +109743,7 @@
false
false
false
+ 0
12194
@@ -97558,6 +109752,7 @@
false
false
false
+ 0
12195
@@ -97566,6 +109761,7 @@
false
false
false
+ 0
12196
@@ -97574,6 +109770,7 @@
false
false
false
+ 0
12197
@@ -97582,6 +109779,7 @@
false
false
false
+ 0
12198
@@ -97590,6 +109788,7 @@
false
false
false
+ 0
12199
@@ -97598,6 +109797,7 @@
false
false
false
+ 0
12200
@@ -97606,6 +109806,7 @@
false
false
false
+ 0
12201
@@ -97614,6 +109815,7 @@
false
false
false
+ 0
12202
@@ -97622,6 +109824,7 @@
false
false
false
+ 0
12203
@@ -97630,6 +109833,7 @@
false
false
false
+ 0
12204
@@ -97638,6 +109842,7 @@
false
false
false
+ 0
12205
@@ -97646,6 +109851,7 @@
false
false
false
+ 0
12206
@@ -97654,6 +109860,7 @@
false
false
false
+ 0
12207
@@ -97662,6 +109869,7 @@
false
false
false
+ 0
12208
@@ -97670,6 +109878,7 @@
false
false
false
+ 0
12209
@@ -97678,6 +109887,7 @@
false
false
false
+ 0
12210
@@ -97686,6 +109896,7 @@
false
false
false
+ 0
12211
@@ -97694,6 +109905,7 @@
false
false
false
+ 0
12212
@@ -97702,6 +109914,7 @@
false
false
false
+ 0
12213
@@ -97710,6 +109923,7 @@
false
false
false
+ 0
12214
@@ -97718,6 +109932,7 @@
false
false
false
+ 0
12215
@@ -97726,6 +109941,7 @@
false
false
false
+ 0
12216
@@ -97734,6 +109950,7 @@
false
false
false
+ 0
12217
@@ -97742,6 +109959,7 @@
false
false
false
+ 0
12218
@@ -97750,6 +109968,7 @@
false
false
false
+ 0
12219
@@ -97758,6 +109977,7 @@
false
false
false
+ 0
12220
@@ -97766,6 +109986,7 @@
false
false
false
+ 0
12221
@@ -97774,6 +109995,7 @@
false
false
false
+ 0
12222
@@ -97782,6 +110004,7 @@
false
false
false
+ 0
12223
@@ -97790,6 +110013,7 @@
false
false
false
+ 0
12224
@@ -97798,6 +110022,7 @@
false
false
false
+ 0
12225
@@ -97806,6 +110031,7 @@
false
false
false
+ 0
12226
@@ -97814,6 +110040,7 @@
false
false
false
+ 0
12227
@@ -97822,6 +110049,7 @@
false
false
false
+ 0
12228
@@ -97830,6 +110058,7 @@
false
false
false
+ 0
12229
@@ -97838,6 +110067,7 @@
false
false
false
+ 0
12230
@@ -97846,6 +110076,7 @@
false
false
false
+ 0
12231
@@ -97854,6 +110085,7 @@
false
false
false
+ 0
12232
@@ -97862,6 +110094,7 @@
false
false
false
+ 0
12233
@@ -97870,6 +110103,7 @@
false
false
false
+ 0
12234
@@ -97878,6 +110112,7 @@
false
false
false
+ 0
12235
@@ -97886,6 +110121,7 @@
false
false
false
+ 0
12236
@@ -97894,6 +110130,7 @@
false
false
false
+ 0
12237
@@ -97902,6 +110139,7 @@
false
false
false
+ 0
12238
@@ -97910,6 +110148,7 @@
false
false
false
+ 0
12239
@@ -97918,6 +110157,7 @@
false
false
false
+ 0
12240
@@ -97926,6 +110166,7 @@
false
false
false
+ 0
12241
@@ -97934,6 +110175,7 @@
false
false
false
+ 0
12242
@@ -97942,6 +110184,7 @@
false
false
false
+ 0
12243
@@ -97950,6 +110193,7 @@
false
false
false
+ 0
12244
@@ -97958,6 +110202,7 @@
false
false
false
+ 0
12245
@@ -97966,6 +110211,7 @@
false
false
false
+ 0
12246
@@ -97974,6 +110220,7 @@
false
false
false
+ 0
12247
@@ -97982,6 +110229,7 @@
false
false
false
+ 0
12248
@@ -97990,6 +110238,7 @@
false
false
false
+ 0
12249
@@ -97998,6 +110247,7 @@
false
false
false
+ 0
12250
@@ -98006,6 +110256,7 @@
false
false
false
+ 0
12251
@@ -98014,6 +110265,7 @@
false
false
false
+ 0
12252
@@ -98022,6 +110274,7 @@
false
false
false
+ 0
12253
@@ -98030,6 +110283,7 @@
false
false
false
+ 0
12254
@@ -98038,6 +110292,7 @@
false
false
false
+ 0
12255
@@ -98046,6 +110301,7 @@
false
false
false
+ 0
12256
@@ -98054,6 +110310,7 @@
false
false
false
+ 0
12257
@@ -98062,6 +110319,7 @@
false
false
false
+ 0
12258
@@ -98070,6 +110328,7 @@
false
false
false
+ 0
12259
@@ -98078,6 +110337,7 @@
false
false
false
+ 0
12260
@@ -98086,6 +110346,7 @@
false
false
false
+ 0
12261
@@ -98094,6 +110355,7 @@
false
false
false
+ 0
12262
@@ -98102,6 +110364,7 @@
false
false
false
+ 0
12263
@@ -98110,6 +110373,7 @@
false
false
false
+ 0
12264
@@ -98118,6 +110382,7 @@
false
false
false
+ 0
12265
@@ -98126,6 +110391,7 @@
false
false
false
+ 0
12266
@@ -98134,6 +110400,7 @@
false
false
false
+ 0
12267
@@ -98142,6 +110409,7 @@
false
false
false
+ 0
12268
@@ -98150,6 +110418,7 @@
false
false
false
+ 0
12269
@@ -98158,6 +110427,7 @@
false
false
false
+ 0
12270
@@ -98166,6 +110436,7 @@
false
false
false
+ 0
12271
@@ -98174,6 +110445,7 @@
false
false
false
+ 0
12272
@@ -98182,6 +110454,7 @@
false
false
false
+ 0
12273
@@ -98190,6 +110463,7 @@
false
false
false
+ 0
12274
@@ -98198,6 +110472,7 @@
false
false
false
+ 0
12275
@@ -98206,6 +110481,7 @@
false
false
false
+ 0
12276
@@ -98214,6 +110490,7 @@
false
false
false
+ 0
12277
@@ -98222,6 +110499,7 @@
false
false
false
+ 0
12278
@@ -98230,6 +110508,7 @@
false
false
false
+ 0
12279
@@ -98238,6 +110517,7 @@
false
false
false
+ 0
12280
@@ -98246,6 +110526,7 @@
false
false
false
+ 0
12281
@@ -98254,6 +110535,7 @@
false
false
false
+ 0
12282
@@ -98262,6 +110544,7 @@
false
false
false
+ 0
12283
@@ -98270,6 +110553,7 @@
false
false
false
+ 0
12284
@@ -98278,6 +110562,7 @@
false
false
false
+ 0
12285
@@ -98286,6 +110571,7 @@
false
false
false
+ 0
12286
@@ -98294,6 +110580,7 @@
false
false
false
+ 0
12287
@@ -98302,6 +110589,7 @@
false
false
false
+ 0
12288
@@ -98310,6 +110598,7 @@
false
false
false
+ 0
12289
@@ -98318,6 +110607,7 @@
false
false
false
+ 0
12290
@@ -98326,6 +110616,7 @@
false
false
false
+ 0
12291
@@ -98334,6 +110625,7 @@
false
false
false
+ 0
12292
@@ -98342,6 +110634,7 @@
false
false
false
+ 0
12293
@@ -98350,6 +110643,7 @@
false
false
false
+ 0
12294
@@ -98358,6 +110652,7 @@
false
false
false
+ 0
12295
@@ -98366,6 +110661,7 @@
false
false
false
+ 0
12296
@@ -98374,6 +110670,7 @@
false
false
false
+ 0
12297
@@ -98382,6 +110679,7 @@
false
false
false
+ 0
12298
@@ -98390,6 +110688,7 @@
false
false
false
+ 0
12299
@@ -98398,6 +110697,7 @@
false
false
false
+ 0
12300
@@ -98406,6 +110706,7 @@
false
false
false
+ 0
12301
@@ -98414,6 +110715,7 @@
false
false
false
+ 0
12302
@@ -98422,6 +110724,7 @@
false
false
false
+ 0
12303
@@ -98430,6 +110733,7 @@
false
false
false
+ 0
12304
@@ -98438,6 +110742,7 @@
false
false
false
+ 0
12305
@@ -98446,6 +110751,7 @@
false
false
false
+ 0
12306
@@ -98454,6 +110760,7 @@
false
false
false
+ 0
12307
@@ -98462,6 +110769,7 @@
false
false
false
+ 0
12308
@@ -98470,6 +110778,7 @@
false
false
false
+ 0
12309
@@ -98478,6 +110787,7 @@
false
false
false
+ 0
12310
@@ -98486,6 +110796,7 @@
false
false
false
+ 0
12311
@@ -98494,6 +110805,7 @@
false
false
false
+ 0
12312
@@ -98502,6 +110814,7 @@
false
false
false
+ 0
12313
@@ -98510,6 +110823,7 @@
false
false
false
+ 0
12314
@@ -98518,6 +110832,7 @@
false
false
false
+ 0
12315
@@ -98526,6 +110841,7 @@
false
false
false
+ 0
12316
@@ -98534,6 +110850,7 @@
false
false
false
+ 0
12317
@@ -98542,6 +110859,7 @@
false
false
false
+ 0
12318
@@ -98550,6 +110868,7 @@
false
false
false
+ 0
12319
@@ -98558,6 +110877,7 @@
false
false
false
+ 0
12320
@@ -98566,6 +110886,7 @@
false
false
false
+ 0
12321
@@ -98574,6 +110895,7 @@
false
false
false
+ 0
12322
@@ -98582,6 +110904,7 @@
false
false
false
+ 0
12323
@@ -98590,6 +110913,7 @@
false
false
false
+ 0
12324
@@ -98598,6 +110922,7 @@
false
false
false
+ 0
12325
@@ -98606,6 +110931,7 @@
false
false
false
+ 0
12326
@@ -98614,6 +110940,7 @@
false
false
false
+ 0
12327
@@ -98622,6 +110949,7 @@
false
false
false
+ 0
12328
@@ -98630,6 +110958,7 @@
false
false
false
+ 0
12329
@@ -98638,6 +110967,7 @@
false
false
false
+ 0
12330
@@ -98646,6 +110976,7 @@
false
false
false
+ 0
12331
@@ -98654,6 +110985,7 @@
false
false
false
+ 0
12332
@@ -98662,6 +110994,7 @@
false
false
false
+ 0
12333
@@ -98670,6 +111003,7 @@
false
false
false
+ 0
12334
@@ -98678,6 +111012,7 @@
false
false
false
+ 0
12335
@@ -98686,6 +111021,7 @@
false
false
false
+ 0
12336
@@ -98694,6 +111030,7 @@
false
false
false
+ 0
12337
@@ -98702,6 +111039,7 @@
false
false
false
+ 0
12338
@@ -98710,6 +111048,7 @@
false
false
false
+ 0
12339
@@ -98718,6 +111057,7 @@
false
false
false
+ 0
12340
@@ -98726,6 +111066,7 @@
false
false
false
+ 0
12341
@@ -98734,6 +111075,7 @@
false
false
false
+ 0
12342
@@ -98742,6 +111084,7 @@
false
false
false
+ 0
12343
@@ -98750,6 +111093,7 @@
false
false
false
+ 0
12344
@@ -98758,6 +111102,7 @@
false
false
false
+ 0
12345
@@ -98766,6 +111111,7 @@
false
false
false
+ 0
12346
@@ -98774,6 +111120,7 @@
false
false
false
+ 0
12347
@@ -98782,6 +111129,7 @@
false
false
false
+ 0
12348
@@ -98790,6 +111138,7 @@
false
false
false
+ 0
12349
@@ -98798,6 +111147,7 @@
false
false
false
+ 0
12350
@@ -98806,6 +111156,7 @@
false
false
false
+ 0
12351
@@ -98814,6 +111165,7 @@
false
false
false
+ 0
12352
@@ -98822,6 +111174,7 @@
false
false
false
+ 0
12353
@@ -98830,6 +111183,7 @@
false
false
false
+ 0
12354
@@ -98838,6 +111192,7 @@
false
false
false
+ 0
12355
@@ -98846,6 +111201,7 @@
false
false
false
+ 0
12356
@@ -98854,6 +111210,7 @@
false
false
false
+ 0
12357
@@ -98862,6 +111219,7 @@
false
false
false
+ 0
12358
@@ -98870,6 +111228,7 @@
false
false
false
+ 0
12359
@@ -98878,6 +111237,7 @@
false
false
false
+ 0
12360
@@ -98886,6 +111246,7 @@
false
false
false
+ 0
12361
@@ -98894,6 +111255,7 @@
false
false
false
+ 0
12362
@@ -98902,6 +111264,7 @@
false
false
false
+ 0
12363
@@ -98910,6 +111273,7 @@
false
false
false
+ 0
12364
@@ -98918,6 +111282,7 @@
false
false
false
+ 0
12365
@@ -98926,6 +111291,7 @@
false
false
false
+ 0
12366
@@ -98934,6 +111300,7 @@
false
false
false
+ 0
12367
@@ -98942,6 +111309,7 @@
false
false
false
+ 0
12368
@@ -98950,6 +111318,7 @@
false
false
false
+ 0
12369
@@ -98958,6 +111327,7 @@
false
false
false
+ 0
12370
@@ -98966,6 +111336,7 @@
false
false
false
+ 0
12371
@@ -98974,6 +111345,7 @@
false
false
false
+ 0
12372
@@ -98982,6 +111354,7 @@
false
false
false
+ 0
12373
@@ -98990,6 +111363,7 @@
false
false
false
+ 0
12374
@@ -98998,6 +111372,7 @@
false
false
false
+ 0
12375
@@ -99006,6 +111381,7 @@
false
false
false
+ 0
12376
@@ -99014,6 +111390,7 @@
false
false
false
+ 0
12377
@@ -99022,6 +111399,7 @@
false
false
false
+ 0
12378
@@ -99030,6 +111408,7 @@
false
false
false
+ 0
12379
@@ -99038,6 +111417,7 @@
false
false
false
+ 0
12380
@@ -99046,6 +111426,7 @@
false
false
false
+ 0
12381
@@ -99054,6 +111435,7 @@
false
false
false
+ 0
12382
@@ -99062,6 +111444,7 @@
false
false
false
+ 0
12383
@@ -99070,6 +111453,7 @@
false
false
false
+ 0
12384
@@ -99078,6 +111462,7 @@
false
false
false
+ 0
12385
@@ -99086,6 +111471,7 @@
false
false
false
+ 0
12386
@@ -99094,6 +111480,7 @@
false
false
false
+ 0
12387
@@ -99102,6 +111489,7 @@
false
false
false
+ 0
12388
@@ -99110,6 +111498,7 @@
false
false
false
+ 0
12389
@@ -99118,6 +111507,7 @@
false
false
false
+ 0
12390
@@ -99126,6 +111516,7 @@
false
false
false
+ 0
12391
@@ -99134,6 +111525,7 @@
false
false
false
+ 0
12392
@@ -99142,6 +111534,7 @@
false
false
false
+ 0
12393
@@ -99150,6 +111543,7 @@
false
false
false
+ 0
12394
@@ -99158,6 +111552,7 @@
false
false
false
+ 0
12395
@@ -99166,6 +111561,7 @@
false
false
false
+ 0
12396
@@ -99174,6 +111570,7 @@
false
false
false
+ 0
12397
@@ -99182,6 +111579,7 @@
false
false
false
+ 0
12398
@@ -99190,6 +111588,7 @@
false
false
false
+ 0
12399
@@ -99198,6 +111597,7 @@
false
false
false
+ 0
12400
@@ -99206,6 +111606,7 @@
false
false
false
+ 0
12401
@@ -99214,6 +111615,7 @@
false
false
false
+ 0
12402
@@ -99222,6 +111624,7 @@
false
false
false
+ 0
12403
@@ -99230,6 +111633,7 @@
false
false
false
+ 0
12404
@@ -99238,6 +111642,7 @@
false
false
false
+ 0
12405
@@ -99246,6 +111651,7 @@
false
false
false
+ 0
12406
@@ -99254,6 +111660,7 @@
false
false
false
+ 0
12407
@@ -99262,6 +111669,7 @@
false
false
false
+ 0
12408
@@ -99270,6 +111678,7 @@
false
false
false
+ 0
12409
@@ -99278,6 +111687,7 @@
false
false
false
+ 0
12410
@@ -99286,6 +111696,7 @@
false
false
false
+ 0
12411
@@ -99294,6 +111705,7 @@
false
false
false
+ 0
12412
@@ -99302,6 +111714,7 @@
false
false
false
+ 0
12413
@@ -99310,6 +111723,7 @@
false
false
false
+ 0
12414
@@ -99318,6 +111732,7 @@
false
false
false
+ 0
12415
@@ -99326,6 +111741,7 @@
false
false
false
+ 0
12416
@@ -99334,6 +111750,7 @@
false
false
false
+ 0
12417
@@ -99342,6 +111759,7 @@
false
false
false
+ 0
12418
@@ -99350,6 +111768,7 @@
false
false
false
+ 0
12419
@@ -99358,6 +111777,7 @@
false
false
false
+ 0
12420
@@ -99366,6 +111786,7 @@
false
false
false
+ 0
12421
@@ -99374,6 +111795,7 @@
false
false
false
+ 0
12422
@@ -99382,6 +111804,7 @@
false
false
false
+ 0
12423
@@ -99390,6 +111813,7 @@
false
false
false
+ 0
12424
@@ -99398,6 +111822,7 @@
false
false
false
+ 0
12425
@@ -99406,6 +111831,7 @@
false
false
false
+ 0
12426
@@ -99414,6 +111840,7 @@
false
false
false
+ 0
12427
@@ -99422,6 +111849,7 @@
false
false
false
+ 0
12428
@@ -99430,6 +111858,7 @@
false
false
false
+ 0
12429
@@ -99438,6 +111867,7 @@
false
false
false
+ 0
12430
@@ -99446,6 +111876,7 @@
false
false
false
+ 0
12431
@@ -99454,6 +111885,7 @@
false
false
false
+ 0
12432
@@ -99462,6 +111894,7 @@
false
false
false
+ 0
12433
@@ -99470,6 +111903,7 @@
false
false
false
+ 0
12434
@@ -99478,6 +111912,7 @@
false
false
false
+ 0
12435
@@ -99486,6 +111921,7 @@
false
false
false
+ 0
12436
@@ -99494,6 +111930,7 @@
false
false
false
+ 0
12437
@@ -99502,6 +111939,7 @@
false
false
false
+ 0
12438
@@ -99510,6 +111948,7 @@
false
false
false
+ 0
12439
@@ -99518,6 +111957,7 @@
false
false
false
+ 0
12440
@@ -99526,6 +111966,7 @@
false
false
false
+ 0
12441
@@ -99534,6 +111975,7 @@
false
false
false
+ 0
12442
@@ -99542,6 +111984,7 @@
false
false
false
+ 0
12443
@@ -99550,6 +111993,7 @@
false
false
false
+ 0
12444
@@ -99558,6 +112002,7 @@
false
false
false
+ 0
12445
@@ -99566,6 +112011,7 @@
false
false
false
+ 0
12446
@@ -99574,6 +112020,7 @@
false
false
false
+ 0
12447
@@ -99582,6 +112029,7 @@
false
false
false
+ 0
12448
@@ -99590,6 +112038,7 @@
false
false
false
+ 0
12449
@@ -99598,6 +112047,7 @@
false
false
false
+ 0
12450
@@ -99606,6 +112056,7 @@
false
false
false
+ 0
12451
@@ -99614,6 +112065,7 @@
false
false
false
+ 0
12452
@@ -99622,6 +112074,7 @@
false
false
false
+ 0
12453
@@ -99630,6 +112083,7 @@
false
false
false
+ 0
12454
@@ -99638,6 +112092,7 @@
false
false
false
+ 0
12455
@@ -99646,6 +112101,7 @@
false
false
false
+ 0
12456
@@ -99654,6 +112110,7 @@
false
false
false
+ 0
12457
@@ -99662,6 +112119,7 @@
false
false
false
+ 0
12458
@@ -99670,6 +112128,7 @@
false
false
false
+ 0
12459
@@ -99678,6 +112137,7 @@
false
false
false
+ 0
12460
@@ -99686,6 +112146,7 @@
false
false
false
+ 0
12461
@@ -99694,6 +112155,7 @@
false
false
false
+ 0
12462
@@ -99702,6 +112164,7 @@
false
false
false
+ 0
12463
@@ -99710,6 +112173,7 @@
false
false
false
+ 0
12464
@@ -99718,6 +112182,7 @@
false
false
false
+ 0
12465
@@ -99726,6 +112191,7 @@
false
false
false
+ 0
12466
@@ -99734,6 +112200,7 @@
false
false
false
+ 0
12467
@@ -99742,6 +112209,7 @@
false
false
false
+ 0
12468
@@ -99750,6 +112218,7 @@
false
false
false
+ 0
12469
@@ -99758,6 +112227,7 @@
false
false
false
+ 0
12470
@@ -99766,6 +112236,7 @@
false
false
false
+ 0
12471
@@ -99774,6 +112245,7 @@
false
false
false
+ 0
12472
@@ -99782,6 +112254,7 @@
false
false
false
+ 0
12473
@@ -99790,6 +112263,7 @@
false
false
false
+ 0
12474
@@ -99798,6 +112272,7 @@
false
false
false
+ 0
12475
@@ -99806,6 +112281,7 @@
false
false
false
+ 0
12476
@@ -99814,6 +112290,7 @@
false
false
false
+ 0
12477
@@ -99822,6 +112299,7 @@
false
false
false
+ 0
12478
@@ -99830,6 +112308,7 @@
false
false
false
+ 0
12479
@@ -99838,6 +112317,7 @@
false
false
false
+ 0
12480
@@ -99846,6 +112326,7 @@
false
false
false
+ 0
12481
@@ -99854,6 +112335,7 @@
false
false
false
+ 0
12482
@@ -99862,6 +112344,7 @@
false
false
false
+ 0
12483
@@ -99870,6 +112353,7 @@
false
false
false
+ 0
12484
@@ -99878,6 +112362,7 @@
false
false
false
+ 0
12485
@@ -99886,6 +112371,7 @@
false
false
false
+ 0
12486
@@ -99894,6 +112380,7 @@
false
false
false
+ 0
12487
@@ -99902,6 +112389,7 @@
false
false
false
+ 0
12488
@@ -99910,6 +112398,7 @@
false
false
false
+ 0
12489
@@ -99918,6 +112407,7 @@
false
false
false
+ 0
12490
@@ -99926,6 +112416,7 @@
false
false
false
+ 0
12491
@@ -99934,6 +112425,7 @@
false
false
false
+ 0
12492
@@ -99942,6 +112434,7 @@
false
false
false
+ 0
12493
@@ -99950,6 +112443,7 @@
false
false
false
+ 0
12494
@@ -99958,6 +112452,7 @@
false
false
false
+ 0
12495
@@ -99966,6 +112461,7 @@
false
false
false
+ 0
12496
@@ -99974,6 +112470,7 @@
false
false
false
+ 0
12497
@@ -99982,6 +112479,7 @@
false
false
false
+ 0
12498
@@ -99990,6 +112488,7 @@
false
false
false
+ 0
12499
@@ -99998,6 +112497,7 @@
false
false
false
+ 0
12500
@@ -100006,6 +112506,7 @@
false
false
false
+ 0
12501
@@ -100014,6 +112515,7 @@
false
false
false
+ 0
12502
@@ -100022,6 +112524,7 @@
false
false
false
+ 0
12503
@@ -100030,6 +112533,7 @@
false
false
false
+ 0
12504
@@ -100038,6 +112542,7 @@
false
false
false
+ 0
12505
@@ -100046,6 +112551,7 @@
false
false
false
+ 0
12506
@@ -100054,6 +112560,7 @@
false
false
false
+ 0
12507
@@ -100062,6 +112569,7 @@
false
false
false
+ 0
12508
@@ -100070,6 +112578,7 @@
false
false
false
+ 0
12509
@@ -100078,6 +112587,7 @@
false
false
false
+ 0
12510
@@ -100086,6 +112596,7 @@
false
false
false
+ 0
12511
@@ -100094,6 +112605,7 @@
false
false
false
+ 0
12512
@@ -100102,6 +112614,7 @@
false
false
false
+ 0
12513
@@ -100110,6 +112623,7 @@
false
false
false
+ 0
12514
@@ -100118,6 +112632,7 @@
false
false
false
+ 0
12515
@@ -100126,6 +112641,7 @@
false
false
false
+ 0
12516
@@ -100134,6 +112650,7 @@
false
false
false
+ 0
12517
@@ -100142,6 +112659,7 @@
false
false
false
+ 0
12518
@@ -100150,6 +112668,7 @@
false
false
false
+ 0
12519
@@ -100158,6 +112677,7 @@
false
false
false
+ 0
12520
@@ -100166,6 +112686,7 @@
false
false
false
+ 0
12521
@@ -100174,6 +112695,7 @@
false
false
false
+ 0
12522
@@ -100182,6 +112704,7 @@
false
false
false
+ 0
12523
@@ -100190,6 +112713,7 @@
false
false
false
+ 0
12524
@@ -100198,6 +112722,7 @@
false
false
false
+ 0
12525
@@ -100206,6 +112731,7 @@
false
false
false
+ 0
12526
@@ -100214,6 +112740,7 @@
false
false
false
+ 0
12527
@@ -100222,6 +112749,7 @@
false
false
false
+ 0
12528
@@ -100230,6 +112758,7 @@
false
false
false
+ 0
12529
@@ -100238,6 +112767,7 @@
false
false
false
+ 0
12530
@@ -100246,6 +112776,7 @@
false
false
false
+ 0
12531
@@ -100254,6 +112785,7 @@
false
false
false
+ 0
12532
@@ -100262,6 +112794,7 @@
false
false
false
+ 0
12533
@@ -100270,6 +112803,7 @@
false
false
false
+ 0
12534
@@ -100278,6 +112812,7 @@
false
false
false
+ 0
12535
@@ -100286,6 +112821,7 @@
false
false
false
+ 0
12536
@@ -100294,6 +112830,7 @@
false
false
false
+ 0
12537
@@ -100302,6 +112839,7 @@
false
false
false
+ 0
12538
@@ -100310,6 +112848,7 @@
false
false
false
+ 0
12539
@@ -100318,6 +112857,7 @@
false
false
false
+ 0
12540
@@ -100326,6 +112866,7 @@
false
false
false
+ 0
12541
@@ -100334,6 +112875,7 @@
false
false
false
+ 0
12542
@@ -100342,6 +112884,7 @@
false
false
false
+ 0
12543
@@ -100350,6 +112893,7 @@
false
false
false
+ 0
12544
@@ -100358,6 +112902,7 @@
false
false
false
+ 0
12545
@@ -100366,6 +112911,7 @@
false
false
false
+ 0
12546
@@ -100374,6 +112920,7 @@
false
false
false
+ 0
12547
@@ -100382,6 +112929,7 @@
false
false
false
+ 0
12548
@@ -100390,6 +112938,7 @@
false
false
false
+ 0
12549
@@ -100398,6 +112947,7 @@
false
false
false
+ 0
12550
@@ -100406,6 +112956,7 @@
false
false
false
+ 0
12551
@@ -100414,6 +112965,7 @@
false
false
false
+ 0
12552
@@ -100422,6 +112974,7 @@
false
false
false
+ 0
12553
@@ -100430,6 +112983,7 @@
false
false
false
+ 0
12554
@@ -100438,6 +112992,7 @@
false
false
false
+ 0
12555
@@ -100446,6 +113001,7 @@
false
false
false
+ 0
12556
@@ -100454,6 +113010,7 @@
false
false
false
+ 0
12557
@@ -100462,6 +113019,7 @@
false
false
false
+ 0
12558
@@ -100470,6 +113028,7 @@
false
false
false
+ 0
12559
@@ -100478,6 +113037,7 @@
false
false
false
+ 0
12560
@@ -100486,6 +113046,7 @@
false
false
false
+ 0
12561
@@ -100494,6 +113055,7 @@
false
false
false
+ 0
12562
@@ -100502,6 +113064,7 @@
false
false
false
+ 0
12563
@@ -100510,6 +113073,7 @@
false
false
false
+ 0
12564
@@ -100518,6 +113082,7 @@
false
false
false
+ 0
12565
@@ -100526,6 +113091,7 @@
false
false
false
+ 0
12566
@@ -100534,6 +113100,7 @@
false
false
false
+ 0
12567
@@ -100542,6 +113109,7 @@
false
false
false
+ 0
12568
@@ -100550,6 +113118,7 @@
false
false
false
+ 0
12569
@@ -100558,6 +113127,7 @@
false
false
false
+ 0
12570
@@ -100566,6 +113136,7 @@
false
false
false
+ 0
12571
@@ -100574,6 +113145,7 @@
false
false
false
+ 0
12572
@@ -100582,6 +113154,7 @@
false
false
false
+ 0
12573
@@ -100590,6 +113163,7 @@
false
false
false
+ 0
12574
@@ -100598,6 +113172,7 @@
false
false
false
+ 0
12575
@@ -100606,6 +113181,7 @@
false
false
false
+ 0
12576
@@ -100614,6 +113190,7 @@
false
false
false
+ 0
12577
@@ -100622,6 +113199,7 @@
false
false
false
+ 0
12578
@@ -100630,6 +113208,7 @@
false
false
false
+ 0
12579
@@ -100638,6 +113217,7 @@
false
false
false
+ 0
12580
@@ -100646,6 +113226,7 @@
false
false
false
+ 0
12581
@@ -100654,6 +113235,7 @@
false
false
false
+ 0
12582
@@ -100662,6 +113244,7 @@
false
false
false
+ 0
12583
@@ -100670,6 +113253,7 @@
false
false
false
+ 0
12584
@@ -100678,6 +113262,7 @@
false
false
false
+ 0
12585
@@ -100686,6 +113271,7 @@
false
false
false
+ 0
12586
@@ -100694,6 +113280,7 @@
false
false
false
+ 0
12587
@@ -100702,6 +113289,7 @@
false
false
false
+ 0
12588
@@ -100710,6 +113298,7 @@
false
false
false
+ 0
12589
@@ -100718,6 +113307,7 @@
false
false
false
+ 0
12590
@@ -100726,6 +113316,7 @@
false
false
false
+ 0
12591
@@ -100734,6 +113325,7 @@
false
false
false
+ 0
12592
@@ -100742,6 +113334,7 @@
false
false
false
+ 0
12593
@@ -100750,6 +113343,7 @@
false
false
false
+ 0
12594
@@ -100758,6 +113352,7 @@
false
false
false
+ 0
12595
@@ -100766,6 +113361,7 @@
false
false
false
+ 0
12596
@@ -100774,6 +113370,7 @@
false
false
false
+ 0
12597
@@ -100782,6 +113379,7 @@
false
false
false
+ 0
12598
@@ -100790,6 +113388,7 @@
false
false
false
+ 0
12599
@@ -100798,6 +113397,7 @@
false
false
false
+ 0
12600
@@ -100806,6 +113406,7 @@
false
false
false
+ 0
12601
@@ -100814,6 +113415,7 @@
false
false
false
+ 0
12602
@@ -100822,6 +113424,7 @@
false
false
false
+ 0
12603
@@ -100830,6 +113433,7 @@
false
false
false
+ 0
12604
@@ -100838,6 +113442,7 @@
false
false
false
+ 0
12605
@@ -100846,6 +113451,7 @@
false
false
false
+ 0
12606
@@ -100854,6 +113460,7 @@
false
false
false
+ 0
12607
@@ -100862,6 +113469,7 @@
false
false
false
+ 0
12608
@@ -100870,6 +113478,7 @@
false
false
false
+ 0
12609
@@ -100878,6 +113487,7 @@
false
false
false
+ 0
12610
@@ -100886,6 +113496,7 @@
false
false
false
+ 0
12611
@@ -100894,6 +113505,7 @@
false
false
false
+ 0
12612
@@ -100902,6 +113514,7 @@
false
false
false
+ 0
12613
@@ -100910,6 +113523,7 @@
false
false
false
+ 0
12614
@@ -100918,6 +113532,7 @@
false
false
false
+ 0
12615
@@ -100926,6 +113541,7 @@
false
false
false
+ 0
12616
@@ -100934,6 +113550,7 @@
false
false
false
+ 0
12617
@@ -100942,6 +113559,7 @@
false
false
false
+ 0
12618
@@ -100950,6 +113568,7 @@
false
false
false
+ 0
12619
@@ -100958,6 +113577,7 @@
false
false
false
+ 0
12620
@@ -100966,6 +113586,7 @@
false
false
false
+ 0
12621
@@ -100974,6 +113595,7 @@
false
false
false
+ 0
12622
@@ -100982,6 +113604,7 @@
false
false
false
+ 0
12623
@@ -100990,6 +113613,7 @@
false
false
false
+ 0
12624
@@ -100998,6 +113622,7 @@
false
false
false
+ 0
12625
@@ -101006,6 +113631,7 @@
false
false
false
+ 0
12626
@@ -101014,6 +113640,7 @@
false
false
false
+ 0
12627
@@ -101022,6 +113649,7 @@
false
false
false
+ 0
12628
@@ -101030,6 +113658,7 @@
false
false
false
+ 0
12629
@@ -101038,6 +113667,7 @@
false
false
false
+ 0
12630
@@ -101046,6 +113676,7 @@
false
false
false
+ 0
12631
@@ -101054,6 +113685,7 @@
false
false
false
+ 0
12632
@@ -101062,6 +113694,7 @@
false
false
false
+ 0
12633
@@ -101070,6 +113703,7 @@
false
false
false
+ 0
12634
@@ -101078,6 +113712,7 @@
false
false
false
+ 0
12635
@@ -101086,6 +113721,7 @@
false
false
false
+ 0
12636
@@ -101094,6 +113730,7 @@
false
false
false
+ 0
12637
@@ -101102,6 +113739,7 @@
false
false
false
+ 0
12638
@@ -101110,6 +113748,7 @@
false
false
false
+ 0
12639
@@ -101118,6 +113757,7 @@
false
false
false
+ 0
12640
@@ -101126,6 +113766,7 @@
false
false
false
+ 0
12641
@@ -101134,6 +113775,7 @@
false
false
false
+ 0
12642
@@ -101142,6 +113784,7 @@
false
false
false
+ 0
12643
@@ -101150,6 +113793,7 @@
false
false
false
+ 0
12644
@@ -101158,6 +113802,7 @@
false
false
false
+ 0
12645
@@ -101166,6 +113811,7 @@
false
false
false
+ 0
12646
@@ -101174,6 +113820,7 @@
false
false
false
+ 0
12647
@@ -101182,6 +113829,7 @@
false
false
false
+ 0
12648
@@ -101190,6 +113838,7 @@
false
false
false
+ 0
12649
@@ -101198,6 +113847,7 @@
false
false
false
+ 0
12650
@@ -101206,6 +113856,7 @@
false
false
false
+ 0
12651
@@ -101214,6 +113865,7 @@
false
false
false
+ 0
12652
@@ -101222,6 +113874,7 @@
false
false
false
+ 0
12653
@@ -101230,6 +113883,7 @@
false
false
false
+ 0
12654
@@ -101238,6 +113892,7 @@
false
false
false
+ 0
12655
@@ -101246,6 +113901,7 @@
false
false
false
+ 0
12656
@@ -101254,6 +113910,7 @@
false
false
false
+ 0
12657
@@ -101262,6 +113919,7 @@
false
false
false
+ 0
12658
@@ -101270,6 +113928,7 @@
false
false
false
+ 0
12659
@@ -101278,6 +113937,7 @@
false
false
false
+ 0
12660
@@ -101286,6 +113946,7 @@
false
false
false
+ 0
12661
@@ -101294,6 +113955,7 @@
false
false
false
+ 0
12662
@@ -101302,6 +113964,7 @@
false
false
false
+ 0
12663
@@ -101310,6 +113973,7 @@
false
false
false
+ 0
12664
@@ -101318,6 +113982,7 @@
false
false
false
+ 0
12665
@@ -101326,6 +113991,7 @@
false
false
false
+ 0
12666
@@ -101334,6 +114000,7 @@
false
false
false
+ 0
12667
@@ -101342,6 +114009,7 @@
false
false
false
+ 0
12668
@@ -101350,6 +114018,7 @@
false
false
false
+ 0
12669
@@ -101358,6 +114027,7 @@
false
false
false
+ 0
12670
@@ -101366,6 +114036,7 @@
false
false
false
+ 0
12671
@@ -101374,6 +114045,7 @@
false
false
false
+ 0
12672
@@ -101382,6 +114054,7 @@
false
false
false
+ 0
12673
@@ -101390,6 +114063,7 @@
false
false
false
+ 0
12674
@@ -101398,6 +114072,7 @@
false
false
false
+ 0
12675
@@ -101406,6 +114081,7 @@
false
false
false
+ 0
12676
@@ -101414,6 +114090,7 @@
false
false
false
+ 0
12677
@@ -101422,6 +114099,7 @@
false
false
false
+ 0
12678
@@ -101430,6 +114108,7 @@
false
false
false
+ 0
12679
@@ -101438,6 +114117,7 @@
false
false
false
+ 0
12680
@@ -101446,6 +114126,7 @@
false
false
false
+ 0
12681
@@ -101454,6 +114135,7 @@
false
false
false
+ 0
12682
@@ -101462,6 +114144,7 @@
false
false
false
+ 0
12683
@@ -101470,6 +114153,7 @@
false
false
false
+ 0
12684
@@ -101478,6 +114162,7 @@
false
false
false
+ 0
12685
@@ -101486,6 +114171,7 @@
false
false
false
+ 0
12686
@@ -101494,6 +114180,7 @@
false
false
false
+ 0
12687
@@ -101502,6 +114189,7 @@
false
false
false
+ 0
12688
@@ -101510,6 +114198,7 @@
false
false
false
+ 0
12689
@@ -101518,6 +114207,7 @@
false
false
false
+ 0
12690
@@ -101526,6 +114216,7 @@
false
false
false
+ 0
12691
@@ -101534,6 +114225,7 @@
false
false
false
+ 0
12692
@@ -101542,6 +114234,7 @@
false
false
false
+ 0
12693
@@ -101550,6 +114243,7 @@
false
false
false
+ 0
12694
@@ -101558,6 +114252,7 @@
false
false
false
+ 0
12695
@@ -101566,6 +114261,7 @@
false
false
false
+ 0
12696
@@ -101574,6 +114270,7 @@
false
false
false
+ 0
12697
@@ -101582,6 +114279,7 @@
false
false
false
+ 0
12698
@@ -101590,6 +114288,7 @@
false
false
false
+ 0
12699
@@ -101598,6 +114297,7 @@
false
false
false
+ 0
12700
@@ -101606,6 +114306,7 @@
false
false
false
+ 0
12701
@@ -101614,6 +114315,7 @@
false
false
false
+ 0
12702
@@ -101622,6 +114324,7 @@
false
false
false
+ 0
12703
@@ -101630,6 +114333,7 @@
false
false
false
+ 0
12704
@@ -101638,6 +114342,7 @@
false
false
false
+ 0
12705
@@ -101646,6 +114351,7 @@
false
false
false
+ 0
12706
@@ -101654,6 +114360,7 @@
false
false
false
+ 0
12707
@@ -101662,6 +114369,7 @@
false
false
false
+ 0
12708
@@ -101670,6 +114378,7 @@
false
false
false
+ 0
12709
@@ -101678,6 +114387,7 @@
false
false
false
+ 0
12710
@@ -101686,6 +114396,7 @@
false
false
false
+ 0
12711
@@ -101694,6 +114405,7 @@
false
false
false
+ 0
12712
@@ -101702,6 +114414,7 @@
false
false
false
+ 0
12713
@@ -101710,6 +114423,7 @@
false
false
false
+ 0
12714
@@ -101718,6 +114432,7 @@
false
false
false
+ 0
12715
@@ -101726,6 +114441,7 @@
false
false
false
+ 0
12716
@@ -101734,6 +114450,7 @@
false
false
false
+ 0
12717
@@ -101742,6 +114459,7 @@
false
false
false
+ 0
12718
@@ -101750,6 +114468,7 @@
false
false
false
+ 0
12719
@@ -101758,6 +114477,7 @@
false
false
false
+ 0
12720
@@ -101766,6 +114486,7 @@
false
false
false
+ 0
12721
@@ -101774,6 +114495,7 @@
false
false
false
+ 0
12722
@@ -101782,6 +114504,7 @@
false
false
false
+ 0
12723
@@ -101790,6 +114513,7 @@
false
false
false
+ 0
12724
@@ -101798,6 +114522,7 @@
false
false
false
+ 0
12725
@@ -101806,6 +114531,7 @@
false
false
false
+ 0
12726
@@ -101814,6 +114540,7 @@
false
false
false
+ 0
12727
@@ -101822,6 +114549,7 @@
false
false
false
+ 0
12728
@@ -101830,6 +114558,7 @@
false
false
false
+ 0
12729
@@ -101838,6 +114567,7 @@
false
false
false
+ 0
12730
@@ -101846,6 +114576,7 @@
false
false
false
+ 0
12731
@@ -101854,6 +114585,7 @@
false
false
false
+ 0
12732
@@ -101862,6 +114594,7 @@
false
false
false
+ 0
12733
@@ -101870,6 +114603,7 @@
false
false
false
+ 0
12734
@@ -101878,6 +114612,7 @@
false
false
false
+ 0
12735
@@ -101886,6 +114621,7 @@
false
false
false
+ 0
12736
@@ -101894,6 +114630,7 @@
false
false
false
+ 0
12737
@@ -101902,6 +114639,7 @@
false
false
false
+ 0
12738
@@ -101910,6 +114648,7 @@
false
false
false
+ 0
12739
@@ -101918,6 +114657,7 @@
false
false
false
+ 0
12740
@@ -101926,6 +114666,7 @@
false
false
false
+ 0
12741
@@ -101934,6 +114675,7 @@
false
false
false
+ 0
12742
@@ -101942,6 +114684,7 @@
false
false
false
+ 0
12743
@@ -101950,6 +114693,7 @@
false
false
false
+ 0
12744
@@ -101958,6 +114702,7 @@
false
false
false
+ 0
12745
@@ -101966,6 +114711,7 @@
false
false
false
+ 0
12746
@@ -101974,6 +114720,7 @@
false
false
false
+ 0
12747
@@ -101982,6 +114729,7 @@
false
false
false
+ 0
12748
@@ -101990,6 +114738,7 @@
false
false
false
+ 0
12749
@@ -101998,6 +114747,7 @@
false
false
false
+ 0
12750
@@ -102006,6 +114756,7 @@
false
false
false
+ 0
12751
@@ -102014,6 +114765,7 @@
false
false
false
+ 0
12752
@@ -102022,6 +114774,7 @@
false
false
false
+ 0
12753
@@ -102030,6 +114783,7 @@
false
false
false
+ 0
12754
@@ -102038,6 +114792,7 @@
false
false
false
+ 0
12755
@@ -102046,6 +114801,7 @@
false
false
false
+ 0
12756
@@ -102054,6 +114810,7 @@
false
false
false
+ 0
12757
@@ -102062,6 +114819,7 @@
false
false
false
+ 0
12758
@@ -102070,6 +114828,7 @@
false
false
false
+ 0
12759
@@ -102078,6 +114837,7 @@
false
false
false
+ 0
12760
@@ -102086,6 +114846,7 @@
false
false
false
+ 0
12761
@@ -102094,6 +114855,7 @@
false
false
false
+ 0
12762
@@ -102102,6 +114864,7 @@
false
false
false
+ 0
12763
@@ -102110,6 +114873,7 @@
false
false
false
+ 0
12764
@@ -102118,6 +114882,7 @@
false
false
false
+ 0
12765
@@ -102126,6 +114891,7 @@
false
false
false
+ 0
12766
@@ -102134,6 +114900,7 @@
false
false
false
+ 0
12767
@@ -102142,6 +114909,7 @@
false
false
false
+ 0
12768
@@ -102150,6 +114918,7 @@
false
false
false
+ 0
12769
@@ -102158,6 +114927,7 @@
false
false
false
+ 0
12770
@@ -102166,6 +114936,7 @@
false
false
false
+ 0
12771
@@ -102174,6 +114945,7 @@
false
false
false
+ 0
12772
@@ -102182,6 +114954,7 @@
false
false
false
+ 0
12773
@@ -102190,6 +114963,7 @@
false
false
false
+ 0
12774
@@ -102198,6 +114972,7 @@
false
false
false
+ 0
12775
@@ -102206,6 +114981,7 @@
false
false
false
+ 0
12776
@@ -102214,6 +114990,7 @@
false
false
false
+ 0
12777
@@ -102222,6 +114999,7 @@
false
false
false
+ 0
12778
@@ -102230,6 +115008,7 @@
false
false
false
+ 0
12779
@@ -102238,6 +115017,7 @@
false
false
false
+ 0
12780
@@ -102246,6 +115026,7 @@
false
false
false
+ 0
12781
@@ -102254,6 +115035,7 @@
false
false
false
+ 0
12782
@@ -102262,6 +115044,7 @@
false
false
false
+ 0
12783
@@ -102270,6 +115053,7 @@
false
false
false
+ 0
12784
@@ -102278,6 +115062,7 @@
false
false
false
+ 0
12785
@@ -102286,6 +115071,7 @@
false
false
false
+ 0
12786
@@ -102294,6 +115080,7 @@
false
false
false
+ 0
12787
@@ -102302,6 +115089,7 @@
false
false
false
+ 0
12788
@@ -102310,6 +115098,7 @@
false
false
false
+ 0
12789
@@ -102318,6 +115107,7 @@
false
false
false
+ 0
12790
@@ -102326,6 +115116,7 @@
false
false
false
+ 0
12791
@@ -102334,6 +115125,7 @@
false
false
false
+ 0
12792
@@ -102342,6 +115134,7 @@
false
false
false
+ 0
12793
@@ -102350,6 +115143,7 @@
false
false
false
+ 0
12794
@@ -102358,6 +115152,7 @@
false
false
false
+ 0
12795
@@ -102366,6 +115161,7 @@
false
false
false
+ 0
12796
@@ -102374,6 +115170,7 @@
false
false
false
+ 0
12797
@@ -102382,6 +115179,7 @@
false
false
false
+ 0
12798
@@ -102390,6 +115188,7 @@
false
false
false
+ 0
12799
@@ -102398,6 +115197,7 @@
false
false
false
+ 0
12800
@@ -102406,6 +115206,7 @@
false
false
false
+ 0
12801
@@ -102414,6 +115215,7 @@
false
false
false
+ 0
12802
@@ -102422,6 +115224,7 @@
false
false
false
+ 0
12803
@@ -102430,6 +115233,7 @@
false
false
false
+ 0
12804
@@ -102438,6 +115242,7 @@
false
false
false
+ 0
12805
@@ -102446,6 +115251,7 @@
false
false
false
+ 0
12806
@@ -102454,6 +115260,7 @@
false
false
false
+ 0
12807
@@ -102462,6 +115269,7 @@
false
false
false
+ 0
12808
@@ -102470,6 +115278,7 @@
false
false
false
+ 0
12809
@@ -102478,6 +115287,7 @@
false
false
false
+ 0
12810
@@ -102486,6 +115296,7 @@
false
false
false
+ 0
12811
@@ -102494,6 +115305,7 @@
false
false
false
+ 0
12812
@@ -102502,6 +115314,7 @@
false
false
false
+ 0
12813
@@ -102510,6 +115323,7 @@
false
false
false
+ 0
12814
@@ -102518,6 +115332,7 @@
false
false
false
+ 0
12815
@@ -102526,6 +115341,7 @@
false
false
false
+ 0
12816
@@ -102534,6 +115350,7 @@
false
false
false
+ 0
12817
@@ -102542,6 +115359,7 @@
false
false
false
+ 0
12818
@@ -102550,6 +115368,7 @@
false
false
false
+ 0
12819
@@ -102558,6 +115377,7 @@
false
false
false
+ 0
12820
@@ -102566,6 +115386,7 @@
false
false
false
+ 0
12821
@@ -102574,6 +115395,7 @@
false
false
false
+ 0
12822
@@ -102582,6 +115404,7 @@
false
false
false
+ 0
12823
@@ -102590,6 +115413,7 @@
false
false
false
+ 0
12824
@@ -102598,6 +115422,7 @@
false
false
false
+ 0
12825
@@ -102606,6 +115431,7 @@
false
false
false
+ 0
12826
@@ -102614,6 +115440,7 @@
false
false
false
+ 0
12827
@@ -102622,6 +115449,7 @@
false
false
false
+ 0
12828
@@ -102630,6 +115458,7 @@
false
false
false
+ 0
12829
@@ -102638,6 +115467,7 @@
false
false
false
+ 0
12830
@@ -102646,6 +115476,7 @@
false
false
false
+ 0
12831
@@ -102654,6 +115485,7 @@
false
false
false
+ 0
12832
@@ -102662,6 +115494,7 @@
false
false
false
+ 0
12833
@@ -102670,6 +115503,7 @@
false
false
false
+ 0
12834
@@ -102678,6 +115512,7 @@
false
false
false
+ 0
12835
@@ -102686,6 +115521,7 @@
false
false
false
+ 0
12836
@@ -102694,6 +115530,7 @@
false
false
false
+ 0
12837
@@ -102702,6 +115539,7 @@
false
false
false
+ 0
12838
@@ -102710,6 +115548,7 @@
false
false
false
+ 0
12839
@@ -102718,6 +115557,7 @@
false
false
false
+ 0
12840
@@ -102726,6 +115566,7 @@
false
false
false
+ 0
12841
@@ -102734,6 +115575,7 @@
false
false
false
+ 0
12842
@@ -102742,6 +115584,7 @@
false
false
false
+ 0
12843
@@ -102750,6 +115593,7 @@
false
false
false
+ 0
12844
@@ -102758,6 +115602,7 @@
false
false
false
+ 0
12845
@@ -102766,6 +115611,7 @@
false
false
false
+ 0
12846
@@ -102774,6 +115620,7 @@
false
false
false
+ 0
12847
@@ -102782,6 +115629,7 @@
false
false
false
+ 0
12848
@@ -102790,6 +115638,7 @@
false
false
false
+ 0
12849
@@ -102798,6 +115647,7 @@
false
false
false
+ 0
12850
@@ -102806,6 +115656,7 @@
false
false
false
+ 0
12851
@@ -102814,6 +115665,7 @@
false
false
false
+ 0
12852
@@ -102822,6 +115674,7 @@
false
false
false
+ 0
12853
@@ -102830,6 +115683,7 @@
false
false
false
+ 0
12854
@@ -102838,6 +115692,7 @@
false
false
false
+ 0
12855
@@ -102846,6 +115701,7 @@
false
false
false
+ 0
12856
@@ -102854,6 +115710,7 @@
false
false
false
+ 0
12857
@@ -102862,6 +115719,7 @@
false
false
false
+ 0
12858
@@ -102870,6 +115728,7 @@
false
false
false
+ 0
12859
@@ -102878,6 +115737,7 @@
false
false
false
+ 0
12860
@@ -102886,6 +115746,7 @@
false
false
false
+ 0
12861
@@ -102894,6 +115755,7 @@
false
false
false
+ 0
12862
@@ -102902,6 +115764,7 @@
false
false
false
+ 0
12863
@@ -102910,6 +115773,7 @@
false
false
false
+ 0
12864
@@ -102918,6 +115782,7 @@
false
false
false
+ 0
12865
@@ -102926,6 +115791,7 @@
false
false
false
+ 0
12866
@@ -102934,6 +115800,7 @@
false
false
false
+ 0
12867
@@ -102942,6 +115809,7 @@
false
false
false
+ 0
12868
@@ -102950,6 +115818,7 @@
false
false
false
+ 0
12869
@@ -102958,6 +115827,7 @@
false
false
false
+ 0
12870
@@ -102966,6 +115836,7 @@
false
false
false
+ 0
12871
@@ -102974,6 +115845,7 @@
false
false
false
+ 0
12872
@@ -102982,6 +115854,7 @@
false
false
false
+ 0
12873
@@ -102990,6 +115863,7 @@
false
false
false
+ 0
12874
@@ -102998,6 +115872,7 @@
false
false
false
+ 0
12875
@@ -103006,6 +115881,7 @@
false
false
false
+ 0
12876
@@ -103014,6 +115890,7 @@
false
false
false
+ 0
12877
@@ -103022,6 +115899,7 @@
false
false
false
+ 0
12878
@@ -103030,6 +115908,7 @@
false
false
false
+ 0
12879
@@ -103038,6 +115917,7 @@
false
false
false
+ 0
12880
@@ -103046,6 +115926,7 @@
false
false
false
+ 0
12881
@@ -103054,6 +115935,7 @@
false
false
false
+ 0
12882
@@ -103062,6 +115944,7 @@
false
false
false
+ 0
12883
@@ -103070,6 +115953,7 @@
false
false
false
+ 0
12884
@@ -103078,6 +115962,7 @@
false
false
false
+ 0
12885
@@ -103086,6 +115971,7 @@
false
false
false
+ 0
12886
@@ -103094,6 +115980,7 @@
false
false
false
+ 0
12887
@@ -103102,6 +115989,7 @@
false
false
false
+ 0
12888
@@ -103110,6 +115998,7 @@
false
false
false
+ 0
12889
@@ -103118,6 +116007,7 @@
false
false
false
+ 0
12890
@@ -103126,6 +116016,7 @@
false
false
false
+ 0
12891
@@ -103134,6 +116025,7 @@
false
false
false
+ 0
12892
@@ -103142,6 +116034,7 @@
false
false
false
+ 0
12893
@@ -103150,6 +116043,7 @@
false
false
false
+ 0
12894
@@ -103158,6 +116052,7 @@
false
false
false
+ 0
12895
@@ -103166,6 +116061,7 @@
false
false
false
+ 0
12896
@@ -103174,6 +116070,7 @@
false
false
false
+ 0
12897
@@ -103182,6 +116079,7 @@
false
false
false
+ 0
12898
@@ -103190,6 +116088,7 @@
false
false
false
+ 0
12899
@@ -103198,6 +116097,7 @@
false
false
false
+ 0
12900
@@ -103206,6 +116106,7 @@
false
false
false
+ 0
12901
@@ -103214,6 +116115,7 @@
false
false
false
+ 0
12902
@@ -103222,6 +116124,7 @@
false
false
false
+ 0
12903
@@ -103230,6 +116133,7 @@
false
false
false
+ 0
12904
@@ -103238,6 +116142,7 @@
false
false
false
+ 0
12905
@@ -103246,6 +116151,7 @@
false
false
false
+ 0
12906
@@ -103254,6 +116160,7 @@
false
false
false
+ 0
12907
@@ -103262,6 +116169,7 @@
false
false
false
+ 0
12908
@@ -103270,6 +116178,7 @@
false
false
false
+ 0
12909
@@ -103278,6 +116187,7 @@
false
false
false
+ 0
12910
@@ -103286,6 +116196,7 @@
false
false
false
+ 0
12911
@@ -103294,6 +116205,7 @@
false
false
false
+ 0
12912
@@ -103302,6 +116214,7 @@
false
false
false
+ 0
12913
@@ -103310,6 +116223,7 @@
false
false
false
+ 0
12914
@@ -103318,6 +116232,7 @@
false
false
false
+ 0
12915
@@ -103326,6 +116241,7 @@
false
false
false
+ 0
12916
@@ -103334,6 +116250,7 @@
false
false
false
+ 0
12917
@@ -103342,6 +116259,7 @@
false
false
false
+ 0
12918
@@ -103350,6 +116268,7 @@
false
false
false
+ 0
12919
@@ -103358,6 +116277,7 @@
false
false
false
+ 0
12920
@@ -103366,6 +116286,7 @@
false
false
false
+ 0
12921
@@ -103374,6 +116295,7 @@
false
false
false
+ 0
12922
@@ -103382,6 +116304,7 @@
false
false
false
+ 0
12923
@@ -103390,6 +116313,7 @@
false
false
false
+ 0
12924
@@ -103398,6 +116322,7 @@
false
false
false
+ 0
12925
@@ -103406,6 +116331,7 @@
false
false
false
+ 0
12926
@@ -103414,6 +116340,7 @@
false
false
false
+ 0
12927
@@ -103422,6 +116349,7 @@
false
false
false
+ 0
12928
@@ -103430,6 +116358,7 @@
false
false
false
+ 0
12929
@@ -103438,6 +116367,7 @@
false
false
false
+ 0
12930
@@ -103446,6 +116376,7 @@
false
false
false
+ 0
12931
@@ -103454,6 +116385,7 @@
false
false
false
+ 0
12932
@@ -103462,6 +116394,7 @@
false
false
false
+ 0
12933
@@ -103470,6 +116403,7 @@
false
false
false
+ 0
12934
@@ -103478,6 +116412,7 @@
false
false
false
+ 0
12935
@@ -103486,6 +116421,7 @@
false
false
false
+ 0
12936
@@ -103494,6 +116430,7 @@
false
false
false
+ 0
12937
@@ -103502,6 +116439,7 @@
false
false
false
+ 0
12938
@@ -103510,6 +116448,7 @@
false
false
false
+ 0
12939
@@ -103518,6 +116457,7 @@
false
false
false
+ 0
12940
@@ -103526,6 +116466,7 @@
false
false
false
+ 0
12941
@@ -103534,6 +116475,7 @@
false
false
false
+ 0
12942
@@ -103542,6 +116484,7 @@
false
false
false
+ 0
12943
@@ -103550,6 +116493,7 @@
false
false
false
+ 0
12944
@@ -103558,6 +116502,7 @@
false
false
false
+ 0
12945
@@ -103566,6 +116511,7 @@
false
false
false
+ 0
12946
@@ -103574,6 +116520,7 @@
false
false
false
+ 0
12947
@@ -103582,6 +116529,7 @@
false
false
false
+ 0
12948
@@ -103590,6 +116538,7 @@
false
false
false
+ 0
12949
@@ -103598,6 +116547,7 @@
false
false
false
+ 0
12950
@@ -103606,6 +116556,7 @@
false
false
false
+ 0
12951
@@ -103614,6 +116565,7 @@
false
false
false
+ 0
12952
@@ -103622,6 +116574,7 @@
false
false
false
+ 0
12953
@@ -103630,6 +116583,7 @@
false
false
false
+ 0
12954
@@ -103638,6 +116592,7 @@
false
false
false
+ 0
12955
@@ -103646,6 +116601,7 @@
false
false
false
+ 0
12956
@@ -103654,6 +116610,7 @@
false
false
false
+ 0
12957
@@ -103662,6 +116619,7 @@
false
false
false
+ 0
12958
@@ -103670,6 +116628,7 @@
false
false
false
+ 0
12959
@@ -103678,6 +116637,7 @@
false
false
false
+ 0
12960
@@ -103686,6 +116646,7 @@
false
false
false
+ 0
12961
@@ -103694,6 +116655,7 @@
false
false
false
+ 0
12962
@@ -103702,6 +116664,7 @@
false
false
false
+ 0
12963
@@ -103710,6 +116673,7 @@
false
false
false
+ 0
12964
@@ -103718,6 +116682,7 @@
false
false
false
+ 0
12965
@@ -103726,6 +116691,7 @@
false
false
false
+ 0
12966
@@ -103734,6 +116700,7 @@
false
false
false
+ 0
12967
@@ -103742,6 +116709,7 @@
false
false
false
+ 0
12968
@@ -103750,6 +116718,7 @@
false
false
false
+ 0
12969
@@ -103758,6 +116727,7 @@
false
false
false
+ 0
12970
@@ -103766,6 +116736,7 @@
false
false
false
+ 0
12971
@@ -103774,6 +116745,7 @@
false
false
false
+ 0
12972
@@ -103782,6 +116754,7 @@
false
false
false
+ 0
12973
@@ -103790,6 +116763,7 @@
false
false
false
+ 0
12974
@@ -103798,6 +116772,7 @@
false
false
false
+ 0
12975
@@ -103806,6 +116781,7 @@
false
false
false
+ 0
12976
@@ -103814,6 +116790,7 @@
false
false
false
+ 0
12977
@@ -103822,6 +116799,7 @@
false
false
false
+ 0
12978
@@ -103830,6 +116808,7 @@
false
false
false
+ 0
12979
@@ -103838,6 +116817,7 @@
false
false
false
+ 0
12980
@@ -103846,6 +116826,7 @@
false
false
false
+ 0
12981
@@ -103854,6 +116835,7 @@
false
false
false
+ 0
12982
@@ -103862,6 +116844,7 @@
false
false
false
+ 0
12983
@@ -103870,6 +116853,7 @@
false
false
false
+ 0
12984
@@ -103878,6 +116862,7 @@
false
false
false
+ 0
12985
@@ -103886,6 +116871,7 @@
false
false
false
+ 0
12986
@@ -103894,6 +116880,7 @@
false
false
false
+ 0
12987
@@ -103902,6 +116889,7 @@
false
false
false
+ 0
12988
@@ -103910,6 +116898,7 @@
false
false
false
+ 0
12989
@@ -103918,6 +116907,7 @@
false
false
false
+ 0
12990
@@ -103926,6 +116916,7 @@
false
false
false
+ 0
12991
@@ -103934,6 +116925,7 @@
false
false
false
+ 0
12992
@@ -103942,6 +116934,7 @@
false
false
false
+ 0
12993
@@ -103950,6 +116943,7 @@
false
false
false
+ 0
12994
@@ -103958,6 +116952,7 @@
false
false
false
+ 0
12995
@@ -103966,6 +116961,7 @@
false
false
false
+ 0
12996
@@ -103974,6 +116970,7 @@
false
false
false
+ 0
12997
@@ -103982,6 +116979,7 @@
false
false
false
+ 0
12998
@@ -103990,6 +116988,7 @@
false
false
false
+ 0
12999
@@ -103998,6 +116997,7 @@
false
false
false
+ 0
13000
@@ -104006,6 +117006,7 @@
false
false
false
+ 0
13001
@@ -104014,6 +117015,7 @@
false
false
false
+ 0
13002
@@ -104022,6 +117024,7 @@
false
false
false
+ 0
13003
@@ -104030,6 +117033,7 @@
false
false
false
+ 0
13004
@@ -104038,6 +117042,7 @@
false
false
false
+ 0
13005
@@ -104046,6 +117051,7 @@
false
false
false
+ 0
13006
@@ -104054,6 +117060,7 @@
false
false
false
+ 0
13007
@@ -104062,6 +117069,7 @@
false
false
false
+ 0
13008
@@ -104070,6 +117078,7 @@
false
false
false
+ 0
13009
@@ -104078,6 +117087,7 @@
false
false
false
+ 0
13010
@@ -104086,6 +117096,7 @@
false
false
false
+ 0
13011
@@ -104094,6 +117105,7 @@
false
false
false
+ 0
13012
@@ -104102,6 +117114,7 @@
false
false
false
+ 0
13013
@@ -104110,6 +117123,7 @@
false
false
false
+ 0
13014
@@ -104118,6 +117132,7 @@
false
false
false
+ 0
13015
@@ -104126,6 +117141,7 @@
false
false
false
+ 0
13016
@@ -104134,6 +117150,7 @@
false
false
false
+ 0
13017
@@ -104142,6 +117159,7 @@
false
false
false
+ 0
13018
@@ -104150,6 +117168,7 @@
false
false
false
+ 0
13019
@@ -104158,6 +117177,7 @@
false
false
false
+ 0
13020
@@ -104166,6 +117186,7 @@
false
false
false
+ 0
13021
@@ -104174,6 +117195,7 @@
false
false
false
+ 0
13022
@@ -104182,6 +117204,7 @@
false
false
false
+ 0
13023
@@ -104190,6 +117213,7 @@
false
false
false
+ 0
13024
@@ -104198,6 +117222,7 @@
false
false
false
+ 0
13025
@@ -104206,6 +117231,7 @@
false
false
false
+ 0
13026
@@ -104214,6 +117240,7 @@
false
false
false
+ 0
13027
@@ -104222,6 +117249,7 @@
false
false
false
+ 0
13028
@@ -104230,6 +117258,7 @@
false
false
false
+ 0
13029
@@ -104238,6 +117267,7 @@
false
false
false
+ 0
13030
@@ -104246,6 +117276,7 @@
false
false
false
+ 0
13031
@@ -104254,6 +117285,7 @@
false
false
false
+ 0
13032
@@ -104262,6 +117294,7 @@
false
false
false
+ 0
13033
@@ -104270,6 +117303,7 @@
false
false
false
+ 0
13034
@@ -104278,6 +117312,7 @@
false
false
false
+ 0
13035
@@ -104286,6 +117321,7 @@
false
false
false
+ 0
13036
@@ -104294,6 +117330,7 @@
false
false
false
+ 0
13037
@@ -104302,6 +117339,7 @@
false
false
false
+ 0
13038
@@ -104310,6 +117348,7 @@
false
false
false
+ 0
13039
@@ -104318,6 +117357,7 @@
false
false
false
+ 0
13040
@@ -104326,6 +117366,7 @@
false
false
false
+ 0
13041
@@ -104334,6 +117375,7 @@
false
false
false
+ 0
13042
@@ -104342,6 +117384,7 @@
false
false
false
+ 0
13043
@@ -104350,6 +117393,7 @@
false
false
false
+ 0
13044
@@ -104358,6 +117402,7 @@
false
false
false
+ 0
13045
@@ -104366,6 +117411,7 @@
false
false
false
+ 0
13046
@@ -104374,6 +117420,7 @@
false
false
false
+ 0
13047
@@ -104382,6 +117429,7 @@
false
false
false
+ 0
13048
@@ -104390,6 +117438,7 @@
false
false
false
+ 0
13049
@@ -104398,6 +117447,7 @@
false
false
false
+ 0
13050
@@ -104406,6 +117456,7 @@
false
false
false
+ 0
13051
@@ -104414,6 +117465,7 @@
false
false
false
+ 0
13052
@@ -104422,6 +117474,7 @@
false
false
false
+ 0
13053
@@ -104430,6 +117483,7 @@
false
false
false
+ 0
13054
@@ -104438,6 +117492,7 @@
false
false
false
+ 0
13055
@@ -104446,6 +117501,7 @@
false
false
false
+ 0
13056
@@ -104454,6 +117510,7 @@
false
false
false
+ 0
13057
@@ -104462,6 +117519,7 @@
false
false
false
+ 0
13058
@@ -104470,6 +117528,7 @@
false
false
false
+ 0
13059
@@ -104478,6 +117537,7 @@
false
false
false
+ 0
13060
@@ -104486,6 +117546,7 @@
false
false
false
+ 0
13061
@@ -104494,6 +117555,7 @@
false
false
false
+ 0
13062
@@ -104502,6 +117564,7 @@
false
false
false
+ 0
13063
@@ -104510,6 +117573,7 @@
false
false
false
+ 0
13064
@@ -104518,6 +117582,7 @@
false
false
false
+ 0
13065
@@ -104526,6 +117591,7 @@
false
false
false
+ 0
13066
@@ -104534,6 +117600,7 @@
false
false
false
+ 0
13067
@@ -104542,6 +117609,7 @@
false
false
false
+ 0
13068
@@ -104550,6 +117618,7 @@
false
false
false
+ 0
13069
@@ -104558,6 +117627,7 @@
false
false
false
+ 0
13070
@@ -104566,6 +117636,7 @@
false
false
false
+ 0
13071
@@ -104574,6 +117645,7 @@
false
false
false
+ 0
13072
@@ -104582,6 +117654,7 @@
false
false
false
+ 0
13073
@@ -104590,6 +117663,7 @@
false
false
false
+ 0
13074
@@ -104598,6 +117672,7 @@
false
false
false
+ 0
13075
@@ -104606,6 +117681,7 @@
false
false
false
+ 0
13076
@@ -104614,6 +117690,7 @@
false
false
false
+ 0
13077
@@ -104622,6 +117699,7 @@
false
false
false
+ 0
13078
@@ -104630,6 +117708,7 @@
false
false
false
+ 0
13079
@@ -104638,6 +117717,7 @@
false
false
false
+ 0
13080
@@ -104646,6 +117726,7 @@
false
false
false
+ 0
13081
@@ -104654,6 +117735,7 @@
false
false
false
+ 0
13082
@@ -104662,6 +117744,7 @@
false
false
false
+ 0
13083
@@ -104670,6 +117753,7 @@
false
false
false
+ 0
13084
@@ -104678,6 +117762,7 @@
false
false
false
+ 0
13085
@@ -104686,6 +117771,7 @@
false
false
false
+ 0
13086
@@ -104694,6 +117780,7 @@
false
false
false
+ 0
13087
@@ -104702,6 +117789,7 @@
false
false
false
+ 0
13088
@@ -104710,6 +117798,7 @@
false
false
false
+ 0
13089
@@ -104718,6 +117807,7 @@
false
false
false
+ 0
13090
@@ -104726,6 +117816,7 @@
false
false
false
+ 0
13091
@@ -104734,6 +117825,7 @@
false
false
false
+ 0
13092
@@ -104742,6 +117834,7 @@
false
false
false
+ 0
13093
@@ -104750,6 +117843,7 @@
false
false
false
+ 0
13094
@@ -104758,6 +117852,7 @@
false
false
false
+ 0
13095
@@ -104766,6 +117861,7 @@
false
false
false
+ 0
13096
@@ -104774,6 +117870,7 @@
false
false
false
+ 0
13097
@@ -104782,6 +117879,7 @@
false
false
false
+ 0
13098
@@ -104790,6 +117888,7 @@
false
false
false
+ 0
13099
@@ -104798,6 +117897,7 @@
false
false
false
+ 0
13100
@@ -104806,6 +117906,7 @@
false
false
false
+ 0
13101
@@ -104814,6 +117915,7 @@
false
false
false
+ 0
13102
@@ -104822,6 +117924,7 @@
false
false
false
+ 0
13103
@@ -104830,6 +117933,7 @@
false
false
false
+ 0
13104
@@ -104838,6 +117942,7 @@
false
false
false
+ 0
13105
@@ -104846,6 +117951,7 @@
false
false
false
+ 0
13106
@@ -104854,6 +117960,7 @@
false
false
false
+ 0
13107
@@ -104862,6 +117969,7 @@
false
false
false
+ 0
13108
@@ -104870,6 +117978,7 @@
false
false
false
+ 0
13109
@@ -104878,6 +117987,7 @@
false
false
false
+ 0
13110
@@ -104886,6 +117996,7 @@
false
false
false
+ 0
13111
@@ -104894,6 +118005,7 @@
false
false
false
+ 0
13112
@@ -104902,6 +118014,7 @@
false
false
false
+ 0
13113
@@ -104910,6 +118023,7 @@
false
false
false
+ 0
13114
@@ -104918,6 +118032,7 @@
false
false
false
+ 0
13115
@@ -104926,6 +118041,7 @@
false
false
false
+ 0
13116
@@ -104934,6 +118050,7 @@
false
false
false
+ 0
13117
@@ -104942,6 +118059,7 @@
false
false
false
+ 0
13118
@@ -104950,6 +118068,7 @@
false
false
false
+ 0
13119
@@ -104958,6 +118077,7 @@
false
false
false
+ 0
13120
@@ -104966,6 +118086,7 @@
false
false
false
+ 0
13121
@@ -104974,6 +118095,7 @@
false
false
false
+ 0
13122
@@ -104982,6 +118104,7 @@
false
false
false
+ 0
13123
@@ -104990,6 +118113,7 @@
false
false
false
+ 0
13124
@@ -104998,6 +118122,7 @@
false
false
false
+ 0
13125
@@ -105006,6 +118131,7 @@
false
false
false
+ 0
13126
@@ -105014,6 +118140,7 @@
false
false
false
+ 0
13127
@@ -105022,6 +118149,7 @@
false
false
false
+ 0
13128
@@ -105030,6 +118158,7 @@
false
false
false
+ 0
13129
@@ -105038,6 +118167,7 @@
false
false
false
+ 0
13130
@@ -105046,6 +118176,7 @@
false
false
false
+ 0
13131
@@ -105054,6 +118185,7 @@
false
false
false
+ 0
13132
@@ -105062,6 +118194,7 @@
false
false
false
+ 0
13133
@@ -105070,6 +118203,7 @@
false
false
false
+ 0
13134
@@ -105078,6 +118212,7 @@
false
false
false
+ 0
13135
@@ -105086,6 +118221,7 @@
false
false
false
+ 0
13136
@@ -105094,6 +118230,7 @@
false
false
false
+ 0
13137
@@ -105102,6 +118239,7 @@
false
false
false
+ 0
13138
@@ -105110,6 +118248,7 @@
false
false
false
+ 0
13139
@@ -105118,6 +118257,7 @@
false
false
false
+ 0
13140
@@ -105126,6 +118266,7 @@
false
false
false
+ 0
13141
@@ -105134,6 +118275,7 @@
false
false
false
+ 0
13142
@@ -105142,6 +118284,7 @@
false
false
false
+ 0
13143
@@ -105150,6 +118293,7 @@
false
false
false
+ 0
13144
@@ -105158,6 +118302,7 @@
false
false
false
+ 0
13145
@@ -105166,6 +118311,7 @@
false
false
false
+ 0
13146
@@ -105174,6 +118320,7 @@
false
false
false
+ 0
13147
@@ -105182,6 +118329,7 @@
false
false
false
+ 0
13148
@@ -105190,6 +118338,7 @@
false
false
false
+ 0
13149
@@ -105198,6 +118347,7 @@
false
false
false
+ 0
13150
@@ -105206,6 +118356,7 @@
false
false
false
+ 0
13151
@@ -105214,6 +118365,7 @@
false
false
false
+ 0
13152
@@ -105222,6 +118374,7 @@
false
false
false
+ 0
13153
@@ -105230,6 +118383,7 @@
false
false
false
+ 0
13154
@@ -105238,6 +118392,7 @@
false
false
false
+ 0
13155
@@ -105246,6 +118401,7 @@
false
false
false
+ 0
13156
@@ -105254,6 +118410,7 @@
false
false
false
+ 0
13157
@@ -105262,6 +118419,7 @@
false
false
false
+ 0
13158
@@ -105270,6 +118428,7 @@
false
false
false
+ 0
13159
@@ -105278,6 +118437,7 @@
false
false
false
+ 0
13160
@@ -105286,6 +118446,7 @@
false
false
false
+ 0
13161
@@ -105294,6 +118455,7 @@
false
false
false
+ 0
13162
@@ -105302,6 +118464,7 @@
false
false
false
+ 0
13163
@@ -105310,6 +118473,7 @@
false
false
false
+ 0
13164
@@ -105318,6 +118482,7 @@
false
false
false
+ 0
13165
@@ -105326,6 +118491,7 @@
false
false
false
+ 0
13166
@@ -105334,6 +118500,7 @@
false
false
false
+ 0
13167
@@ -105342,6 +118509,7 @@
false
false
false
+ 0
13168
@@ -105350,6 +118518,7 @@
false
false
false
+ 0
13169
@@ -105358,6 +118527,7 @@
false
false
false
+ 0
13170
@@ -105366,6 +118536,7 @@
false
false
false
+ 0
13171
@@ -105374,6 +118545,7 @@
false
false
false
+ 0
13172
@@ -105382,6 +118554,7 @@
false
false
false
+ 0
13173
@@ -105390,6 +118563,7 @@
false
false
false
+ 0
13174
@@ -105398,6 +118572,7 @@
false
false
false
+ 0
13175
@@ -105406,6 +118581,7 @@
false
false
false
+ 0
13176
@@ -105414,6 +118590,7 @@
false
false
false
+ 0
13177
@@ -105422,6 +118599,7 @@
false
false
false
+ 0
13178
@@ -105430,6 +118608,7 @@
false
false
false
+ 0
13179
@@ -105438,6 +118617,7 @@
false
false
false
+ 0
13180
@@ -105446,6 +118626,7 @@
false
false
false
+ 0
13181
@@ -105454,6 +118635,7 @@
false
false
false
+ 0
13182
@@ -105462,6 +118644,7 @@
false
false
false
+ 0
13183
@@ -105470,6 +118653,7 @@
false
false
false
+ 0
13184
@@ -105478,6 +118662,7 @@
false
false
false
+ 0
13185
@@ -105486,6 +118671,7 @@
false
false
false
+ 0
13186
@@ -105494,6 +118680,7 @@
false
false
false
+ 0
13187
@@ -105502,6 +118689,7 @@
false
false
false
+ 0
13188
@@ -105510,6 +118698,7 @@
false
false
false
+ 0
13189
@@ -105518,6 +118707,7 @@
false
false
false
+ 0
13190
@@ -105526,6 +118716,7 @@
false
false
false
+ 0
13191
@@ -105534,6 +118725,7 @@
false
false
false
+ 0
13192
@@ -105542,6 +118734,7 @@
false
false
false
+ 0
13193
@@ -105550,6 +118743,7 @@
false
false
false
+ 0
13194
@@ -105558,6 +118752,7 @@
false
false
false
+ 0
13195
@@ -105566,6 +118761,7 @@
false
false
false
+ 0
13196
@@ -105574,6 +118770,7 @@
false
false
false
+ 0
13197
@@ -105582,6 +118779,7 @@
false
false
false
+ 0
13198
@@ -105590,6 +118788,7 @@
false
false
false
+ 0
13199
@@ -105598,6 +118797,7 @@
false
false
false
+ 0
13200
@@ -105606,6 +118806,7 @@
false
false
false
+ 0
13201
@@ -105614,6 +118815,7 @@
false
false
false
+ 0
13202
@@ -105622,6 +118824,7 @@
false
false
false
+ 0
13203
@@ -105630,6 +118833,7 @@
false
false
false
+ 0
13204
@@ -105638,6 +118842,7 @@
false
false
false
+ 0
13205
@@ -105646,6 +118851,7 @@
false
false
false
+ 0
13206
@@ -105654,6 +118860,7 @@
false
false
false
+ 0
13207
@@ -105662,6 +118869,7 @@
false
false
false
+ 0
13208
@@ -105670,6 +118878,7 @@
false
false
false
+ 0
13209
@@ -105678,6 +118887,7 @@
false
false
false
+ 0
13210
@@ -105686,6 +118896,7 @@
false
false
false
+ 0
13211
@@ -105694,6 +118905,7 @@
false
false
false
+ 0
13212
@@ -105702,6 +118914,7 @@
false
false
false
+ 0
13213
@@ -105710,6 +118923,7 @@
false
false
false
+ 0
13214
@@ -105718,6 +118932,7 @@
false
false
false
+ 0
13215
@@ -105726,6 +118941,7 @@
false
false
false
+ 0
13216
@@ -105734,6 +118950,7 @@
false
false
false
+ 0
13217
@@ -105742,6 +118959,7 @@
false
false
false
+ 0
13218
@@ -105750,6 +118968,7 @@
false
false
false
+ 0
13219
@@ -105758,6 +118977,7 @@
false
false
false
+ 0
13220
@@ -105766,6 +118986,7 @@
false
false
false
+ 0
13221
@@ -105774,6 +118995,7 @@
false
false
false
+ 0
13222
@@ -105782,6 +119004,7 @@
false
false
false
+ 0
13223
@@ -105790,6 +119013,7 @@
false
false
false
+ 0
13224
@@ -105798,6 +119022,7 @@
false
false
false
+ 0
13225
@@ -105806,6 +119031,7 @@
false
false
false
+ 0
13226
@@ -105814,6 +119040,7 @@
false
false
false
+ 0
13227
@@ -105822,6 +119049,7 @@
false
false
false
+ 0
13228
@@ -105830,6 +119058,7 @@
false
false
false
+ 0
13229
@@ -105838,6 +119067,7 @@
false
false
false
+ 0
13230
@@ -105846,6 +119076,7 @@
false
false
false
+ 0
13231
@@ -105854,6 +119085,7 @@
false
false
false
+ 0
13232
@@ -105862,6 +119094,7 @@
false
false
false
+ 0
13233
@@ -105870,6 +119103,7 @@
false
false
false
+ 0
13234
@@ -105878,6 +119112,7 @@
false
false
false
+ 0
13235
@@ -105886,6 +119121,7 @@
false
false
false
+ 0
13236
@@ -105894,6 +119130,7 @@
false
false
false
+ 0
13237
@@ -105902,6 +119139,7 @@
false
false
false
+ 0
13238
@@ -105910,6 +119148,7 @@
false
false
false
+ 0
13239
@@ -105918,6 +119157,7 @@
false
false
false
+ 0
13240
@@ -105926,6 +119166,7 @@
false
false
false
+ 0
13241
@@ -105934,6 +119175,7 @@
false
false
false
+ 0
13242
@@ -105942,6 +119184,7 @@
false
false
false
+ 0
13243
@@ -105950,6 +119193,7 @@
false
false
false
+ 0
13244
@@ -105958,6 +119202,7 @@
false
false
false
+ 0
13245
@@ -105966,6 +119211,7 @@
false
false
false
+ 0
13246
@@ -105974,6 +119220,7 @@
false
false
false
+ 0
13247
@@ -105982,6 +119229,7 @@
false
false
false
+ 0
13248
@@ -105990,6 +119238,7 @@
false
false
false
+ 0
13249
@@ -105998,6 +119247,7 @@
false
false
false
+ 0
13250
@@ -106006,6 +119256,7 @@
false
false
false
+ 0
13251
@@ -106014,6 +119265,7 @@
false
false
false
+ 0
13252
@@ -106022,6 +119274,7 @@
false
false
false
+ 0
13253
@@ -106030,6 +119283,7 @@
false
false
false
+ 0
13254
@@ -106038,6 +119292,7 @@
false
false
false
+ 0
13255
@@ -106046,6 +119301,7 @@
false
false
false
+ 0
13256
@@ -106054,6 +119310,7 @@
false
false
false
+ 0
13257
@@ -106062,6 +119319,7 @@
false
false
false
+ 0
13258
@@ -106070,6 +119328,7 @@
false
false
false
+ 0
13259
@@ -106078,6 +119337,7 @@
false
false
false
+ 0
13260
@@ -106086,6 +119346,7 @@
false
false
false
+ 0
13261
@@ -106094,6 +119355,7 @@
false
false
false
+ 0
13262
@@ -106102,6 +119364,7 @@
false
false
false
+ 0
13263
@@ -106110,6 +119373,7 @@
false
false
false
+ 0
13264
@@ -106118,6 +119382,7 @@
false
false
false
+ 0
13265
@@ -106126,6 +119391,7 @@
false
false
false
+ 0
13266
@@ -106134,6 +119400,7 @@
false
false
false
+ 0
13267
@@ -106142,6 +119409,7 @@
false
false
false
+ 0
13268
@@ -106150,6 +119418,7 @@
false
false
false
+ 0
13269
@@ -106158,6 +119427,7 @@
false
false
false
+ 0
13270
@@ -106166,6 +119436,7 @@
false
false
false
+ 0
13271
@@ -106174,6 +119445,7 @@
false
false
false
+ 0
13272
@@ -106182,6 +119454,7 @@
false
false
false
+ 0
13273
@@ -106190,6 +119463,7 @@
false
false
false
+ 0
13274
@@ -106198,6 +119472,7 @@
false
false
false
+ 0
13275
@@ -106206,6 +119481,7 @@
false
false
false
+ 0
13276
@@ -106214,6 +119490,7 @@
false
false
false
+ 0
13277
@@ -106222,6 +119499,7 @@
false
false
false
+ 0
13278
@@ -106230,6 +119508,7 @@
false
false
false
+ 0
13279
@@ -106238,6 +119517,7 @@
false
false
false
+ 0
13280
@@ -106246,6 +119526,7 @@
false
false
false
+ 0
13281
@@ -106254,6 +119535,7 @@
false
false
false
+ 0
13282
@@ -106262,6 +119544,7 @@
false
false
false
+ 0
13283
@@ -106270,6 +119553,7 @@
false
false
false
+ 0
13284
@@ -106278,6 +119562,7 @@
false
false
false
+ 0
13285
@@ -106286,6 +119571,7 @@
false
false
false
+ 0
13286
@@ -106294,6 +119580,7 @@
false
false
false
+ 0
13287
@@ -106302,6 +119589,7 @@
false
false
false
+ 0
13288
@@ -106310,6 +119598,7 @@
false
false
false
+ 0
13289
@@ -106318,6 +119607,7 @@
false
false
false
+ 0
13290
@@ -106326,6 +119616,7 @@
false
false
false
+ 0
13291
@@ -106334,6 +119625,7 @@
false
false
false
+ 0
13292
@@ -106342,6 +119634,7 @@
false
false
false
+ 0
13293
@@ -106350,6 +119643,7 @@
false
false
false
+ 0
13294
@@ -106358,6 +119652,7 @@
false
false
false
+ 0
13295
@@ -106366,6 +119661,7 @@
false
false
false
+ 0
13296
@@ -106374,6 +119670,7 @@
false
false
false
+ 0
13297
@@ -106382,6 +119679,7 @@
false
false
false
+ 0
13298
@@ -106390,6 +119688,7 @@
false
false
false
+ 0
13299
@@ -106398,6 +119697,7 @@
false
false
false
+ 0
13300
@@ -106406,6 +119706,7 @@
false
false
false
+ 0
13301
@@ -106414,6 +119715,7 @@
false
false
false
+ 0
13302
@@ -106422,6 +119724,7 @@
false
false
false
+ 0
13303
@@ -106430,6 +119733,7 @@
false
false
false
+ 0
13304
@@ -106438,6 +119742,7 @@
false
false
false
+ 0
13305
@@ -106446,6 +119751,7 @@
false
false
false
+ 0
13306
@@ -106454,6 +119760,7 @@
false
false
false
+ 0
13307
@@ -106462,6 +119769,7 @@
false
false
false
+ 0
13308
@@ -106470,6 +119778,7 @@
false
false
false
+ 0
13309
@@ -106478,6 +119787,7 @@
false
false
false
+ 0
13310
@@ -106486,6 +119796,7 @@
false
false
false
+ 0
13311
@@ -106494,6 +119805,7 @@
false
false
false
+ 0
13312
@@ -106502,6 +119814,7 @@
false
false
false
+ 0
13313
@@ -106510,6 +119823,7 @@
false
false
false
+ 0
13314
@@ -106518,6 +119832,7 @@
false
false
false
+ 0
13315
@@ -106526,6 +119841,7 @@
false
false
false
+ 0
13316
@@ -106534,6 +119850,7 @@
false
false
false
+ 0
13317
@@ -106542,6 +119859,7 @@
false
false
false
+ 0
13318
@@ -106550,6 +119868,7 @@
false
false
false
+ 0
13319
@@ -106558,6 +119877,7 @@
false
false
false
+ 0
13320
@@ -106566,6 +119886,7 @@
false
false
false
+ 0
13321
@@ -106574,6 +119895,7 @@
false
false
false
+ 0
13322
@@ -106582,6 +119904,7 @@
false
false
false
+ 0
13323
@@ -106590,6 +119913,7 @@
false
false
false
+ 0
13324
@@ -106598,6 +119922,7 @@
false
false
false
+ 0
13325
@@ -106606,6 +119931,7 @@
false
false
false
+ 0
13326
@@ -106614,6 +119940,7 @@
false
false
false
+ 0
13327
@@ -106622,6 +119949,7 @@
false
false
false
+ 0
13328
@@ -106630,6 +119958,7 @@
false
false
false
+ 0
13329
@@ -106638,6 +119967,7 @@
false
false
false
+ 0
13330
@@ -106646,6 +119976,7 @@
false
false
false
+ 0
13331
@@ -106654,6 +119985,7 @@
false
false
false
+ 0
13332
@@ -106662,6 +119994,7 @@
false
false
false
+ 0
13333
@@ -106670,6 +120003,7 @@
false
false
false
+ 0
13334
@@ -106678,6 +120012,7 @@
false
false
false
+ 0
13335
@@ -106686,6 +120021,7 @@
false
false
false
+ 0
13336
@@ -106694,6 +120030,7 @@
false
false
false
+ 0
13337
@@ -106702,6 +120039,7 @@
false
false
false
+ 0
13338
@@ -106710,6 +120048,7 @@
false
false
false
+ 0
13339
@@ -106718,6 +120057,7 @@
false
false
false
+ 0
13340
@@ -106726,6 +120066,7 @@
false
false
false
+ 0
13341
@@ -106734,6 +120075,7 @@
false
false
false
+ 0
13342
@@ -106742,6 +120084,7 @@
false
false
false
+ 0
13343
@@ -106750,6 +120093,7 @@
false
false
false
+ 0
13344
@@ -106758,6 +120102,7 @@
false
false
false
+ 0
13345
@@ -106766,6 +120111,7 @@
false
false
false
+ 0
13346
@@ -106774,6 +120120,7 @@
false
false
false
+ 0
13347
@@ -106782,6 +120129,7 @@
false
false
false
+ 0
13348
@@ -106790,6 +120138,7 @@
false
false
false
+ 0
13349
@@ -106798,6 +120147,7 @@
false
false
false
+ 0
13350
@@ -106806,6 +120156,7 @@
false
false
false
+ 0
13351
@@ -106814,6 +120165,7 @@
false
false
false
+ 0
13352
@@ -106822,6 +120174,7 @@
false
false
false
+ 0
13353
@@ -106830,6 +120183,7 @@
false
false
false
+ 0
13354
@@ -106838,6 +120192,7 @@
false
false
false
+ 0
13355
@@ -106846,6 +120201,7 @@
false
false
false
+ 0
13356
@@ -106854,6 +120210,7 @@
false
false
false
+ 0
13357
@@ -106862,6 +120219,7 @@
false
false
false
+ 0
13358
@@ -106870,6 +120228,7 @@
false
false
false
+ 0
13359
@@ -106878,6 +120237,7 @@
false
false
false
+ 0
13360
@@ -106886,6 +120246,7 @@
false
false
false
+ 0
13361
@@ -106894,6 +120255,7 @@
false
false
false
+ 0
13362
@@ -106902,6 +120264,7 @@
false
false
false
+ 0
13363
@@ -106910,6 +120273,7 @@
false
false
false
+ 0
13364
@@ -106918,6 +120282,7 @@
false
false
false
+ 0
13365
@@ -106926,6 +120291,7 @@
false
false
false
+ 0
13366
@@ -106934,6 +120300,7 @@
false
false
false
+ 0
13367
@@ -106942,6 +120309,7 @@
false
false
false
+ 0
13368
@@ -106950,6 +120318,7 @@
false
false
false
+ 0
13369
@@ -106958,6 +120327,7 @@
false
false
false
+ 0
13370
@@ -106966,6 +120336,7 @@
false
false
false
+ 0
13371
@@ -106974,6 +120345,7 @@
false
false
false
+ 0
13372
@@ -106982,6 +120354,7 @@
false
false
false
+ 0
13373
@@ -106990,6 +120363,7 @@
false
false
false
+ 0
13374
@@ -106998,6 +120372,7 @@
false
false
false
+ 0
13375
@@ -107006,6 +120381,7 @@
false
false
false
+ 0
13376
@@ -107014,6 +120390,7 @@
false
false
false
+ 0
13377
@@ -107022,6 +120399,7 @@
false
false
false
+ 0
13378
@@ -107030,6 +120408,7 @@
false
false
false
+ 0
13379
@@ -107038,6 +120417,7 @@
false
false
false
+ 0
13380
@@ -107046,6 +120426,7 @@
false
false
false
+ 0
13381
@@ -107054,6 +120435,7 @@
false
false
false
+ 0
13382
@@ -107062,6 +120444,7 @@
false
false
false
+ 0
13383
@@ -107070,6 +120453,7 @@
false
false
false
+ 0
13384
@@ -107078,6 +120462,7 @@
false
false
false
+ 0
13385
@@ -107086,6 +120471,7 @@
false
false
false
+ 0
13386
@@ -107094,6 +120480,7 @@
false
false
false
+ 0
13387
@@ -107102,6 +120489,7 @@
false
false
false
+ 0
13388
@@ -107110,6 +120498,7 @@
false
false
false
+ 0
13389
@@ -107118,6 +120507,7 @@
false
false
false
+ 0
13390
@@ -107126,6 +120516,7 @@
false
false
false
+ 0
13391
@@ -107134,6 +120525,7 @@
false
false
false
+ 0
13392
@@ -107142,6 +120534,7 @@
false
false
false
+ 0
13393
@@ -107150,6 +120543,7 @@
false
false
false
+ 0
13394
@@ -107158,6 +120552,7 @@
false
false
false
+ 0
13395
@@ -107166,6 +120561,7 @@
false
false
false
+ 0
13396
@@ -107174,6 +120570,7 @@
false
false
false
+ 0
13397
@@ -107182,6 +120579,7 @@
false
false
false
+ 0
13398
@@ -107190,6 +120588,7 @@
false
false
false
+ 0
13399
@@ -107198,6 +120597,7 @@
false
false
false
+ 0
13400
@@ -107206,6 +120606,7 @@
false
false
false
+ 0
13401
@@ -107214,6 +120615,7 @@
false
false
false
+ 0
13402
@@ -107222,6 +120624,7 @@
false
false
false
+ 0
13403
@@ -107230,6 +120633,7 @@
false
false
false
+ 0
13404
@@ -107238,6 +120642,7 @@
false
false
false
+ 0
13405
@@ -107246,6 +120651,7 @@
false
false
false
+ 0
13406
@@ -107254,6 +120660,7 @@
false
false
false
+ 0
13407
@@ -107262,6 +120669,7 @@
false
false
false
+ 0
13408
@@ -107270,6 +120678,7 @@
false
false
false
+ 0
13409
@@ -107278,6 +120687,7 @@
false
false
false
+ 0
13410
@@ -107286,6 +120696,7 @@
false
false
false
+ 0
13411
@@ -107294,6 +120705,7 @@
false
false
false
+ 0
13412
@@ -107302,6 +120714,7 @@
false
false
false
+ 0
13413
@@ -107310,6 +120723,7 @@
false
false
false
+ 0
13414
@@ -107318,6 +120732,7 @@
false
false
false
+ 0
13415
@@ -107326,6 +120741,7 @@
false
false
false
+ 0
13416
@@ -107334,6 +120750,7 @@
false
false
false
+ 0
13417
@@ -107342,6 +120759,7 @@
false
false
false
+ 0
13418
@@ -107350,6 +120768,7 @@
false
false
false
+ 0
13419
@@ -107358,6 +120777,7 @@
false
false
false
+ 0
13420
@@ -107366,6 +120786,7 @@
false
false
false
+ 0
13421
@@ -107374,6 +120795,7 @@
false
false
false
+ 0
13422
@@ -107382,6 +120804,7 @@
false
false
false
+ 0
13423
@@ -107390,6 +120813,7 @@
false
false
false
+ 0
13424
@@ -107398,6 +120822,7 @@
false
false
false
+ 0
13425
@@ -107406,6 +120831,7 @@
false
false
false
+ 0
13426
@@ -107414,6 +120840,7 @@
false
false
false
+ 0
13427
@@ -107422,6 +120849,7 @@
false
false
false
+ 0
13428
@@ -107430,6 +120858,7 @@
false
false
false
+ 0
13429
@@ -107438,6 +120867,7 @@
false
false
false
+ 0
13430
@@ -107446,6 +120876,7 @@
false
false
false
+ 0
13431
@@ -107454,6 +120885,7 @@
false
false
false
+ 0
13432
@@ -107462,6 +120894,7 @@
false
false
false
+ 0
13433
@@ -107470,6 +120903,7 @@
false
false
false
+ 0
13434
@@ -107478,6 +120912,7 @@
false
false
false
+ 0
13435
@@ -107486,6 +120921,7 @@
false
false
false
+ 0
13436
@@ -107494,6 +120930,7 @@
false
false
false
+ 0
13437
@@ -107502,6 +120939,7 @@
false
false
false
+ 0
13438
@@ -107510,6 +120948,7 @@
false
false
false
+ 0
13439
@@ -107518,6 +120957,7 @@
false
false
false
+ 0
13440
@@ -107526,6 +120966,7 @@
false
false
false
+ 0
13441
@@ -107534,6 +120975,7 @@
false
false
false
+ 0
13442
@@ -107542,6 +120984,7 @@
false
false
false
+ 0
13443
@@ -107550,6 +120993,7 @@
false
false
false
+ 0
13444
@@ -107558,6 +121002,7 @@
false
false
false
+ 0
13445
@@ -107566,6 +121011,7 @@
false
false
false
+ 0
13446
@@ -107574,6 +121020,7 @@
false
false
false
+ 0
13447
@@ -107582,6 +121029,7 @@
false
false
false
+ 0
13448
@@ -107590,6 +121038,7 @@
false
false
false
+ 0
13449
@@ -107598,6 +121047,7 @@
false
false
false
+ 0
13450
@@ -107606,6 +121056,7 @@
false
false
false
+ 0
13451
@@ -107614,6 +121065,7 @@
false
false
false
+ 0
13452
@@ -107622,6 +121074,7 @@
false
false
false
+ 0
13453
@@ -107630,6 +121083,7 @@
false
false
false
+ 0
13454
@@ -107638,6 +121092,7 @@
false
false
false
+ 0
13455
@@ -107646,6 +121101,7 @@
false
false
false
+ 0
13456
@@ -107654,6 +121110,7 @@
false
false
false
+ 0
13457
@@ -107662,6 +121119,7 @@
false
false
false
+ 0
13458
@@ -107670,6 +121128,7 @@
false
false
false
+ 0
13459
@@ -107678,6 +121137,7 @@
false
false
false
+ 0
13460
@@ -107686,6 +121146,7 @@
false
false
false
+ 0
13461
@@ -107694,6 +121155,7 @@
false
false
false
+ 0
13462
@@ -107702,6 +121164,7 @@
false
false
false
+ 0
13463
@@ -107710,6 +121173,7 @@
false
false
false
+ 0
13464
@@ -107718,6 +121182,7 @@
false
false
false
+ 0
13465
@@ -107726,6 +121191,7 @@
false
false
false
+ 0
13466
@@ -107734,6 +121200,7 @@
false
false
false
+ 0
13467
@@ -107742,6 +121209,7 @@
false
false
false
+ 0
13468
@@ -107750,6 +121218,7 @@
false
false
false
+ 0
13469
@@ -107758,6 +121227,7 @@
false
false
false
+ 0
13470
@@ -107766,6 +121236,7 @@
false
false
false
+ 0
13471
@@ -107774,6 +121245,7 @@
false
false
false
+ 0
13472
@@ -107782,6 +121254,7 @@
false
false
false
+ 0
13473
@@ -107790,6 +121263,7 @@
false
false
false
+ 0
13474
@@ -107798,6 +121272,7 @@
false
false
false
+ 0
13475
@@ -107806,6 +121281,7 @@
false
false
false
+ 0
13476
@@ -107814,6 +121290,7 @@
false
false
false
+ 0
13477
@@ -107822,6 +121299,7 @@
false
false
false
+ 0
13478
@@ -107830,6 +121308,7 @@
false
false
false
+ 0
13479
@@ -107838,6 +121317,7 @@
false
false
false
+ 0
13480
@@ -107846,6 +121326,7 @@
false
false
false
+ 0
13481
@@ -107854,6 +121335,7 @@
false
false
false
+ 0
13482
@@ -107862,6 +121344,7 @@
false
false
false
+ 0
13483
@@ -107870,6 +121353,7 @@
false
false
false
+ 0
13484
@@ -107878,6 +121362,7 @@
false
false
false
+ 0
13485
@@ -107886,6 +121371,7 @@
false
false
false
+ 0
13486
@@ -107894,6 +121380,7 @@
false
false
false
+ 0
13487
@@ -107902,6 +121389,7 @@
false
false
false
+ 0
13488
@@ -107910,6 +121398,7 @@
false
false
false
+ 0
13489
@@ -107918,6 +121407,7 @@
false
false
false
+ 0
13490
@@ -107926,6 +121416,7 @@
false
false
false
+ 0
13491
@@ -107934,6 +121425,7 @@
false
false
false
+ 0
13492
@@ -107942,6 +121434,7 @@
false
false
false
+ 0
13493
@@ -107950,6 +121443,7 @@
false
false
false
+ 0
13494
@@ -107958,6 +121452,7 @@
false
false
false
+ 0
13495
@@ -107966,6 +121461,7 @@
false
false
false
+ 0
13496
@@ -107974,6 +121470,7 @@
false
false
false
+ 0
13497
@@ -107982,6 +121479,7 @@
false
false
false
+ 0
13498
@@ -107990,6 +121488,7 @@
false
false
false
+ 0
13499
@@ -107998,6 +121497,7 @@
false
false
false
+ 0
13500
@@ -108006,6 +121506,7 @@
false
false
false
+ 0
13501
@@ -108014,6 +121515,7 @@
false
false
false
+ 0
13502
@@ -108022,6 +121524,7 @@
false
false
false
+ 0
13503
@@ -108030,6 +121533,7 @@
false
false
false
+ 0
13504
@@ -108038,6 +121542,7 @@
false
false
false
+ 0
13505
@@ -108046,6 +121551,7 @@
false
false
false
+ 0
13506
@@ -108054,6 +121560,7 @@
false
false
false
+ 0
13507
@@ -108062,6 +121569,7 @@
false
false
false
+ 0
13508
@@ -108070,6 +121578,7 @@
false
false
false
+ 0
13509
@@ -108078,6 +121587,7 @@
false
false
false
+ 0
13510
@@ -108086,6 +121596,7 @@
false
false
false
+ 0
13511
@@ -108094,6 +121605,7 @@
false
false
false
+ 0
13512
@@ -108102,6 +121614,7 @@
false
false
false
+ 0
13513
@@ -108110,6 +121623,7 @@
false
false
false
+ 0
13514
@@ -108118,6 +121632,7 @@
false
false
false
+ 0
13515
@@ -108126,6 +121641,7 @@
false
false
false
+ 0
13516
@@ -108134,6 +121650,7 @@
false
false
false
+ 0
13517
@@ -108142,6 +121659,7 @@
false
false
false
+ 0
13518
@@ -108150,6 +121668,7 @@
false
false
false
+ 0
13519
@@ -108158,6 +121677,7 @@
false
false
false
+ 0
13520
@@ -108166,6 +121686,7 @@
false
false
false
+ 0
13521
@@ -108174,6 +121695,7 @@
false
false
false
+ 0
13522
@@ -108182,6 +121704,7 @@
false
false
false
+ 0
13523
@@ -108190,6 +121713,7 @@
false
false
false
+ 0
13524
@@ -108198,6 +121722,7 @@
false
false
false
+ 0
13525
@@ -108206,6 +121731,7 @@
false
false
false
+ 0
13526
@@ -108214,6 +121740,7 @@
false
false
false
+ 0
13527
@@ -108222,6 +121749,7 @@
false
false
false
+ 0
13528
@@ -108230,6 +121758,7 @@
false
false
false
+ 0
13529
@@ -108238,6 +121767,7 @@
false
false
false
+ 0
13530
@@ -108246,6 +121776,7 @@
false
false
false
+ 0
13531
@@ -108254,6 +121785,7 @@
false
false
false
+ 0
13532
@@ -108262,6 +121794,7 @@
false
false
false
+ 0
13533
@@ -108270,6 +121803,7 @@
false
false
false
+ 0
13534
@@ -108278,6 +121812,7 @@
false
false
false
+ 0
13535
@@ -108286,6 +121821,7 @@
false
false
false
+ 0
13536
@@ -108294,6 +121830,7 @@
false
false
false
+ 0
13537
@@ -108302,6 +121839,7 @@
false
false
false
+ 0
13538
@@ -108310,6 +121848,7 @@
false
false
false
+ 0
13539
@@ -108318,6 +121857,7 @@
false
false
false
+ 0
13540
@@ -108326,6 +121866,7 @@
false
false
false
+ 0
13541
@@ -108334,6 +121875,7 @@
false
false
false
+ 0
13542
@@ -108342,6 +121884,7 @@
false
false
false
+ 0
13543
@@ -108350,6 +121893,7 @@
false
false
false
+ 0
13544
@@ -108358,6 +121902,7 @@
false
false
false
+ 0
13545
@@ -108366,6 +121911,7 @@
false
false
false
+ 0
13546
@@ -108374,6 +121920,7 @@
false
false
false
+ 0
13547
@@ -108382,6 +121929,7 @@
false
false
false
+ 0
13548
@@ -108390,6 +121938,7 @@
false
false
false
+ 0
13549
@@ -108398,6 +121947,7 @@
false
false
false
+ 0
13550
@@ -108406,6 +121956,7 @@
false
false
false
+ 0
13551
@@ -108414,6 +121965,7 @@
false
false
false
+ 0
13552
@@ -108422,6 +121974,7 @@
false
false
false
+ 0
13553
@@ -108430,6 +121983,7 @@
false
false
false
+ 0
13554
@@ -108438,6 +121992,7 @@
false
false
false
+ 0
13555
@@ -108446,6 +122001,7 @@
false
false
false
+ 0
13556
@@ -108454,6 +122010,7 @@
false
false
false
+ 0
13557
@@ -108462,6 +122019,7 @@
false
false
false
+ 0
13558
@@ -108470,6 +122028,7 @@
false
false
false
+ 0
13559
@@ -108478,6 +122037,7 @@
false
false
false
+ 0
13560
@@ -108486,6 +122046,7 @@
false
false
false
+ 0
13561
@@ -108494,6 +122055,7 @@
false
false
false
+ 0
13562
@@ -108502,6 +122064,7 @@
false
false
false
+ 0
13563
@@ -108510,6 +122073,7 @@
false
false
false
+ 0
13564
@@ -108518,6 +122082,7 @@
false
false
false
+ 0
13565
@@ -108526,6 +122091,7 @@
false
false
false
+ 0
13566
@@ -108534,6 +122100,7 @@
false
false
false
+ 0
13567
@@ -108542,6 +122109,7 @@
false
false
false
+ 0
13568
@@ -108550,6 +122118,7 @@
false
false
false
+ 0
13569
@@ -108558,6 +122127,7 @@
false
false
false
+ 0
13570
@@ -108566,6 +122136,7 @@
false
false
false
+ 0
13571
@@ -108574,6 +122145,7 @@
false
false
false
+ 0
13572
@@ -108582,6 +122154,7 @@
false
false
false
+ 0
13573
@@ -108590,6 +122163,7 @@
false
false
false
+ 0
13574
@@ -108598,6 +122172,7 @@
false
false
false
+ 0
13575
@@ -108606,6 +122181,7 @@
false
false
false
+ 0
13576
@@ -108614,6 +122190,7 @@
false
false
false
+ 0
13577
@@ -108622,6 +122199,7 @@
false
false
false
+ 0
13578
@@ -108630,6 +122208,7 @@
false
false
false
+ 0
13579
@@ -108638,6 +122217,7 @@
false
false
false
+ 0
13580
@@ -108646,6 +122226,7 @@
false
false
false
+ 0
13581
@@ -108654,6 +122235,7 @@
false
false
false
+ 0
13582
@@ -108662,6 +122244,7 @@
false
false
false
+ 0
13583
@@ -108670,6 +122253,7 @@
false
false
false
+ 0
13584
@@ -108678,6 +122262,7 @@
false
false
false
+ 0
13585
@@ -108686,6 +122271,7 @@
false
false
false
+ 0
13586
@@ -108694,6 +122280,7 @@
false
false
false
+ 0
13587
@@ -108702,6 +122289,7 @@
false
false
false
+ 0
13588
@@ -108710,6 +122298,7 @@
false
false
false
+ 0
13589
@@ -108718,6 +122307,7 @@
false
false
false
+ 0
13590
@@ -108726,6 +122316,7 @@
false
false
false
+ 0
13591
@@ -108734,6 +122325,7 @@
false
false
false
+ 0
13592
@@ -108742,6 +122334,7 @@
false
false
false
+ 0
13593
@@ -108750,6 +122343,7 @@
false
false
false
+ 0
13594
@@ -108758,6 +122352,7 @@
false
false
false
+ 0
13595
@@ -108766,6 +122361,7 @@
false
false
false
+ 0
13596
@@ -108774,6 +122370,7 @@
false
false
false
+ 0
13597
@@ -108782,6 +122379,7 @@
false
false
false
+ 0
13598
@@ -108790,6 +122388,7 @@
false
false
false
+ 0
13599
@@ -108798,6 +122397,7 @@
false
false
false
+ 0
13600
@@ -108806,6 +122406,7 @@
false
false
false
+ 0
13601
@@ -108814,6 +122415,7 @@
false
false
false
+ 0
13602
@@ -108822,6 +122424,7 @@
false
false
false
+ 0
13603
@@ -108830,6 +122433,7 @@
false
false
false
+ 0
13604
@@ -108838,6 +122442,7 @@
false
false
false
+ 0
13605
@@ -108846,6 +122451,7 @@
false
false
false
+ 0
13606
@@ -108854,6 +122460,7 @@
false
false
false
+ 0
13607
@@ -108862,6 +122469,7 @@
false
false
false
+ 0
13608
@@ -108870,6 +122478,7 @@
false
false
false
+ 0
13609
@@ -108878,6 +122487,7 @@
false
false
false
+ 0
13610
@@ -108886,6 +122496,7 @@
false
false
false
+ 0
13611
@@ -108894,6 +122505,7 @@
false
false
false
+ 0
13612
@@ -108902,6 +122514,7 @@
false
false
false
+ 0
13613
@@ -108910,6 +122523,7 @@
false
false
false
+ 0
13614
@@ -108918,6 +122532,7 @@
false
false
false
+ 0
13615
@@ -108926,6 +122541,7 @@
false
false
false
+ 0
13616
@@ -108934,6 +122550,7 @@
false
false
false
+ 0
13617
@@ -108942,6 +122559,7 @@
false
false
false
+ 0
13618
@@ -108950,6 +122568,7 @@
false
false
false
+ 0
13619
@@ -108958,6 +122577,7 @@
false
false
false
+ 0
13620
@@ -108966,6 +122586,7 @@
false
false
false
+ 0
13621
@@ -108974,6 +122595,7 @@
false
false
false
+ 0
13622
@@ -108982,6 +122604,7 @@
false
false
false
+ 0
13623
@@ -108990,6 +122613,7 @@
false
false
false
+ 0
13624
@@ -108998,6 +122622,7 @@
false
false
false
+ 0
13625
@@ -109006,6 +122631,7 @@
false
false
false
+ 0
13626
@@ -109014,6 +122640,7 @@
false
false
false
+ 0
13627
@@ -109022,6 +122649,7 @@
false
false
false
+ 0
13628
@@ -109030,6 +122658,7 @@
false
false
false
+ 0
13629
@@ -109038,6 +122667,7 @@
false
false
false
+ 0
13630
@@ -109046,6 +122676,7 @@
false
false
false
+ 0
13631
@@ -109054,6 +122685,7 @@
false
false
false
+ 0
13632
@@ -109062,6 +122694,7 @@
false
false
false
+ 0
13633
@@ -109070,6 +122703,7 @@
false
false
false
+ 0
13634
@@ -109078,6 +122712,7 @@
false
false
false
+ 0
13635
@@ -109086,6 +122721,7 @@
false
false
false
+ 0
13636
@@ -109094,6 +122730,7 @@
false
false
false
+ 0
13637
@@ -109102,6 +122739,7 @@
false
false
false
+ 0
13638
@@ -109110,6 +122748,7 @@
false
false
false
+ 0
13639
@@ -109118,6 +122757,7 @@
false
false
false
+ 0
13640
@@ -109126,6 +122766,7 @@
false
false
false
+ 0
13641
@@ -109134,6 +122775,7 @@
false
false
false
+ 0
13642
@@ -109142,6 +122784,7 @@
false
false
false
+ 0
13643
@@ -109150,6 +122793,7 @@
false
false
false
+ 0
13644
@@ -109158,6 +122802,7 @@
false
false
false
+ 0
13645
@@ -109166,6 +122811,7 @@
false
false
false
+ 0
13646
@@ -109174,6 +122820,7 @@
false
false
false
+ 0
13647
@@ -109182,6 +122829,7 @@
false
false
false
+ 0
13648
@@ -109190,6 +122838,7 @@
false
false
false
+ 0
13649
@@ -109198,6 +122847,7 @@
false
false
false
+ 0
13650
@@ -109206,6 +122856,7 @@
false
false
false
+ 0
13651
@@ -109214,6 +122865,7 @@
false
false
false
+ 0
13652
@@ -109222,6 +122874,7 @@
false
false
false
+ 0
13653
@@ -109230,6 +122883,7 @@
false
false
false
+ 0
13654
@@ -109238,6 +122892,7 @@
false
false
false
+ 0
13655
@@ -109246,6 +122901,7 @@
false
false
false
+ 0
13656
@@ -109254,6 +122910,7 @@
false
false
false
+ 0
13657
@@ -109262,6 +122919,7 @@
false
false
false
+ 0
13658
@@ -109270,6 +122928,7 @@
false
false
false
+ 0
13659
@@ -109278,6 +122937,7 @@
false
false
false
+ 0
13660
@@ -109286,6 +122946,7 @@
false
false
false
+ 0
13661
@@ -109294,6 +122955,7 @@
false
false
false
+ 0
13662
@@ -109302,6 +122964,7 @@
false
false
false
+ 0
13663
@@ -109310,6 +122973,7 @@
false
false
false
+ 0
13664
@@ -109318,6 +122982,7 @@
false
false
false
+ 0
13665
@@ -109326,6 +122991,7 @@
false
false
false
+ 0
13666
@@ -109334,6 +123000,7 @@
false
false
false
+ 0
13667
@@ -109342,6 +123009,7 @@
false
false
false
+ 0
13668
@@ -109350,6 +123018,7 @@
false
false
false
+ 0
13669
@@ -109358,6 +123027,7 @@
false
false
false
+ 0
13670
@@ -109366,6 +123036,7 @@
false
false
false
+ 0
13671
@@ -109374,6 +123045,7 @@
false
false
false
+ 0
13672
@@ -109382,6 +123054,7 @@
false
false
false
+ 0
13673
@@ -109390,6 +123063,7 @@
false
false
false
+ 0
13674
@@ -109398,6 +123072,7 @@
false
false
false
+ 0
13675
@@ -109406,6 +123081,7 @@
false
false
false
+ 0
13676
@@ -109414,6 +123090,7 @@
false
false
false
+ 0
13677
@@ -109422,6 +123099,7 @@
false
false
false
+ 0
13678
@@ -109430,6 +123108,7 @@
false
false
false
+ 0
13679
@@ -109438,6 +123117,7 @@
false
false
false
+ 0
13680
@@ -109446,6 +123126,7 @@
false
false
false
+ 0
13681
@@ -109454,6 +123135,7 @@
false
false
false
+ 0
13682
@@ -109462,6 +123144,7 @@
false
false
false
+ 0
13683
@@ -109470,6 +123153,7 @@
false
false
false
+ 0
13684
@@ -109478,6 +123162,7 @@
false
false
false
+ 0
13685
@@ -109486,6 +123171,7 @@
false
false
false
+ 0
13686
@@ -109494,6 +123180,7 @@
false
false
false
+ 0
13687
@@ -109502,6 +123189,7 @@
false
false
false
+ 0
13688
@@ -109510,6 +123198,7 @@
false
false
false
+ 0
13689
@@ -109518,6 +123207,7 @@
false
false
false
+ 0
13690
@@ -109526,6 +123216,7 @@
false
false
false
+ 0
13691
@@ -109534,6 +123225,7 @@
false
false
false
+ 0
13692
@@ -109542,6 +123234,7 @@
false
false
false
+ 0
13693
@@ -109550,6 +123243,7 @@
false
false
false
+ 0
13694
@@ -109558,6 +123252,7 @@
false
false
false
+ 0
13695
@@ -109566,6 +123261,7 @@
false
false
false
+ 0
13696
@@ -109574,6 +123270,7 @@
false
false
false
+ 0
13697
@@ -109582,6 +123279,7 @@
false
false
false
+ 0
13698
@@ -109590,6 +123288,7 @@
false
false
false
+ 0
13699
@@ -109598,6 +123297,7 @@
false
false
false
+ 0
13700
@@ -109606,6 +123306,7 @@
false
false
false
+ 0
13701
@@ -109614,6 +123315,7 @@
false
false
false
+ 0
13702
@@ -109622,6 +123324,7 @@
false
false
false
+ 0
13703
@@ -109630,6 +123333,7 @@
false
false
false
+ 0
13704
@@ -109638,6 +123342,7 @@
false
false
false
+ 0
13705
@@ -109646,6 +123351,7 @@
false
false
false
+ 0
13706
@@ -109654,6 +123360,7 @@
false
false
false
+ 0
13707
@@ -109662,6 +123369,7 @@
false
false
false
+ 0
13708
@@ -109670,6 +123378,7 @@
false
false
false
+ 0
13709
@@ -109678,6 +123387,7 @@
false
false
false
+ 0
13710
@@ -109686,6 +123396,7 @@
false
false
false
+ 0
13711
@@ -109694,6 +123405,7 @@
false
false
false
+ 0
13712
@@ -109702,6 +123414,7 @@
false
false
false
+ 0
13713
@@ -109710,6 +123423,7 @@
false
false
false
+ 0
13714
@@ -109718,6 +123432,7 @@
false
false
false
+ 0
13715
@@ -109726,6 +123441,7 @@
false
false
false
+ 0
13716
@@ -109734,6 +123450,7 @@
false
false
false
+ 0
13717
@@ -109742,6 +123459,7 @@
false
false
false
+ 0
13718
@@ -109750,6 +123468,7 @@
false
false
false
+ 0
13719
@@ -109758,6 +123477,7 @@
false
false
false
+ 0
13720
@@ -109766,6 +123486,7 @@
false
false
false
+ 0
13721
@@ -109774,6 +123495,7 @@
false
false
false
+ 0
13722
@@ -109782,6 +123504,7 @@
false
false
false
+ 0
13723
@@ -109790,6 +123513,7 @@
false
false
false
+ 0
13724
@@ -109798,6 +123522,7 @@
false
false
false
+ 0
13725
@@ -109806,6 +123531,7 @@
false
false
false
+ 0
13726
@@ -109814,6 +123540,7 @@
false
false
false
+ 0
13727
@@ -109822,6 +123549,7 @@
false
false
false
+ 0
13728
@@ -109830,6 +123558,7 @@
false
false
false
+ 0
13729
@@ -109838,6 +123567,7 @@
false
false
false
+ 0
13730
@@ -109846,6 +123576,7 @@
false
false
false
+ 0
13731
@@ -109854,6 +123585,7 @@
false
false
false
+ 0
13732
@@ -109862,6 +123594,7 @@
false
false
false
+ 0
13733
@@ -109870,6 +123603,7 @@
false
false
false
+ 0
13734
@@ -109878,6 +123612,7 @@
false
false
false
+ 0
13735
@@ -109886,6 +123621,7 @@
false
false
false
+ 0
13736
@@ -109894,6 +123630,7 @@
false
false
false
+ 0
13737
@@ -109902,6 +123639,7 @@
false
false
false
+ 0
13738
@@ -109910,6 +123648,7 @@
false
false
false
+ 0
13739
@@ -109918,6 +123657,7 @@
false
false
false
+ 0
13740
@@ -109926,6 +123666,7 @@
false
false
false
+ 0
13741
@@ -109934,6 +123675,7 @@
false
false
false
+ 0
13742
@@ -109942,6 +123684,7 @@
false
false
false
+ 0
13743
@@ -109950,6 +123693,7 @@
false
false
false
+ 0
13744
@@ -109958,6 +123702,7 @@
false
false
false
+ 0
13745
@@ -109966,6 +123711,7 @@
false
false
false
+ 0
13746
@@ -109974,6 +123720,7 @@
false
false
false
+ 0
13747
@@ -109982,6 +123729,7 @@
false
false
false
+ 0
13748
@@ -109990,6 +123738,7 @@
false
false
false
+ 0
13749
@@ -109998,6 +123747,7 @@
false
false
false
+ 0
13750
@@ -110006,6 +123756,7 @@
false
false
false
+ 0
13751
@@ -110014,6 +123765,7 @@
false
false
false
+ 0
13752
@@ -110022,6 +123774,7 @@
false
false
false
+ 0
13753
@@ -110030,6 +123783,7 @@
false
false
false
+ 0
13754
@@ -110038,6 +123792,7 @@
false
false
false
+ 0
13755
@@ -110046,6 +123801,7 @@
false
false
false
+ 0
13756
@@ -110054,6 +123810,7 @@
false
false
false
+ 0
13757
@@ -110062,6 +123819,7 @@
false
false
false
+ 0
13758
@@ -110070,6 +123828,7 @@
false
false
false
+ 0
13759
@@ -110078,6 +123837,7 @@
false
false
false
+ 0
13760
@@ -110086,6 +123846,7 @@
false
false
false
+ 0
13761
@@ -110094,6 +123855,7 @@
false
false
false
+ 0
13762
@@ -110102,6 +123864,7 @@
false
false
false
+ 0
13763
@@ -110110,6 +123873,7 @@
false
false
false
+ 0
13764
@@ -110118,6 +123882,7 @@
false
false
false
+ 0
13765
@@ -110126,6 +123891,7 @@
false
false
false
+ 0
13766
@@ -110134,6 +123900,7 @@
false
false
false
+ 0
13767
@@ -110142,6 +123909,7 @@
false
false
false
+ 0
13768
@@ -110150,6 +123918,7 @@
false
false
false
+ 0
13769
@@ -110158,6 +123927,7 @@
false
false
false
+ 0
13770
@@ -110166,6 +123936,7 @@
false
false
false
+ 0
13771
@@ -110174,6 +123945,7 @@
false
false
false
+ 0
13772
@@ -110182,6 +123954,7 @@
false
false
false
+ 0
13773
@@ -110190,6 +123963,7 @@
false
false
false
+ 0
13774
@@ -110198,6 +123972,7 @@
false
false
false
+ 0
13775
@@ -110206,6 +123981,7 @@
false
false
false
+ 0
13776
@@ -110214,6 +123990,7 @@
false
false
false
+ 0
13777
@@ -110222,6 +123999,7 @@
false
false
false
+ 0
13778
@@ -110230,6 +124008,7 @@
false
false
false
+ 0
13779
@@ -110238,6 +124017,7 @@
false
false
false
+ 0
13780
@@ -110246,6 +124026,7 @@
false
false
false
+ 0
13781
@@ -110254,6 +124035,7 @@
false
false
false
+ 0
13782
@@ -110262,6 +124044,7 @@
false
false
false
+ 0
13783
@@ -110270,6 +124053,7 @@
false
false
false
+ 0
13784
@@ -110278,6 +124062,7 @@
false
false
false
+ 0
13785
@@ -110286,6 +124071,7 @@
false
false
false
+ 0
13786
@@ -110294,6 +124080,7 @@
false
false
false
+ 0
13787
@@ -110302,6 +124089,7 @@
false
false
false
+ 0
13788
@@ -110310,6 +124098,7 @@
false
false
false
+ 0
13789
@@ -110318,6 +124107,7 @@
false
false
false
+ 0
13790
@@ -110326,6 +124116,7 @@
false
false
false
+ 0
13791
@@ -110334,6 +124125,7 @@
false
false
false
+ 0
13792
@@ -110342,6 +124134,7 @@
false
false
false
+ 0
13793
@@ -110350,6 +124143,7 @@
false
false
false
+ 0
13794
@@ -110358,6 +124152,7 @@
false
false
false
+ 0
13795
@@ -110366,6 +124161,7 @@
false
false
false
+ 0
13796
@@ -110374,6 +124170,7 @@
false
false
false
+ 0
13797
@@ -110382,6 +124179,7 @@
false
false
false
+ 0
13798
@@ -110390,6 +124188,7 @@
false
false
false
+ 0
13799
@@ -110398,6 +124197,7 @@
false
false
false
+ 0
13800
@@ -110406,6 +124206,7 @@
false
false
false
+ 0
13801
@@ -110414,6 +124215,7 @@
false
false
false
+ 0
13802
@@ -110422,6 +124224,7 @@
false
false
false
+ 0
13803
@@ -110430,6 +124233,7 @@
false
false
false
+ 0
13804
@@ -110438,6 +124242,7 @@
false
false
false
+ 0
13805
@@ -110446,6 +124251,7 @@
false
false
false
+ 0
13806
@@ -110454,6 +124260,7 @@
false
false
false
+ 0
13807
@@ -110462,6 +124269,7 @@
false
false
false
+ 0
13808
@@ -110470,6 +124278,7 @@
false
false
false
+ 0
13809
@@ -110478,6 +124287,7 @@
false
false
false
+ 0
13810
@@ -110486,6 +124296,7 @@
false
false
false
+ 0
13811
@@ -110494,6 +124305,7 @@
false
false
false
+ 0
13812
@@ -110502,6 +124314,7 @@
false
false
false
+ 0
13813
@@ -110510,6 +124323,7 @@
false
false
false
+ 0
13814
@@ -110518,6 +124332,7 @@
false
false
false
+ 0
13815
@@ -110526,6 +124341,7 @@
false
false
false
+ 0
13816
@@ -110534,6 +124350,7 @@
false
false
false
+ 0
13817
@@ -110542,6 +124359,7 @@
false
false
false
+ 0
13818
@@ -110550,6 +124368,7 @@
false
false
false
+ 0
13819
@@ -110558,6 +124377,7 @@
false
false
false
+ 0
13820
@@ -110566,6 +124386,7 @@
false
false
false
+ 0
13821
@@ -110574,6 +124395,7 @@
false
false
false
+ 0
13822
@@ -110582,6 +124404,7 @@
false
false
false
+ 0
13823
@@ -110590,6 +124413,7 @@
false
false
false
+ 0
13824
@@ -110598,6 +124422,7 @@
false
false
false
+ 0
13825
@@ -110606,6 +124431,7 @@
false
false
false
+ 0
13826
@@ -110614,6 +124440,7 @@
false
false
false
+ 0
13827
@@ -110622,6 +124449,7 @@
false
false
false
+ 0
13828
@@ -110630,6 +124458,7 @@
false
false
false
+ 0
13829
@@ -110638,6 +124467,7 @@
false
false
false
+ 0
13830
@@ -110646,6 +124476,7 @@
false
false
false
+ 0
13831
@@ -110654,6 +124485,7 @@
false
false
false
+ 0
13832
@@ -110662,6 +124494,7 @@
false
false
false
+ 0
13833
@@ -110670,6 +124503,7 @@
false
false
false
+ 0
13834
@@ -110678,6 +124512,7 @@
false
false
false
+ 0
13835
@@ -110686,6 +124521,7 @@
false
false
false
+ 0
13836
@@ -110694,6 +124530,7 @@
false
false
false
+ 0
13837
@@ -110702,6 +124539,7 @@
false
false
false
+ 0
13838
@@ -110710,6 +124548,7 @@
false
false
false
+ 0
13839
@@ -110718,6 +124557,7 @@
false
false
false
+ 0
13840
@@ -110726,6 +124566,7 @@
false
false
false
+ 0
13841
@@ -110734,6 +124575,7 @@
false
false
false
+ 0
13842
@@ -110742,6 +124584,7 @@
false
false
false
+ 0
13843
@@ -110750,6 +124593,7 @@
false
false
false
+ 0
13844
@@ -110758,6 +124602,7 @@
false
false
false
+ 0
13845
@@ -110766,6 +124611,7 @@
false
false
false
+ 0
13846
@@ -110774,6 +124620,7 @@
false
false
false
+ 0
13847
@@ -110782,6 +124629,7 @@
false
false
false
+ 0
13848
@@ -110790,6 +124638,7 @@
false
false
false
+ 0
13849
@@ -110798,6 +124647,7 @@
false
false
false
+ 0
13850
@@ -110806,6 +124656,7 @@
false
false
false
+ 0
13851
@@ -110814,6 +124665,7 @@
false
false
false
+ 0
13852
@@ -110822,6 +124674,7 @@
false
false
false
+ 0
13853
@@ -110830,6 +124683,7 @@
false
false
false
+ 0
13854
@@ -110838,6 +124692,7 @@
false
false
false
+ 0
13855
@@ -110846,6 +124701,7 @@
false
false
false
+ 0
13856
@@ -110854,6 +124710,7 @@
false
false
false
+ 0
13857
@@ -110862,6 +124719,7 @@
false
false
false
+ 0
13858
@@ -110870,6 +124728,7 @@
false
false
false
+ 0
13859
@@ -110878,6 +124737,7 @@
false
false
false
+ 0
13860
@@ -110886,6 +124746,7 @@
false
false
false
+ 0
13861
@@ -110894,6 +124755,7 @@
false
false
false
+ 0
13862
@@ -110902,6 +124764,7 @@
false
false
false
+ 0
13863
@@ -110910,6 +124773,7 @@
false
false
false
+ 0
13864
@@ -110918,6 +124782,7 @@
false
false
false
+ 0
13865
@@ -110926,6 +124791,7 @@
false
false
false
+ 0
13866
@@ -110934,6 +124800,7 @@
false
false
false
+ 0
13867
@@ -110942,6 +124809,7 @@
false
false
false
+ 0
13868
@@ -110950,6 +124818,7 @@
false
false
false
+ 0
13869
@@ -110958,6 +124827,7 @@
false
false
false
+ 0
13870
@@ -110966,6 +124836,7 @@
false
false
false
+ 0
13871
@@ -110974,6 +124845,7 @@
false
false
false
+ 0
13872
@@ -110982,6 +124854,7 @@
false
false
false
+ 0
13873
@@ -110990,6 +124863,7 @@
false
false
false
+ 0
13874
@@ -110998,6 +124872,7 @@
false
false
false
+ 0
13875
@@ -111006,6 +124881,7 @@
false
false
false
+ 0
13876
@@ -111014,6 +124890,7 @@
false
false
false
+ 0
13877
@@ -111022,6 +124899,7 @@
false
false
false
+ 0
13878
@@ -111030,6 +124908,7 @@
false
false
false
+ 0
13879
@@ -111038,6 +124917,7 @@
false
false
false
+ 0
13880
@@ -111046,6 +124926,7 @@
false
false
false
+ 0
13881
@@ -111054,6 +124935,7 @@
false
false
false
+ 0
13882
@@ -111062,6 +124944,7 @@
false
false
false
+ 0
13883
@@ -111070,6 +124953,7 @@
false
false
false
+ 0
13884
@@ -111078,6 +124962,7 @@
false
false
false
+ 0
13885
@@ -111086,6 +124971,7 @@
false
false
false
+ 0
13886
@@ -111094,6 +124980,7 @@
false
false
false
+ 0
13887
@@ -111102,6 +124989,7 @@
false
false
false
+ 0
13888
@@ -111110,6 +124998,7 @@
false
false
false
+ 0
13889
@@ -111118,6 +125007,7 @@
false
false
false
+ 0
13890
@@ -111126,6 +125016,7 @@
false
false
false
+ 0
13891
@@ -111134,6 +125025,7 @@
false
false
false
+ 0
13892
@@ -111142,6 +125034,7 @@
false
false
false
+ 0
13893
@@ -111150,6 +125043,7 @@
false
false
false
+ 0
13894
@@ -111158,6 +125052,7 @@
false
false
false
+ 0
13895
@@ -111166,6 +125061,7 @@
false
false
false
+ 0
13896
@@ -111174,6 +125070,7 @@
false
false
false
+ 0
13897
@@ -111182,6 +125079,7 @@
false
false
false
+ 0
13898
@@ -111190,6 +125088,7 @@
false
false
false
+ 0
13899
@@ -111198,6 +125097,7 @@
false
false
false
+ 0
13900
@@ -111206,6 +125106,7 @@
false
false
false
+ 0
13901
@@ -111214,6 +125115,7 @@
false
false
false
+ 0
13902
@@ -111222,6 +125124,7 @@
false
false
false
+ 0
13903
@@ -111230,6 +125133,7 @@
false
false
false
+ 0
13904
@@ -111238,6 +125142,7 @@
false
false
false
+ 0
13905
@@ -111246,6 +125151,7 @@
false
false
false
+ 0
13906
@@ -111254,6 +125160,7 @@
false
false
false
+ 0
13907
@@ -111262,6 +125169,7 @@
false
false
false
+ 0
13908
@@ -111270,6 +125178,7 @@
false
false
false
+ 0
13909
@@ -111278,6 +125187,7 @@
false
false
false
+ 0
13910
@@ -111286,6 +125196,7 @@
false
false
false
+ 0
13911
@@ -111294,6 +125205,7 @@
false
false
false
+ 0
13912
@@ -111302,6 +125214,7 @@
false
false
false
+ 0
13913
@@ -111310,6 +125223,7 @@
false
false
false
+ 0
13914
@@ -111318,6 +125232,7 @@
false
false
false
+ 0
13915
@@ -111326,6 +125241,7 @@
false
false
false
+ 0
13916
@@ -111334,6 +125250,7 @@
false
false
false
+ 0
13917
@@ -111342,6 +125259,7 @@
false
false
false
+ 0
13918
@@ -111350,6 +125268,7 @@
false
false
false
+ 0
13919
@@ -111358,6 +125277,7 @@
false
false
false
+ 0
13920
@@ -111366,6 +125286,7 @@
false
false
false
+ 0
13921
@@ -111374,6 +125295,7 @@
false
false
false
+ 0
13922
@@ -111382,6 +125304,7 @@
false
false
false
+ 0
13923
@@ -111390,6 +125313,7 @@
false
false
false
+ 0
13924
@@ -111398,6 +125322,7 @@
false
false
false
+ 0
13925
@@ -111406,6 +125331,7 @@
false
false
false
+ 0
13926
@@ -111414,6 +125340,7 @@
false
false
false
+ 0
13927
@@ -111422,6 +125349,7 @@
false
false
false
+ 0
13928
@@ -111430,6 +125358,7 @@
false
false
false
+ 0
13929
@@ -111438,6 +125367,7 @@
false
false
false
+ 0
13930
@@ -111446,6 +125376,7 @@
false
false
false
+ 0
13931
@@ -111454,6 +125385,7 @@
false
false
false
+ 0
13932
@@ -111462,6 +125394,7 @@
false
false
false
+ 0
13933
@@ -111470,6 +125403,7 @@
false
false
false
+ 0
13934
@@ -111478,6 +125412,7 @@
false
false
false
+ 0
13935
@@ -111486,6 +125421,7 @@
false
false
false
+ 0
13936
@@ -111494,6 +125430,7 @@
false
false
false
+ 0
13937
@@ -111502,6 +125439,7 @@
false
false
false
+ 0
13938
@@ -111510,6 +125448,7 @@
false
false
false
+ 0
13939
@@ -111518,6 +125457,7 @@
false
false
false
+ 0
13940
@@ -111526,6 +125466,7 @@
false
false
false
+ 0
13941
@@ -111534,6 +125475,7 @@
false
false
false
+ 0
13942
@@ -111542,6 +125484,7 @@
false
false
false
+ 0
13943
@@ -111550,6 +125493,7 @@
false
false
false
+ 0
13944
@@ -111558,6 +125502,7 @@
false
false
false
+ 0
13945
@@ -111566,6 +125511,7 @@
false
false
false
+ 0
13946
@@ -111574,6 +125520,7 @@
false
false
false
+ 0
13947
@@ -111582,6 +125529,7 @@
false
false
false
+ 0
13948
@@ -111590,6 +125538,7 @@
false
false
false
+ 0
13949
@@ -111598,6 +125547,7 @@
false
false
false
+ 0
13950
@@ -111606,6 +125556,7 @@
false
false
false
+ 0
13951
@@ -111614,6 +125565,7 @@
false
false
false
+ 0
13952
@@ -111622,6 +125574,7 @@
false
false
false
+ 0
13953
@@ -111630,6 +125583,7 @@
false
false
false
+ 0
13954
@@ -111638,6 +125592,7 @@
false
false
false
+ 0
13955
@@ -111646,6 +125601,7 @@
false
false
false
+ 0
13956
@@ -111654,6 +125610,7 @@
false
false
false
+ 0
13957
@@ -111662,6 +125619,7 @@
false
false
false
+ 0
13958
@@ -111670,6 +125628,7 @@
false
false
false
+ 0
13959
@@ -111678,6 +125637,7 @@
false
false
false
+ 0
13960
@@ -111686,6 +125646,7 @@
false
false
false
+ 0
13961
@@ -111694,6 +125655,7 @@
false
false
false
+ 0
13962
@@ -111702,6 +125664,7 @@
false
false
false
+ 0
13963
@@ -111710,6 +125673,7 @@
false
false
false
+ 0
13964
@@ -111718,6 +125682,7 @@
false
false
false
+ 0
13965
@@ -111726,6 +125691,7 @@
false
false
false
+ 0
13966
@@ -111734,6 +125700,7 @@
false
false
false
+ 0
13967
@@ -111742,6 +125709,7 @@
false
false
false
+ 0
13968
@@ -111750,6 +125718,7 @@
false
false
false
+ 0
13969
@@ -111758,6 +125727,7 @@
false
false
false
+ 0
13970
@@ -111766,6 +125736,7 @@
false
false
false
+ 0
13971
@@ -111774,6 +125745,7 @@
false
false
false
+ 0
13972
@@ -111782,6 +125754,7 @@
false
false
false
+ 0
13973
@@ -111790,6 +125763,7 @@
false
false
false
+ 0
13974
@@ -111798,6 +125772,7 @@
false
false
false
+ 0
13975
@@ -111806,6 +125781,7 @@
false
false
false
+ 0
13976
@@ -111814,6 +125790,7 @@
false
false
false
+ 0
13977
@@ -111822,6 +125799,7 @@
false
false
false
+ 0
13978
@@ -111830,6 +125808,7 @@
false
false
false
+ 0
13979
@@ -111838,6 +125817,7 @@
false
false
false
+ 0
13980
@@ -111846,6 +125826,7 @@
false
false
false
+ 0
13981
@@ -111854,6 +125835,7 @@
false
false
false
+ 0
13982
@@ -111862,6 +125844,7 @@
false
false
false
+ 0
13983
@@ -111870,6 +125853,7 @@
false
false
false
+ 0
13984
@@ -111878,6 +125862,7 @@
false
false
false
+ 0
13985
@@ -111886,6 +125871,7 @@
false
false
false
+ 0
13986
@@ -111894,6 +125880,7 @@
false
false
false
+ 0
13987
@@ -111902,6 +125889,7 @@
false
false
false
+ 0
13988
@@ -111910,6 +125898,7 @@
false
false
false
+ 0
13989
@@ -111918,6 +125907,7 @@
false
false
false
+ 0
13990
@@ -111926,6 +125916,7 @@
false
false
false
+ 0
13991
@@ -111934,6 +125925,7 @@
false
false
false
+ 0
13992
@@ -111942,6 +125934,7 @@
false
false
false
+ 0
13993
@@ -111950,6 +125943,7 @@
false
false
false
+ 0
13994
@@ -111958,6 +125952,7 @@
false
false
false
+ 0
13995
@@ -111966,6 +125961,7 @@
false
false
false
+ 0
13996
@@ -111974,6 +125970,7 @@
false
false
false
+ 0
13997
@@ -111982,6 +125979,7 @@
false
false
false
+ 0
13998
@@ -111990,6 +125988,7 @@
false
false
false
+ 0
13999
@@ -111998,6 +125997,7 @@
false
false
false
+ 0
14000
@@ -112006,6 +126006,7 @@
false
false
false
+ 0
14001
@@ -112014,6 +126015,7 @@
false
false
false
+ 0
14002
@@ -112022,6 +126024,7 @@
false
false
false
+ 0
14003
@@ -112030,6 +126033,7 @@
false
false
false
+ 0
14004
@@ -112038,6 +126042,7 @@
false
false
false
+ 0
14005
@@ -112046,6 +126051,7 @@
false
false
false
+ 0
14006
@@ -112054,6 +126060,7 @@
false
false
false
+ 0
14007
@@ -112062,6 +126069,7 @@
false
false
false
+ 0
14008
@@ -112070,6 +126078,7 @@
false
false
false
+ 0
14009
@@ -112078,6 +126087,7 @@
false
false
false
+ 0
14010
@@ -112086,6 +126096,7 @@
false
false
false
+ 0
14011
@@ -112094,6 +126105,7 @@
false
false
false
+ 0
14012
@@ -112102,6 +126114,7 @@
false
false
false
+ 0
14013
@@ -112110,6 +126123,7 @@
false
false
false
+ 0
14014
@@ -112118,6 +126132,7 @@
false
false
false
+ 0
14015
@@ -112126,6 +126141,7 @@
false
false
false
+ 0
14016
@@ -112134,6 +126150,7 @@
false
false
false
+ 0
14017
@@ -112142,6 +126159,7 @@
false
false
false
+ 0
14018
@@ -112150,6 +126168,7 @@
false
false
false
+ 0
14019
@@ -112158,6 +126177,7 @@
false
false
false
+ 0
14020
@@ -112166,6 +126186,7 @@
false
false
false
+ 0
14021
@@ -112174,6 +126195,7 @@
false
false
false
+ 0
14022
@@ -112182,6 +126204,7 @@
false
false
false
+ 0
14023
@@ -112190,6 +126213,7 @@
false
false
false
+ 0
14024
@@ -112198,6 +126222,7 @@
false
false
false
+ 0
14025
@@ -112206,6 +126231,7 @@
false
false
false
+ 0
14026
@@ -112214,6 +126240,7 @@
false
false
false
+ 0
14027
@@ -112222,6 +126249,7 @@
false
false
false
+ 0
14028
@@ -112230,6 +126258,7 @@
false
false
false
+ 0
14029
@@ -112238,6 +126267,7 @@
false
false
false
+ 0
14030
@@ -112246,6 +126276,7 @@
false
false
false
+ 0
14031
@@ -112254,6 +126285,7 @@
false
false
false
+ 0
14032
@@ -112262,6 +126294,7 @@
false
false
false
+ 0
14033
@@ -112270,6 +126303,7 @@
false
false
false
+ 0
14034
@@ -112278,6 +126312,7 @@
false
false
false
+ 0
14035
@@ -112286,6 +126321,7 @@
false
false
false
+ 0
14036
@@ -112294,6 +126330,7 @@
false
false
false
+ 0
14037
@@ -112302,6 +126339,7 @@
false
false
false
+ 0
14038
@@ -112310,6 +126348,7 @@
false
false
false
+ 0
14039
@@ -112318,6 +126357,7 @@
false
false
false
+ 0
14040
@@ -112326,6 +126366,7 @@
false
false
false
+ 0
14041
@@ -112334,6 +126375,7 @@
false
false
false
+ 0
14042
@@ -112342,6 +126384,7 @@
false
false
false
+ 0
14043
@@ -112350,6 +126393,7 @@
false
false
false
+ 0
14044
@@ -112358,6 +126402,7 @@
false
false
false
+ 0
14045
@@ -112366,6 +126411,7 @@
false
false
false
+ 0
14046
@@ -112374,6 +126420,7 @@
false
false
false
+ 0
14047
@@ -112382,6 +126429,7 @@
false
false
false
+ 0
14048
@@ -112390,6 +126438,7 @@
false
false
false
+ 0
14049
@@ -112398,6 +126447,7 @@
false
false
false
+ 0
14050
@@ -112406,6 +126456,7 @@
false
false
false
+ 0
14051
@@ -112414,6 +126465,7 @@
false
false
false
+ 0
14052
@@ -112422,6 +126474,7 @@
false
false
false
+ 0
14053
@@ -112430,6 +126483,7 @@
false
false
false
+ 0
14054
@@ -112438,6 +126492,7 @@
false
false
false
+ 0
14055
@@ -112446,6 +126501,7 @@
false
false
false
+ 0
14056
@@ -112454,6 +126510,7 @@
false
false
false
+ 0
14057
@@ -112462,6 +126519,7 @@
false
false
false
+ 0
14058
@@ -112470,6 +126528,7 @@
false
false
false
+ 0
14059
@@ -112478,6 +126537,7 @@
false
false
false
+ 0
14060
@@ -112486,6 +126546,7 @@
false
false
false
+ 0
14061
@@ -112494,6 +126555,7 @@
false
false
false
+ 0
14062
@@ -112502,6 +126564,7 @@
false
false
false
+ 0
14063
@@ -112510,6 +126573,7 @@
false
false
false
+ 0
14064
@@ -112518,6 +126582,7 @@
false
false
false
+ 0
14065
@@ -112526,6 +126591,7 @@
false
false
false
+ 0
14066
@@ -112534,6 +126600,7 @@
false
false
false
+ 0
14067
@@ -112542,6 +126609,7 @@
false
false
false
+ 0
14068
@@ -112550,6 +126618,7 @@
false
false
false
+ 0
14069
@@ -112558,6 +126627,7 @@
false
false
false
+ 0
14070
@@ -112566,6 +126636,7 @@
false
false
false
+ 0
14071
@@ -112574,6 +126645,7 @@
false
false
false
+ 0
14072
@@ -112582,6 +126654,7 @@
false
false
false
+ 0
14073
@@ -112590,6 +126663,7 @@
false
false
false
+ 0
14074
@@ -112598,6 +126672,7 @@
false
false
false
+ 0
14075
@@ -112606,6 +126681,7 @@
false
false
false
+ 0
14076
@@ -112614,6 +126690,7 @@
false
false
false
+ 0
14077
@@ -112622,6 +126699,7 @@
false
false
false
+ 0
14078
@@ -112630,6 +126708,7 @@
false
false
false
+ 0
14079
@@ -112638,6 +126717,7 @@
false
false
false
+ 0
14080
@@ -112646,6 +126726,7 @@
false
false
false
+ 0
14081
@@ -112654,6 +126735,7 @@
false
false
false
+ 0
14082
@@ -112662,6 +126744,7 @@
false
false
false
+ 0
14083
@@ -112670,6 +126753,7 @@
false
false
false
+ 0
14084
@@ -112678,6 +126762,7 @@
false
false
false
+ 0
14085
@@ -112686,6 +126771,7 @@
false
false
false
+ 0
14086
@@ -112694,6 +126780,7 @@
false
false
false
+ 0
14087
@@ -112702,6 +126789,7 @@
false
false
false
+ 0
14088
@@ -112710,6 +126798,7 @@
false
false
false
+ 0
14089
@@ -112718,6 +126807,7 @@
false
false
false
+ 0
14090
@@ -112726,6 +126816,7 @@
false
false
false
+ 0
14091
@@ -112734,6 +126825,7 @@
false
false
false
+ 0
14092
@@ -112742,6 +126834,7 @@
false
false
false
+ 0
14093
@@ -112750,6 +126843,7 @@
false
false
false
+ 0
14094
@@ -112758,6 +126852,7 @@
false
false
false
+ 0
14095
@@ -112766,6 +126861,7 @@
false
false
false
+ 0
14096
@@ -112774,6 +126870,7 @@
false
false
false
+ 0
14097
@@ -112782,6 +126879,7 @@
false
false
false
+ 0
14098
@@ -112790,6 +126888,7 @@
false
false
false
+ 0
14099
@@ -112798,6 +126897,7 @@
false
false
false
+ 0
14100
@@ -112806,6 +126906,7 @@
false
false
false
+ 0
14101
@@ -112814,6 +126915,7 @@
false
false
false
+ 0
14102
@@ -112822,6 +126924,7 @@
false
false
false
+ 0
14103
@@ -112830,6 +126933,7 @@
false
false
false
+ 0
14104
@@ -112838,6 +126942,7 @@
false
false
false
+ 0
14105
@@ -112846,6 +126951,7 @@
false
false
false
+ 0
14106
@@ -112854,6 +126960,7 @@
false
false
false
+ 0
14107
@@ -112862,6 +126969,7 @@
false
false
false
+ 0
14108
@@ -112870,6 +126978,7 @@
false
false
false
+ 0
14109
@@ -112878,6 +126987,7 @@
false
false
false
+ 0
14110
@@ -112886,6 +126996,7 @@
false
false
false
+ 0
14111
@@ -112894,6 +127005,7 @@
false
false
false
+ 0
14112
@@ -112902,6 +127014,7 @@
false
false
false
+ 0
14113
@@ -112910,6 +127023,7 @@
false
false
false
+ 0
14114
@@ -112918,6 +127032,7 @@
false
false
false
+ 0
14115
@@ -112926,6 +127041,7 @@
false
false
false
+ 0
14116
@@ -112934,6 +127050,7 @@
false
false
false
+ 0
14117
@@ -112942,6 +127059,7 @@
false
false
false
+ 0
14118
@@ -112950,6 +127068,7 @@
false
false
false
+ 0
14119
@@ -112958,6 +127077,7 @@
false
false
false
+ 0
14120
@@ -112966,6 +127086,7 @@
false
false
false
+ 0
14121
@@ -112974,6 +127095,7 @@
false
false
false
+ 0
14122
@@ -112982,6 +127104,7 @@
false
false
false
+ 0
14123
@@ -112990,6 +127113,7 @@
false
false
false
+ 0
14124
@@ -112998,6 +127122,7 @@
false
false
false
+ 0
14125
@@ -113006,6 +127131,7 @@
false
false
false
+ 0
14126
@@ -113014,6 +127140,7 @@
false
false
false
+ 0
14127
@@ -113022,6 +127149,7 @@
false
false
false
+ 0
14128
@@ -113030,6 +127158,7 @@
false
false
false
+ 0
14129
@@ -113038,6 +127167,7 @@
false
false
false
+ 0
14130
@@ -113046,6 +127176,7 @@
false
false
false
+ 0
14131
@@ -113054,6 +127185,7 @@
false
false
false
+ 0
14132
@@ -113062,6 +127194,7 @@
false
false
false
+ 0
14133
@@ -113070,6 +127203,7 @@
false
false
false
+ 0
14134
@@ -113078,6 +127212,7 @@
false
false
false
+ 0
14135
@@ -113086,6 +127221,7 @@
false
false
false
+ 0
14136
@@ -113094,6 +127230,7 @@
false
false
false
+ 0
14137
@@ -113102,6 +127239,7 @@
false
false
false
+ 0
14138
@@ -113110,6 +127248,7 @@
false
false
false
+ 0
14139
@@ -113118,6 +127257,7 @@
false
false
false
+ 0
14140
@@ -113126,6 +127266,7 @@
false
false
false
+ 0
14141
@@ -113134,6 +127275,7 @@
false
false
false
+ 0
14142
@@ -113142,6 +127284,7 @@
false
false
false
+ 0
14143
@@ -113150,6 +127293,7 @@
false
false
false
+ 0
14144
@@ -113158,6 +127302,7 @@
false
false
false
+ 0
14145
@@ -113166,6 +127311,7 @@
false
false
false
+ 0
14146
@@ -113174,6 +127320,7 @@
false
false
false
+ 0
14147
@@ -113182,6 +127329,7 @@
false
false
false
+ 0
14148
@@ -113190,6 +127338,7 @@
false
false
false
+ 0
14149
@@ -113198,6 +127347,7 @@
false
false
false
+ 0
14150
@@ -113206,6 +127356,7 @@
false
false
false
+ 0
14151
@@ -113214,6 +127365,7 @@
false
false
false
+ 0
14152
@@ -113222,6 +127374,7 @@
false
false
false
+ 0
14153
@@ -113230,6 +127383,7 @@
false
false
false
+ 0
14154
@@ -113238,6 +127392,7 @@
false
false
false
+ 0
14155
@@ -113246,6 +127401,7 @@
false
false
false
+ 0
14156
@@ -113254,6 +127410,7 @@
false
false
false
+ 0
14157
@@ -113262,6 +127419,7 @@
false
false
false
+ 0
14158
@@ -113270,6 +127428,7 @@
false
false
false
+ 0
14159
@@ -113278,6 +127437,7 @@
false
false
false
+ 0
14160
@@ -113286,6 +127446,7 @@
false
false
false
+ 0
14161
@@ -113294,6 +127455,7 @@
false
false
false
+ 0
14162
@@ -113302,6 +127464,7 @@
false
false
false
+ 0
14163
@@ -113310,6 +127473,7 @@
false
false
false
+ 0
14164
@@ -113318,6 +127482,7 @@
false
false
false
+ 0
14165
@@ -113326,6 +127491,7 @@
false
false
false
+ 0
14166
@@ -113334,6 +127500,7 @@
false
false
false
+ 0
14167
@@ -113342,6 +127509,7 @@
false
false
false
+ 0
14168
@@ -113350,6 +127518,7 @@
false
false
false
+ 0
14169
@@ -113358,6 +127527,7 @@
false
false
false
+ 0
14170
@@ -113366,6 +127536,7 @@
false
false
false
+ 0
14171
@@ -113374,6 +127545,7 @@
false
false
false
+ 0
14172
@@ -113382,6 +127554,7 @@
false
false
false
+ 0
14173
@@ -113390,6 +127563,7 @@
false
false
false
+ 0
14174
@@ -113398,6 +127572,7 @@
false
false
false
+ 0
14175
@@ -113406,6 +127581,7 @@
false
false
false
+ 0
14176
@@ -113414,6 +127590,7 @@
false
false
false
+ 0
14177
@@ -113422,6 +127599,7 @@
false
false
false
+ 0
14178
@@ -113430,6 +127608,7 @@
false
false
false
+ 0
14179
@@ -113438,6 +127617,7 @@
false
false
false
+ 0
14180
@@ -113446,6 +127626,7 @@
false
false
false
+ 0
14181
@@ -113454,6 +127635,7 @@
false
false
false
+ 0
14182
@@ -113462,6 +127644,7 @@
false
false
false
+ 0
14183
@@ -113470,6 +127653,7 @@
false
false
false
+ 0
14184
@@ -113478,6 +127662,7 @@
false
false
false
+ 0
14185
@@ -113486,6 +127671,7 @@
false
false
false
+ 0
14186
@@ -113494,6 +127680,7 @@
false
false
false
+ 0
14187
@@ -113502,6 +127689,7 @@
false
false
false
+ 0
14188
@@ -113510,6 +127698,7 @@
false
false
false
+ 0
14189
@@ -113518,6 +127707,7 @@
false
false
false
+ 0
14190
@@ -113526,6 +127716,7 @@
false
false
false
+ 0
14191
@@ -113534,6 +127725,7 @@
false
false
false
+ 0
14192
@@ -113542,6 +127734,7 @@
false
false
false
+ 0
14193
@@ -113550,6 +127743,7 @@
false
false
false
+ 0
14194
@@ -113558,6 +127752,7 @@
false
false
false
+ 0
14195
@@ -113566,6 +127761,7 @@
false
false
false
+ 0
14196
@@ -113574,6 +127770,7 @@
false
false
false
+ 0
14197
@@ -113582,6 +127779,7 @@
false
false
false
+ 0
14198
@@ -113590,6 +127788,7 @@
false
false
false
+ 0
14199
@@ -113598,6 +127797,7 @@
false
false
false
+ 0
14200
@@ -113606,6 +127806,7 @@
false
false
false
+ 0
14201
@@ -113614,6 +127815,7 @@
false
false
false
+ 0
14202
@@ -113622,6 +127824,7 @@
false
false
false
+ 0
14203
@@ -113630,6 +127833,7 @@
false
false
false
+ 0
14204
@@ -113638,6 +127842,7 @@
false
false
false
+ 0
14205
@@ -113646,6 +127851,7 @@
false
false
false
+ 0
14206
@@ -113654,6 +127860,7 @@
false
false
false
+ 0
14207
@@ -113662,6 +127869,7 @@
false
false
false
+ 0
14208
@@ -113670,6 +127878,7 @@
false
false
false
+ 0
14209
@@ -113678,6 +127887,7 @@
false
false
false
+ 0
14210
@@ -113686,6 +127896,7 @@
false
false
false
+ 0
14211
@@ -113694,6 +127905,7 @@
false
false
false
+ 0
14212
@@ -113702,6 +127914,7 @@
false
false
false
+ 0
14213
@@ -113710,6 +127923,7 @@
false
false
false
+ 0
14214
@@ -113718,6 +127932,7 @@
false
false
false
+ 0
14215
@@ -113726,6 +127941,7 @@
false
false
false
+ 0
14216
@@ -113734,6 +127950,7 @@
false
false
false
+ 0
14217
@@ -113742,6 +127959,7 @@
false
false
false
+ 0
14218
@@ -113750,6 +127968,7 @@
false
false
false
+ 0
14219
@@ -113758,6 +127977,7 @@
false
false
false
+ 0
14220
@@ -113766,6 +127986,7 @@
false
false
false
+ 0
14221
@@ -113774,6 +127995,7 @@
false
false
false
+ 0
14222
@@ -113782,6 +128004,7 @@
false
false
false
+ 0
14223
@@ -113790,6 +128013,7 @@
false
false
false
+ 0
14224
@@ -113798,6 +128022,7 @@
false
false
false
+ 0
14225
@@ -113806,6 +128031,7 @@
false
false
false
+ 0
14226
@@ -113814,6 +128040,7 @@
false
false
false
+ 0
14227
@@ -113822,6 +128049,7 @@
false
false
false
+ 0
14228
@@ -113830,6 +128058,7 @@
false
false
false
+ 0
14229
@@ -113838,6 +128067,7 @@
false
false
false
+ 0
14230
@@ -113846,6 +128076,7 @@
false
false
false
+ 0
14231
@@ -113854,6 +128085,7 @@
false
false
false
+ 0
14232
@@ -113862,6 +128094,7 @@
false
false
false
+ 0
14233
@@ -113870,6 +128103,7 @@
false
false
false
+ 0
14234
@@ -113878,6 +128112,7 @@
false
false
false
+ 0
14235
@@ -113886,6 +128121,7 @@
false
false
false
+ 0
14236
@@ -113894,6 +128130,7 @@
false
false
false
+ 0
14237
@@ -113902,6 +128139,7 @@
false
false
false
+ 0
14238
@@ -113910,6 +128148,7 @@
false
false
false
+ 0
14239
@@ -113918,6 +128157,7 @@
false
false
false
+ 0
14240
@@ -113926,6 +128166,7 @@
false
false
false
+ 0
14241
@@ -113934,6 +128175,7 @@
false
false
false
+ 0
14242
@@ -113942,6 +128184,7 @@
false
false
false
+ 0
14243
@@ -113950,6 +128193,7 @@
false
false
false
+ 0
14244
@@ -113958,6 +128202,7 @@
false
false
false
+ 0
14245
@@ -113966,6 +128211,7 @@
false
false
false
+ 0
14246
@@ -113974,6 +128220,7 @@
false
false
false
+ 0
14247
@@ -113982,6 +128229,7 @@
false
false
false
+ 0
14248
@@ -113990,6 +128238,7 @@
false
false
false
+ 0
14249
@@ -113998,6 +128247,7 @@
false
false
false
+ 0
14250
@@ -114006,6 +128256,7 @@
false
false
false
+ 0
14251
@@ -114014,6 +128265,7 @@
false
false
false
+ 0
14252
@@ -114022,6 +128274,7 @@
false
false
false
+ 0
14253
@@ -114030,6 +128283,7 @@
false
false
false
+ 0
14254
@@ -114038,6 +128292,7 @@
false
false
false
+ 0
14255
@@ -114046,6 +128301,7 @@
false
false
false
+ 0
14256
@@ -114054,6 +128310,7 @@
false
false
false
+ 0
14257
@@ -114062,6 +128319,7 @@
false
false
false
+ 0
14258
@@ -114070,6 +128328,7 @@
false
false
false
+ 0
14259
@@ -114078,6 +128337,7 @@
false
false
false
+ 0
14260
@@ -114086,6 +128346,7 @@
false
false
false
+ 0
14261
@@ -114094,6 +128355,7 @@
false
false
false
+ 0
14262
@@ -114102,6 +128364,7 @@
false
false
false
+ 0
14263
@@ -114110,6 +128373,7 @@
false
false
false
+ 0
14264
@@ -114118,6 +128382,7 @@
false
false
false
+ 0
14265
@@ -114126,6 +128391,7 @@
false
false
false
+ 0
14266
@@ -114134,6 +128400,7 @@
false
false
false
+ 0
14267
@@ -114142,6 +128409,7 @@
false
false
false
+ 0
14268
@@ -114150,6 +128418,7 @@
false
false
false
+ 0
14269
@@ -114158,6 +128427,7 @@
false
false
false
+ 0
14270
@@ -114166,6 +128436,7 @@
false
false
false
+ 0
14271
@@ -114174,6 +128445,7 @@
false
false
false
+ 0
14272
@@ -114182,6 +128454,7 @@
false
false
false
+ 0
14273
@@ -114190,6 +128463,7 @@
false
false
false
+ 0
14274
@@ -114198,6 +128472,7 @@
false
false
false
+ 0
14275
@@ -114206,6 +128481,7 @@
false
false
false
+ 0
14276
@@ -114214,6 +128490,7 @@
false
false
false
+ 0
14277
@@ -114222,6 +128499,7 @@
false
false
false
+ 0
14278
@@ -114230,6 +128508,7 @@
false
false
false
+ 0
14279
@@ -114238,6 +128517,7 @@
false
false
false
+ 0
14280
@@ -114246,6 +128526,7 @@
false
false
false
+ 0
14281
@@ -114254,6 +128535,7 @@
false
false
false
+ 0
14282
@@ -114262,6 +128544,7 @@
false
false
false
+ 0
14283
@@ -114270,6 +128553,7 @@
false
false
false
+ 0
14284
@@ -114278,6 +128562,7 @@
false
false
false
+ 0
14285
@@ -114286,6 +128571,7 @@
false
false
false
+ 0
14286
@@ -114294,6 +128580,7 @@
false
false
false
+ 0
14287
@@ -114302,6 +128589,7 @@
false
false
false
+ 0
14288
@@ -114310,6 +128598,7 @@
false
false
false
+ 0
14289
@@ -114318,6 +128607,7 @@
false
false
false
+ 0
14290
@@ -114326,6 +128616,7 @@
false
false
false
+ 0
14291
@@ -114334,6 +128625,7 @@
false
false
false
+ 0
14292
@@ -114342,6 +128634,7 @@
false
false
false
+ 0
14293
@@ -114350,6 +128643,7 @@
false
false
false
+ 0
14294
@@ -114358,6 +128652,7 @@
false
false
false
+ 0
14295
@@ -114366,6 +128661,7 @@
false
false
false
+ 0
14296
@@ -114374,6 +128670,7 @@
false
false
false
+ 0
14297
@@ -114382,6 +128679,7 @@
false
false
false
+ 0
14298
@@ -114390,6 +128688,7 @@
false
false
false
+ 0
14299
@@ -114398,6 +128697,7 @@
false
false
false
+ 0
14300
@@ -114406,6 +128706,7 @@
false
false
false
+ 0
14301
@@ -114414,6 +128715,7 @@
false
false
false
+ 0
14302
@@ -114422,6 +128724,7 @@
false
false
false
+ 0
14303
@@ -114430,6 +128733,7 @@
false
false
false
+ 0
14304
@@ -114438,6 +128742,7 @@
false
false
false
+ 0
14305
@@ -114446,6 +128751,7 @@
false
false
false
+ 0
14306
@@ -114454,6 +128760,7 @@
false
false
false
+ 0
14307
@@ -114462,6 +128769,7 @@
false
false
false
+ 0
14308
@@ -114470,6 +128778,7 @@
false
false
false
+ 0
14309
@@ -114478,6 +128787,7 @@
false
false
false
+ 0
14310
@@ -114486,6 +128796,7 @@
false
false
false
+ 0
14311
@@ -114494,6 +128805,7 @@
false
false
false
+ 0
14312
@@ -114502,6 +128814,7 @@
false
false
false
+ 0
14313
@@ -114510,6 +128823,7 @@
false
false
false
+ 0
14314
@@ -114518,6 +128832,7 @@
false
false
false
+ 0
14315
@@ -114526,6 +128841,7 @@
false
false
false
+ 0
14316
@@ -114534,6 +128850,7 @@
false
false
false
+ 0
14317
@@ -114542,6 +128859,7 @@
false
false
false
+ 0
14318
@@ -114550,6 +128868,7 @@
false
false
false
+ 0
14319
@@ -114558,6 +128877,7 @@
false
false
false
+ 0
14320
@@ -114566,6 +128886,7 @@
false
false
false
+ 0
14321
@@ -114574,6 +128895,7 @@
false
false
false
+ 0
14322
@@ -114582,6 +128904,7 @@
false
false
false
+ 0
14323
@@ -114590,6 +128913,7 @@
false
false
false
+ 0
14324
@@ -114598,6 +128922,7 @@
false
false
false
+ 0
14325
@@ -114606,6 +128931,7 @@
false
false
false
+ 0
14326
@@ -114614,6 +128940,7 @@
false
false
false
+ 0
14327
@@ -114622,6 +128949,7 @@
false
false
false
+ 0
14328
@@ -114630,6 +128958,7 @@
false
false
false
+ 0
14329
@@ -114638,6 +128967,7 @@
false
false
false
+ 0
14330
@@ -114646,6 +128976,7 @@
false
false
false
+ 0
14331
@@ -114654,6 +128985,7 @@
false
false
false
+ 0
14332
@@ -114662,6 +128994,7 @@
false
false
false
+ 0
14333
@@ -114670,6 +129003,7 @@
false
false
false
+ 0
14334
@@ -114678,6 +129012,7 @@
false
false
false
+ 0
14335
@@ -114686,6 +129021,7 @@
false
false
false
+ 0
14336
@@ -114694,6 +129030,7 @@
false
false
false
+ 0
14337
@@ -114702,6 +129039,7 @@
false
false
false
+ 0
14338
@@ -114710,6 +129048,7 @@
false
false
false
+ 0
14339
@@ -114718,6 +129057,7 @@
false
false
false
+ 0
14340
@@ -114726,6 +129066,7 @@
false
false
false
+ 0
14341
@@ -114734,6 +129075,7 @@
false
false
false
+ 0
14342
@@ -114742,6 +129084,7 @@
false
false
false
+ 0
14343
@@ -114750,6 +129093,7 @@
false
false
false
+ 0
14344
@@ -114758,6 +129102,7 @@
false
false
false
+ 0
14345
@@ -114766,6 +129111,7 @@
false
false
false
+ 0
14346
@@ -114774,6 +129120,7 @@
false
false
false
+ 0
14347
@@ -114782,6 +129129,7 @@
false
false
false
+ 0
14348
@@ -114790,6 +129138,7 @@
false
false
false
+ 0
14349
@@ -114798,6 +129147,7 @@
false
false
false
+ 0
14350
@@ -114806,6 +129156,7 @@
false
false
false
+ 0
14351
@@ -114814,6 +129165,7 @@
false
false
false
+ 0
14352
@@ -114822,6 +129174,7 @@
false
false
false
+ 0
14353
@@ -114830,6 +129183,7 @@
false
false
false
+ 0
14354
@@ -114838,6 +129192,7 @@
false
false
false
+ 0
14355
@@ -114846,6 +129201,7 @@
false
false
false
+ 0
14356
@@ -114854,6 +129210,7 @@
false
false
false
+ 0
14357
@@ -114862,6 +129219,7 @@
false
false
false
+ 0
14358
@@ -114870,6 +129228,7 @@
false
false
false
+ 0
14359
@@ -114878,6 +129237,7 @@
false
false
false
+ 0
14360
@@ -114886,6 +129246,7 @@
false
false
false
+ 0
14361
@@ -114894,6 +129255,7 @@
false
false
false
+ 0
14362
@@ -114902,6 +129264,7 @@
false
false
false
+ 0
14363
@@ -114910,6 +129273,7 @@
false
false
false
+ 0
14364
@@ -114918,6 +129282,7 @@
false
false
false
+ 0
14365
@@ -114926,6 +129291,7 @@
false
false
false
+ 0
14366
@@ -114934,6 +129300,7 @@
false
false
false
+ 0
14367
@@ -114942,6 +129309,7 @@
false
false
false
+ 0
14368
@@ -114950,6 +129318,7 @@
false
false
false
+ 0
14369
@@ -114958,6 +129327,7 @@
false
false
false
+ 0
14370
@@ -114966,6 +129336,7 @@
false
false
false
+ 0
14371
@@ -114974,6 +129345,7 @@
false
false
false
+ 0
14372
@@ -114982,6 +129354,7 @@
false
false
false
+ 0
14373
@@ -114990,6 +129363,7 @@
false
false
false
+ 0
14374
@@ -114998,6 +129372,7 @@
false
false
false
+ 0
14375
@@ -115006,6 +129381,7 @@
false
false
false
+ 0
14376
@@ -115014,6 +129390,7 @@
false
false
false
+ 0
14377
@@ -115022,6 +129399,7 @@
false
false
false
+ 0
14378
@@ -115030,6 +129408,7 @@
false
false
false
+ 0
14379
@@ -115038,6 +129417,7 @@
false
false
false
+ 0
14380
@@ -115046,6 +129426,7 @@
false
false
false
+ 0
14381
@@ -115054,6 +129435,7 @@
false
false
false
+ 0
14382
@@ -115062,6 +129444,7 @@
false
false
false
+ 0
14383
@@ -115070,6 +129453,7 @@
false
false
false
+ 0
14384
@@ -115078,6 +129462,7 @@
false
false
false
+ 0
14385
@@ -115086,6 +129471,7 @@
false
false
false
+ 0
14386
@@ -115094,6 +129480,7 @@
false
false
false
+ 0
14387
@@ -115102,6 +129489,7 @@
false
false
false
+ 0
14388
@@ -115110,6 +129498,7 @@
false
false
false
+ 0
14389
@@ -115118,6 +129507,7 @@
false
false
false
+ 0
14390
@@ -115126,6 +129516,7 @@
false
false
false
+ 0
14391
@@ -115134,6 +129525,7 @@
false
false
false
+ 0
14392
@@ -115142,6 +129534,7 @@
false
false
false
+ 0
14393
@@ -115150,6 +129543,7 @@
false
false
false
+ 0
14394
@@ -115158,6 +129552,7 @@
false
false
false
+ 0
14395
@@ -115166,6 +129561,7 @@
false
false
false
+ 0
14396
@@ -115174,6 +129570,7 @@
false
false
false
+ 0
14397
@@ -115182,6 +129579,7 @@
false
false
false
+ 0
14398
@@ -115190,6 +129588,7 @@
false
false
false
+ 0
14399
@@ -115198,6 +129597,7 @@
false
false
false
+ 0
14400
@@ -115206,6 +129606,7 @@
false
false
false
+ 0
14401
@@ -115214,6 +129615,7 @@
false
false
false
+ 0
14402
@@ -115222,6 +129624,7 @@
false
false
false
+ 0
14403
@@ -115230,6 +129633,7 @@
false
false
false
+ 0
14404
@@ -115238,6 +129642,7 @@
false
false
false
+ 0
14405
@@ -115246,6 +129651,7 @@
false
false
false
+ 0
14406
@@ -115254,6 +129660,7 @@
false
false
false
+ 0
14407
@@ -115262,6 +129669,7 @@
false
false
false
+ 0
14408
@@ -115270,6 +129678,7 @@
false
false
false
+ 0
14409
@@ -115278,6 +129687,7 @@
false
false
false
+ 0
14410
@@ -115286,6 +129696,7 @@
false
false
false
+ 0
14411
@@ -115294,6 +129705,7 @@
false
false
false
+ 0
14412
@@ -115302,6 +129714,7 @@
false
false
false
+ 0
14413
@@ -115310,6 +129723,7 @@
false
false
false
+ 0
14414
@@ -115318,6 +129732,7 @@
false
false
false
+ 0
14415
@@ -115326,6 +129741,7 @@
false
false
false
+ 0
14416
@@ -115334,6 +129750,7 @@
false
false
false
+ 0
14417
@@ -115342,6 +129759,7 @@
false
false
false
+ 0
14418
@@ -115350,6 +129768,7 @@
false
false
false
+ 0
14419
@@ -115358,6 +129777,7 @@
false
false
false
+ 0
14420
@@ -115366,6 +129786,7 @@
false
false
false
+ 0
14421
@@ -115374,6 +129795,7 @@
false
false
false
+ 0
14422
@@ -115382,6 +129804,7 @@
false
false
false
+ 0
14423
@@ -115390,6 +129813,7 @@
false
false
false
+ 0
14424
@@ -115398,6 +129822,7 @@
false
false
false
+ 0
14425
@@ -115406,6 +129831,7 @@
false
false
false
+ 0
14426
@@ -115414,6 +129840,7 @@
false
false
false
+ 0
14427
@@ -115422,6 +129849,7 @@
false
false
false
+ 0
14428
@@ -115430,6 +129858,7 @@
false
false
false
+ 0
14429
@@ -115438,6 +129867,7 @@
false
false
false
+ 0
14430
@@ -115446,6 +129876,7 @@
false
false
false
+ 0
14431
@@ -115454,6 +129885,7 @@
false
false
false
+ 0
14432
@@ -115462,6 +129894,7 @@
false
false
false
+ 0
14433
@@ -115470,6 +129903,7 @@
false
false
false
+ 0
14434
@@ -115478,6 +129912,7 @@
false
false
false
+ 0
14435
@@ -115486,6 +129921,7 @@
false
false
false
+ 0
14436
@@ -115494,6 +129930,7 @@
false
false
false
+ 0
14437
@@ -115502,6 +129939,7 @@
false
false
false
+ 0
14438
@@ -115510,6 +129948,7 @@
false
false
false
+ 0
14439
@@ -115518,6 +129957,7 @@
false
false
false
+ 0
14440
@@ -115526,6 +129966,7 @@
false
false
false
+ 0
14441
@@ -115534,6 +129975,7 @@
false
false
false
+ 0
14442
@@ -115542,6 +129984,7 @@
false
false
false
+ 0
14443
@@ -115550,6 +129993,7 @@
false
false
false
+ 0
14444
@@ -115558,6 +130002,7 @@
false
false
false
+ 0
14445
@@ -115566,6 +130011,7 @@
false
false
false
+ 0
14446
@@ -115574,6 +130020,7 @@
false
false
false
+ 0
14447
@@ -115582,6 +130029,7 @@
false
false
false
+ 0
14448
@@ -115590,6 +130038,7 @@
false
false
false
+ 0
14449
@@ -115598,6 +130047,7 @@
false
false
false
+ 0
14450
@@ -115606,6 +130056,7 @@
false
false
false
+ 0
14451
@@ -115614,6 +130065,7 @@
false
false
false
+ 0
14452
@@ -115622,6 +130074,7 @@
false
false
false
+ 0
14453
@@ -115630,6 +130083,7 @@
false
false
false
+ 0
14454
@@ -115638,6 +130092,7 @@
false
false
false
+ 0
14455
@@ -115646,6 +130101,7 @@
false
false
false
+ 0
14456
@@ -115654,6 +130110,7 @@
false
false
false
+ 0
14457
@@ -115662,6 +130119,7 @@
false
false
false
+ 0
14458
@@ -115670,6 +130128,7 @@
false
false
false
+ 0
14459
@@ -115678,6 +130137,7 @@
false
false
false
+ 0
14460
@@ -115686,6 +130146,7 @@
false
false
false
+ 0
14461
@@ -115694,6 +130155,7 @@
false
false
false
+ 0
14462
@@ -115702,6 +130164,7 @@
false
false
false
+ 0
14463
@@ -115710,6 +130173,7 @@
false
false
false
+ 0
14464
@@ -115718,6 +130182,7 @@
false
false
false
+ 0
14465
@@ -115726,6 +130191,7 @@
false
false
false
+ 0
14466
@@ -115734,6 +130200,7 @@
false
false
false
+ 0
14467
@@ -115742,6 +130209,7 @@
false
false
false
+ 0
14468
@@ -115750,6 +130218,7 @@
false
false
false
+ 0
14469
@@ -115758,6 +130227,7 @@
false
false
false
+ 0
14470
@@ -115766,6 +130236,7 @@
false
false
false
+ 0
14471
@@ -115774,6 +130245,7 @@
false
false
false
+ 0
14472
@@ -115782,6 +130254,7 @@
false
false
false
+ 0
14473
@@ -115790,6 +130263,7 @@
false
false
false
+ 0
14474
@@ -115798,6 +130272,7 @@
false
false
false
+ 0
14475
@@ -115806,6 +130281,7 @@
false
false
false
+ 0
14476
@@ -115814,6 +130290,7 @@
false
false
false
+ 0
14477
@@ -115822,6 +130299,7 @@
false
false
false
+ 0
14478
@@ -115830,6 +130308,7 @@
false
false
false
+ 0
14479
@@ -115838,6 +130317,7 @@
false
false
false
+ 0
14480
@@ -115846,6 +130326,7 @@
false
false
false
+ 0
14481
@@ -115854,6 +130335,7 @@
false
false
false
+ 0
14482
@@ -115862,6 +130344,7 @@
false
false
false
+ 0
14483
@@ -115870,6 +130353,7 @@
false
false
false
+ 0
14484
@@ -115878,6 +130362,7 @@
false
false
false
+ 0
14485
@@ -115886,6 +130371,7 @@
false
false
false
+ 0
14486
@@ -115894,6 +130380,7 @@
false
false
false
+ 0
14487
@@ -115902,6 +130389,7 @@
false
false
false
+ 0
14488
@@ -115910,6 +130398,7 @@
false
false
false
+ 0
14489
@@ -115918,6 +130407,7 @@
false
false
false
+ 0
14490
@@ -115926,6 +130416,7 @@
false
false
false
+ 0
14491
@@ -115934,6 +130425,7 @@
false
false
false
+ 0
14492
@@ -115942,6 +130434,7 @@
false
false
false
+ 0
14493
@@ -115950,6 +130443,7 @@
false
false
false
+ 0
14494
@@ -115958,6 +130452,7 @@
false
false
false
+ 0
14495
@@ -115966,6 +130461,7 @@
false
false
false
+ 0
14496
@@ -115974,6 +130470,7 @@
false
false
false
+ 0
14497
@@ -115982,6 +130479,7 @@
false
false
false
+ 0
14498
@@ -115990,6 +130488,7 @@
false
false
false
+ 0
14499
@@ -115998,6 +130497,7 @@
false
false
false
+ 0
14500
@@ -116006,6 +130506,7 @@
false
false
false
+ 0
14501
@@ -116014,6 +130515,7 @@
false
false
false
+ 0
14502
@@ -116022,6 +130524,7 @@
false
false
false
+ 0
14503
@@ -116030,6 +130533,7 @@
false
false
false
+ 0
14504
@@ -116038,6 +130542,7 @@
false
false
false
+ 0
14505
@@ -116046,6 +130551,7 @@
false
false
false
+ 0
14506
@@ -116054,6 +130560,7 @@
false
false
false
+ 0
14507
@@ -116062,6 +130569,7 @@
false
false
false
+ 0
14508
@@ -116070,6 +130578,7 @@
false
false
false
+ 0
14509
@@ -116078,6 +130587,7 @@
false
false
false
+ 0
14510
@@ -116086,6 +130596,7 @@
false
false
false
+ 0
14511
@@ -116094,6 +130605,7 @@
false
false
false
+ 0
14512
@@ -116102,6 +130614,7 @@
false
false
false
+ 0
14513
@@ -116110,6 +130623,7 @@
false
false
false
+ 0
14514
@@ -116118,6 +130632,7 @@
false
false
false
+ 0
14515
@@ -116126,6 +130641,7 @@
false
false
false
+ 0
14516
@@ -116134,6 +130650,7 @@
false
false
false
+ 0
14517
@@ -116142,6 +130659,7 @@
false
false
false
+ 0
14518
@@ -116150,6 +130668,7 @@
false
false
false
+ 0
14519
@@ -116158,6 +130677,7 @@
false
false
false
+ 0
14520
@@ -116166,6 +130686,7 @@
false
false
false
+ 0
14521
@@ -116174,6 +130695,7 @@
false
false
false
+ 0
14522
@@ -116182,6 +130704,7 @@
false
false
false
+ 0
14523
@@ -116190,6 +130713,7 @@
false
false
false
+ 0
14524
@@ -116198,6 +130722,7 @@
false
false
false
+ 0
14525
@@ -116206,6 +130731,7 @@
false
false
false
+ 0
14526
@@ -116214,6 +130740,7 @@
false
false
false
+ 0
14527
@@ -116222,6 +130749,7 @@
false
false
false
+ 0
14528
@@ -116230,6 +130758,7 @@
false
false
false
+ 0
14529
@@ -116238,6 +130767,7 @@
false
false
false
+ 0
14530
@@ -116246,6 +130776,7 @@
false
false
false
+ 0
14531
@@ -116254,6 +130785,7 @@
false
false
false
+ 0
14532
@@ -116262,6 +130794,7 @@
false
false
false
+ 0
14533
@@ -116270,6 +130803,7 @@
false
false
false
+ 0
14534
@@ -116278,6 +130812,7 @@
false
false
false
+ 0
14535
@@ -116286,6 +130821,7 @@
false
false
false
+ 0
14536
@@ -116294,6 +130830,7 @@
false
false
false
+ 0
14537
@@ -116302,6 +130839,7 @@
false
false
false
+ 0
14538
@@ -116310,6 +130848,7 @@
false
false
false
+ 0
14539
@@ -116318,6 +130857,7 @@
false
false
false
+ 0
14540
@@ -116326,6 +130866,7 @@
false
false
false
+ 0
14541
@@ -116334,6 +130875,7 @@
false
false
false
+ 0
14542
@@ -116342,6 +130884,7 @@
false
false
false
+ 0
14543
@@ -116350,6 +130893,7 @@
false
false
false
+ 0
14544
@@ -116358,6 +130902,7 @@
false
false
false
+ 0
14545
@@ -116366,6 +130911,7 @@
false
false
false
+ 0
14546
@@ -116374,6 +130920,7 @@
false
false
false
+ 0
14547
@@ -116382,6 +130929,7 @@
false
false
false
+ 0
14548
@@ -116390,6 +130938,7 @@
false
false
false
+ 0
14549
@@ -116398,6 +130947,7 @@
false
false
false
+ 0
14550
@@ -116406,6 +130956,7 @@
false
false
false
+ 0
14551
@@ -116414,6 +130965,7 @@
false
false
false
+ 0
14552
@@ -116422,6 +130974,7 @@
false
false
false
+ 0
14553
@@ -116430,6 +130983,7 @@
false
false
false
+ 0
14554
@@ -116438,6 +130992,7 @@
false
false
false
+ 0
14555
@@ -116446,6 +131001,7 @@
false
false
false
+ 0
14556
@@ -116454,6 +131010,7 @@
false
false
false
+ 0
14557
@@ -116462,6 +131019,7 @@
false
false
false
+ 0
14558
@@ -116470,6 +131028,7 @@
false
false
false
+ 0
14559
@@ -116478,6 +131037,7 @@
false
false
false
+ 0
14560
@@ -116486,6 +131046,7 @@
false
false
false
+ 0
14561
@@ -116494,6 +131055,7 @@
false
false
false
+ 0
14562
@@ -116502,6 +131064,7 @@
false
false
false
+ 0
14563
@@ -116510,6 +131073,7 @@
false
false
false
+ 0
14564
@@ -116518,6 +131082,7 @@
false
false
false
+ 0
14565
@@ -116526,6 +131091,7 @@
false
false
false
+ 0
14566
@@ -116534,6 +131100,7 @@
false
false
false
+ 0
14567
@@ -116542,6 +131109,7 @@
false
false
false
+ 0
14568
@@ -116550,6 +131118,7 @@
false
false
false
+ 0
14569
@@ -116558,6 +131127,7 @@
false
false
false
+ 0
14570
@@ -116566,6 +131136,7 @@
false
false
false
+ 0
14571
@@ -116574,6 +131145,7 @@
false
false
false
+ 0
14572
@@ -116582,6 +131154,7 @@
false
false
false
+ 0
14573
@@ -116590,6 +131163,7 @@
false
false
false
+ 0
14574
@@ -116598,6 +131172,7 @@
false
false
false
+ 0
14575
@@ -116606,6 +131181,7 @@
false
false
false
+ 0
14576
@@ -116614,6 +131190,7 @@
false
false
false
+ 0
14577
@@ -116622,6 +131199,7 @@
false
false
false
+ 0
14578
@@ -116630,6 +131208,7 @@
false
false
false
+ 0
14579
@@ -116638,6 +131217,7 @@
false
false
false
+ 0
14580
@@ -116646,6 +131226,7 @@
false
false
false
+ 0
14581
@@ -116654,6 +131235,7 @@
false
false
false
+ 0
14582
@@ -116662,6 +131244,7 @@
false
false
false
+ 0
14583
@@ -116670,6 +131253,7 @@
false
false
false
+ 0
14584
@@ -116678,6 +131262,7 @@
false
false
false
+ 0
14585
@@ -116686,6 +131271,7 @@
false
false
false
+ 0
14586
@@ -116694,6 +131280,7 @@
false
false
false
+ 0
14587
@@ -116702,6 +131289,7 @@
false
false
false
+ 0
14588
@@ -116710,6 +131298,7 @@
false
false
false
+ 0
14589
@@ -116718,6 +131307,7 @@
false
false
false
+ 0
14590
@@ -116726,6 +131316,7 @@
false
false
false
+ 0
14591
@@ -116734,6 +131325,7 @@
false
false
false
+ 0
14592
@@ -116742,6 +131334,7 @@
false
false
false
+ 0
14593
@@ -116750,6 +131343,7 @@
false
false
false
+ 0
14594
@@ -116758,6 +131352,7 @@
false
false
false
+ 0
14595
@@ -116766,6 +131361,7 @@
false
false
false
+ 0
14596
@@ -116774,6 +131370,7 @@
false
false
false
+ 0
14597
@@ -116782,6 +131379,7 @@
false
false
false
+ 0
14598
@@ -116790,6 +131388,7 @@
false
false
false
+ 0
14599
@@ -116798,6 +131397,7 @@
false
false
false
+ 0
14600
@@ -116806,6 +131406,7 @@
false
false
false
+ 0
14601
@@ -116814,6 +131415,7 @@
false
false
false
+ 0
14602
@@ -116822,6 +131424,7 @@
false
false
false
+ 0
14603
@@ -116830,6 +131433,7 @@
false
false
false
+ 0
14604
@@ -116838,6 +131442,7 @@
false
false
false
+ 0
14605
@@ -116846,6 +131451,7 @@
false
false
false
+ 0
14606
@@ -116854,6 +131460,7 @@
false
false
false
+ 0
14607
@@ -116862,6 +131469,7 @@
false
false
false
+ 0
14608
@@ -116870,6 +131478,7 @@
false
false
false
+ 0
14609
@@ -116878,6 +131487,7 @@
false
false
false
+ 0
14610
@@ -116886,6 +131496,7 @@
false
false
false
+ 0
14611
@@ -116894,6 +131505,7 @@
false
false
false
+ 0
14612
@@ -116902,6 +131514,7 @@
false
false
false
+ 0
14613
@@ -116910,6 +131523,7 @@
false
false
false
+ 0
14614
@@ -116918,6 +131532,7 @@
false
false
false
+ 0
14615
@@ -116926,6 +131541,7 @@
false
false
false
+ 0
14616
@@ -116934,6 +131550,7 @@
false
false
false
+ 0
14617
@@ -116942,6 +131559,7 @@
false
false
false
+ 0
14618
@@ -116950,6 +131568,7 @@
false
false
false
+ 0
14619
@@ -116958,6 +131577,7 @@
false
false
false
+ 0
14620
@@ -116966,6 +131586,7 @@
false
false
false
+ 0
14621
@@ -116974,6 +131595,7 @@
false
false
false
+ 0
14622
@@ -116982,6 +131604,7 @@
false
false
false
+ 0
14623
@@ -116990,6 +131613,7 @@
false
false
false
+ 0
14624
@@ -116998,6 +131622,7 @@
false
false
false
+ 0
14625
@@ -117006,6 +131631,7 @@
false
false
false
+ 0
14626
@@ -117014,6 +131640,7 @@
false
false
false
+ 0
14627
@@ -117022,6 +131649,7 @@
false
false
false
+ 0
14628
@@ -117030,6 +131658,7 @@
false
false
false
+ 0
14629
@@ -117038,6 +131667,7 @@
false
false
false
+ 0
14630
@@ -117046,6 +131676,7 @@
false
false
false
+ 0
14631
@@ -117054,6 +131685,7 @@
false
false
false
+ 0
14632
@@ -117062,6 +131694,7 @@
false
false
false
+ 0
14633
@@ -117070,6 +131703,7 @@
false
false
false
+ 0
14634
@@ -117078,6 +131712,7 @@
false
false
false
+ 0
14635
@@ -117086,6 +131721,7 @@
false
false
false
+ 0
14636
@@ -117094,6 +131730,7 @@
false
false
false
+ 0
14637
@@ -117102,6 +131739,7 @@
false
false
false
+ 0
14638
@@ -117110,6 +131748,7 @@
false
false
false
+ 0
14639
@@ -117118,6 +131757,7 @@
false
false
false
+ 0
14640
@@ -117126,6 +131766,7 @@
false
false
false
+ 0
14641
@@ -117134,6 +131775,7 @@
false
false
false
+ 0
14642
@@ -117142,6 +131784,7 @@
false
false
false
+ 0
14643
@@ -117150,6 +131793,7 @@
false
false
false
+ 0
14644
@@ -117158,6 +131802,7 @@
false
false
false
+ 0
14645
@@ -117166,6 +131811,7 @@
false
false
false
+ 0
14646
@@ -117174,6 +131820,7 @@
false
false
false
+ 0
14647
@@ -117182,6 +131829,7 @@
false
false
false
+ 0
14648
@@ -117190,6 +131838,7 @@
false
false
false
+ 0
14649
@@ -117198,6 +131847,7 @@
false
false
false
+ 0
14650
@@ -117206,6 +131856,7 @@
false
false
false
+ 0
14651
@@ -117214,6 +131865,7 @@
false
false
false
+ 0
14652
@@ -117222,6 +131874,7 @@
false
false
false
+ 0
14653
@@ -117230,6 +131883,7 @@
false
false
false
+ 0
14654
@@ -117238,6 +131892,7 @@
false
false
false
+ 0
14655
@@ -117246,6 +131901,7 @@
false
false
false
+ 0
14656
@@ -117254,6 +131910,7 @@
false
false
false
+ 0
14657
@@ -117262,6 +131919,7 @@
false
false
false
+ 0
14658
@@ -117270,6 +131928,7 @@
false
false
false
+ 0
14659
@@ -117278,6 +131937,7 @@
false
false
false
+ 0
14660
@@ -117286,6 +131946,7 @@
false
false
false
+ 0
14661
@@ -117294,6 +131955,7 @@
false
false
false
+ 0
14662
@@ -117302,6 +131964,7 @@
false
false
false
+ 0
14663
@@ -117310,6 +131973,7 @@
false
false
false
+ 0
14664
@@ -117318,6 +131982,7 @@
false
false
false
+ 0
14665
@@ -117326,6 +131991,7 @@
false
false
false
+ 0
14666
@@ -117334,6 +132000,7 @@
false
false
false
+ 0
14667
@@ -117342,6 +132009,7 @@
false
false
false
+ 0
14668
@@ -117350,6 +132018,7 @@
false
false
false
+ 0
14669
@@ -117358,6 +132027,7 @@
false
false
false
+ 0
14670
@@ -117366,6 +132036,7 @@
false
false
false
+ 0
14671
@@ -117374,6 +132045,7 @@
false
false
false
+ 0
14672
@@ -117382,6 +132054,7 @@
false
false
false
+ 0
14673
@@ -117390,6 +132063,7 @@
false
false
false
+ 0
14674
@@ -117398,6 +132072,7 @@
false
false
false
+ 0
14675
@@ -117406,6 +132081,7 @@
false
false
false
+ 0
14676
@@ -117414,6 +132090,7 @@
false
false
false
+ 0
14677
@@ -117422,6 +132099,7 @@
false
false
false
+ 0
14678
@@ -117430,6 +132108,7 @@
false
false
false
+ 0
14679
@@ -117438,6 +132117,7 @@
false
false
false
+ 0
14680
@@ -117446,6 +132126,7 @@
false
false
false
+ 0
14681
@@ -117454,6 +132135,7 @@
false
false
false
+ 0
14682
@@ -117462,6 +132144,7 @@
false
false
false
+ 0
14683
@@ -117470,6 +132153,7 @@
false
false
false
+ 0
14684
@@ -117478,6 +132162,7 @@
false
false
false
+ 0
14685
@@ -117486,6 +132171,7 @@
false
false
false
+ 0
14686
@@ -117494,6 +132180,7 @@
false
false
false
+ 0
14687
@@ -117502,6 +132189,7 @@
false
false
false
+ 0
14688
@@ -117510,6 +132198,7 @@
false
false
false
+ 0
14689
@@ -117518,6 +132207,7 @@
false
false
false
+ 0
14690
@@ -117526,6 +132216,7 @@
false
false
false
+ 0
14691
@@ -117534,6 +132225,7 @@
false
false
false
+ 0
14692
@@ -117542,6 +132234,7 @@
false
false
false
+ 0
14693
@@ -117550,6 +132243,7 @@
false
false
false
+ 0
14694
@@ -117558,6 +132252,7 @@
false
false
false
+ 0
14695
@@ -117566,6 +132261,7 @@
false
false
false
+ 0
14696
@@ -117574,6 +132270,7 @@
false
false
false
+ 0
14697
@@ -117582,6 +132279,7 @@
false
false
false
+ 0
14698
@@ -117590,6 +132288,7 @@
false
false
false
+ 0
14699
@@ -117598,6 +132297,7 @@
false
false
false
+ 0
14700
@@ -117606,6 +132306,7 @@
false
false
false
+ 0
14701
@@ -117614,6 +132315,7 @@
false
false
false
+ 0
14702
@@ -117622,6 +132324,7 @@
false
false
false
+ 0
14703
@@ -117630,6 +132333,7 @@
false
false
false
+ 0
14704
@@ -117638,6 +132342,7 @@
false
false
false
+ 0
14705
@@ -117646,6 +132351,7 @@
false
false
false
+ 0
14706
@@ -117654,6 +132360,7 @@
false
false
false
+ 0
14707
@@ -117662,6 +132369,7 @@
false
false
false
+ 0
14708
@@ -117670,6 +132378,7 @@
false
false
false
+ 0
14709
@@ -117678,6 +132387,7 @@
false
false
false
+ 0
14710
@@ -117686,6 +132396,7 @@
false
false
false
+ 0
14711
@@ -117694,6 +132405,7 @@
false
false
false
+ 0
14712
@@ -117702,6 +132414,7 @@
false
false
false
+ 0
14713
@@ -117710,6 +132423,7 @@
false
false
false
+ 0
14714
@@ -117718,6 +132432,7 @@
false
false
false
+ 0
14715
@@ -117726,6 +132441,7 @@
false
false
false
+ 0
14716
@@ -117734,6 +132450,7 @@
false
false
false
+ 0
14717
@@ -117742,6 +132459,7 @@
false
false
false
+ 0
14718
@@ -117750,6 +132468,7 @@
false
false
false
+ 0
14719
@@ -117758,6 +132477,7 @@
false
false
false
+ 0
14720
@@ -117766,6 +132486,7 @@
false
false
false
+ 0
14721
@@ -117774,6 +132495,7 @@
false
false
false
+ 0
14722
@@ -117782,6 +132504,7 @@
false
false
false
+ 0
14723
@@ -117790,6 +132513,7 @@
false
false
false
+ 0
14724
@@ -117798,6 +132522,7 @@
false
false
false
+ 0
14725
@@ -117806,6 +132531,7 @@
false
false
false
+ 0
14726
@@ -117814,6 +132540,7 @@
false
false
false
+ 0
14727
@@ -117822,6 +132549,7 @@
false
false
false
+ 0
14728
@@ -117830,6 +132558,7 @@
false
false
false
+ 0
14729
@@ -117838,6 +132567,7 @@
false
false
false
+ 0
14730
@@ -117846,6 +132576,7 @@
false
false
false
+ 0
14731
@@ -117854,6 +132585,7 @@
false
false
false
+ 0
14732
@@ -117862,6 +132594,7 @@
false
false
false
+ 0
14733
@@ -117870,6 +132603,7 @@
false
false
false
+ 0
14734
@@ -117878,6 +132612,7 @@
false
false
false
+ 0
14735
@@ -117886,6 +132621,7 @@
false
false
false
+ 0
14736
@@ -117894,6 +132630,7 @@
false
false
false
+ 0
14737
@@ -117902,6 +132639,7 @@
false
false
false
+ 0
14738
@@ -117910,6 +132648,7 @@
false
false
false
+ 0
14739
@@ -117918,6 +132657,7 @@
false
false
false
+ 0
14740
@@ -117926,6 +132666,7 @@
false
false
false
+ 0
14741
@@ -117934,6 +132675,7 @@
false
false
false
+ 0
14742
@@ -117942,6 +132684,7 @@
false
false
false
+ 0
14743
@@ -117950,6 +132693,7 @@
false
false
false
+ 0
14744
@@ -117958,6 +132702,7 @@
false
false
false
+ 0
14745
@@ -117966,6 +132711,7 @@
false
false
false
+ 0
14746
@@ -117974,6 +132720,7 @@
false
false
false
+ 0
14747
@@ -117982,6 +132729,7 @@
false
false
false
+ 0
14748
@@ -117990,6 +132738,7 @@
false
false
false
+ 0
14749
@@ -117998,6 +132747,7 @@
false
false
false
+ 0
14750
@@ -118006,6 +132756,7 @@
false
false
false
+ 0
14751
@@ -118014,6 +132765,7 @@
false
false
false
+ 0
14752
@@ -118022,6 +132774,7 @@
false
false
false
+ 0
14753
@@ -118030,6 +132783,7 @@
false
false
false
+ 0
14754
@@ -118038,6 +132792,7 @@
false
false
false
+ 0
14755
@@ -118046,6 +132801,7 @@
false
false
false
+ 0
14756
@@ -118054,6 +132810,7 @@
false
false
false
+ 0
14757
@@ -118062,6 +132819,7 @@
false
false
false
+ 0
14758
@@ -118070,6 +132828,7 @@
false
false
false
+ 0
14759
@@ -118078,6 +132837,7 @@
false
false
false
+ 0
14760
@@ -118086,6 +132846,7 @@
false
false
false
+ 0
14761
@@ -118094,6 +132855,7 @@
false
false
false
+ 0
14762
@@ -118102,6 +132864,7 @@
false
false
false
+ 0
14763
@@ -118110,6 +132873,7 @@
false
false
false
+ 0
14764
@@ -118118,6 +132882,7 @@
false
false
false
+ 0
14765
@@ -118126,6 +132891,7 @@
false
false
false
+ 0
14766
@@ -118134,6 +132900,7 @@
false
false
false
+ 0
14767
@@ -118142,6 +132909,7 @@
false
false
false
+ 0
14768
@@ -118150,6 +132918,7 @@
false
false
false
+ 0
14769
@@ -118158,6 +132927,7 @@
false
false
false
+ 0
14770
@@ -118166,6 +132936,7 @@
false
false
false
+ 0
14771
@@ -118174,6 +132945,7 @@
false
false
false
+ 0
14772
@@ -118182,6 +132954,7 @@
false
false
false
+ 0
14773
@@ -118190,6 +132963,7 @@
false
false
false
+ 0
14774
@@ -118198,6 +132972,7 @@
false
false
false
+ 0
14775
@@ -118206,6 +132981,7 @@
false
false
false
+ 0
14776
@@ -118214,6 +132990,7 @@
false
false
false
+ 0
14777
@@ -118222,6 +132999,7 @@
false
false
false
+ 0
14778
@@ -118230,6 +133008,7 @@
false
false
false
+ 0
14779
@@ -118238,6 +133017,7 @@
false
false
false
+ 0
14780
@@ -118246,6 +133026,7 @@
false
false
false
+ 0
14781
@@ -118254,6 +133035,7 @@
false
false
false
+ 0
14782
@@ -118262,6 +133044,7 @@
false
false
false
+ 0
14783
@@ -118270,6 +133053,7 @@
false
false
false
+ 0
14784
@@ -118278,6 +133062,7 @@
false
false
false
+ 0
14785
@@ -118286,6 +133071,7 @@
false
false
false
+ 0
14786
@@ -118294,6 +133080,7 @@
false
false
false
+ 0
14787
@@ -118302,6 +133089,7 @@
false
false
false
+ 0
14788
@@ -118310,6 +133098,7 @@
false
false
false
+ 0
14789
@@ -118318,6 +133107,7 @@
false
false
false
+ 0
14790
@@ -118326,6 +133116,7 @@
false
false
false
+ 0
14791
@@ -118334,6 +133125,7 @@
false
false
false
+ 0
14792
@@ -118342,6 +133134,7 @@
false
false
false
+ 0
14793
@@ -118350,6 +133143,7 @@
false
false
false
+ 0
14794
@@ -118358,6 +133152,7 @@
false
false
false
+ 0
14795
@@ -118366,6 +133161,7 @@
false
false
false
+ 0
14796
@@ -118374,6 +133170,7 @@
false
false
false
+ 0
14797
@@ -118382,6 +133179,7 @@
false
false
false
+ 0
14798
@@ -118390,6 +133188,7 @@
false
false
false
+ 0
14799
@@ -118398,6 +133197,7 @@
false
false
false
+ 0
14800
@@ -118406,6 +133206,7 @@
false
false
false
+ 0
14801
@@ -118414,6 +133215,7 @@
false
false
false
+ 0
14802
@@ -118422,6 +133224,7 @@
false
false
false
+ 0
14803
@@ -118430,6 +133233,7 @@
false
false
false
+ 0
14804
@@ -118438,6 +133242,7 @@
false
false
false
+ 0
14805
@@ -118446,6 +133251,7 @@
false
false
false
+ 0
14806
@@ -118454,6 +133260,7 @@
false
false
false
+ 0
14807
@@ -118462,6 +133269,7 @@
false
false
false
+ 0
14808
@@ -118470,6 +133278,7 @@
false
false
false
+ 0
14809
@@ -118478,6 +133287,7 @@
false
false
false
+ 0
14810
@@ -118486,6 +133296,7 @@
false
false
false
+ 0
14811
@@ -118494,6 +133305,7 @@
false
false
false
+ 0
14812
@@ -118502,6 +133314,7 @@
false
false
false
+ 0
14813
@@ -118510,6 +133323,7 @@
false
false
false
+ 0
14814
@@ -118518,6 +133332,7 @@
false
false
false
+ 0
14815
@@ -118526,6 +133341,7 @@
false
false
false
+ 0
14816
@@ -118534,6 +133350,7 @@
false
false
false
+ 0
14817
@@ -118542,6 +133359,7 @@
false
false
false
+ 0
14818
@@ -118550,6 +133368,7 @@
false
false
false
+ 0
14819
@@ -118558,6 +133377,7 @@
false
false
false
+ 0
14820
@@ -118566,6 +133386,7 @@
false
false
false
+ 0
14821
@@ -118574,6 +133395,7 @@
false
false
false
+ 0
14822
@@ -118582,6 +133404,7 @@
false
false
false
+ 0
14823
@@ -118590,6 +133413,7 @@
false
false
false
+ 0
14824
@@ -118598,6 +133422,7 @@
false
false
false
+ 0
14825
@@ -118606,6 +133431,7 @@
false
false
false
+ 0
14826
@@ -118614,6 +133440,7 @@
false
false
false
+ 0
14827
@@ -118622,6 +133449,7 @@
false
false
false
+ 0
14828
@@ -118630,6 +133458,7 @@
false
false
false
+ 0
14829
@@ -118638,6 +133467,7 @@
false
false
false
+ 0
14830
@@ -118646,6 +133476,7 @@
false
false
false
+ 0
14831
@@ -118654,6 +133485,7 @@
false
false
false
+ 0
14832
@@ -118662,6 +133494,7 @@
false
false
false
+ 0
14833
@@ -118670,6 +133503,7 @@
false
false
false
+ 0
14834
@@ -118678,6 +133512,7 @@
false
false
false
+ 0
14835
@@ -118686,6 +133521,7 @@
false
false
false
+ 0
14836
@@ -118694,6 +133530,7 @@
false
false
false
+ 0
14837
@@ -118702,6 +133539,7 @@
false
false
false
+ 0
14838
@@ -118710,6 +133548,7 @@
false
false
false
+ 0
14839
@@ -118718,6 +133557,7 @@
false
false
false
+ 0
14840
@@ -118726,6 +133566,7 @@
false
false
false
+ 0
14841
@@ -118734,6 +133575,7 @@
false
false
false
+ 0
14842
@@ -118742,6 +133584,7 @@
false
false
false
+ 0
14843
@@ -118750,6 +133593,7 @@
false
false
false
+ 0
14844
@@ -118758,6 +133602,7 @@
false
false
false
+ 0
14845
@@ -118766,6 +133611,7 @@
false
false
false
+ 0
14846
@@ -118774,6 +133620,7 @@
false
false
false
+ 0
14847
@@ -118782,6 +133629,7 @@
false
false
false
+ 0
14848
@@ -118790,6 +133638,7 @@
false
false
false
+ 0
14849
@@ -118798,6 +133647,7 @@
false
false
false
+ 0
14850
@@ -118806,6 +133656,7 @@
false
false
false
+ 0
14851
@@ -118814,6 +133665,7 @@
false
false
false
+ 0
14852
@@ -118822,6 +133674,7 @@
false
false
false
+ 0
14853
@@ -118830,6 +133683,7 @@
false
false
false
+ 0
14854
@@ -118838,6 +133692,7 @@
false
false
false
+ 0
14855
@@ -118846,6 +133701,7 @@
false
false
false
+ 0
14856
@@ -118854,6 +133710,7 @@
false
false
false
+ 0
14857
@@ -118862,6 +133719,7 @@
false
false
false
+ 0
14858
@@ -118870,6 +133728,7 @@
false
false
false
+ 0
14859
@@ -118878,6 +133737,7 @@
false
false
false
+ 0
14860
@@ -118886,6 +133746,7 @@
false
false
false
+ 0
14861
@@ -118894,6 +133755,7 @@
false
false
false
+ 0
14862
@@ -118902,6 +133764,7 @@
false
false
false
+ 0
14863
@@ -118910,6 +133773,7 @@
false
false
false
+ 0
14864
@@ -118918,6 +133782,7 @@
false
false
false
+ 0
14865
@@ -118926,6 +133791,7 @@
false
false
false
+ 0
14866
@@ -118934,6 +133800,7 @@
false
false
false
+ 0
14867
@@ -118942,6 +133809,7 @@
false
false
false
+ 0
14868
@@ -118950,6 +133818,7 @@
false
false
false
+ 0
14869
@@ -118958,6 +133827,7 @@
false
false
false
+ 0
14870
@@ -118966,6 +133836,7 @@
false
false
false
+ 0
14871
@@ -118974,6 +133845,7 @@
false
false
false
+ 0
14872
@@ -118982,6 +133854,7 @@
false
false
false
+ 0
14873
@@ -118990,6 +133863,7 @@
false
false
false
+ 0
14874
@@ -118998,6 +133872,7 @@
false
false
false
+ 0
14875
@@ -119006,6 +133881,7 @@
false
false
false
+ 0
14876
@@ -119014,6 +133890,7 @@
false
false
false
+ 0
14877
@@ -119022,6 +133899,7 @@
false
false
false
+ 0
14878
@@ -119030,6 +133908,7 @@
false
false
false
+ 0
14879
@@ -119038,6 +133917,7 @@
false
false
false
+ 0
14880
@@ -119046,6 +133926,7 @@
false
false
false
+ 0
14881
@@ -119054,6 +133935,7 @@
false
false
false
+ 0
14882
@@ -119062,6 +133944,7 @@
false
false
false
+ 0
14883
@@ -119070,6 +133953,7 @@
false
false
false
+ 0
14884
@@ -119078,6 +133962,7 @@
false
false
false
+ 0
14885
@@ -119086,6 +133971,7 @@
false
false
false
+ 0
14886
@@ -119094,6 +133980,7 @@
false
false
false
+ 0
14887
@@ -119102,6 +133989,7 @@
false
false
false
+ 0
14888
@@ -119110,6 +133998,7 @@
false
false
false
+ 0
14889
@@ -119118,6 +134007,7 @@
false
false
false
+ 0
14890
@@ -119126,6 +134016,7 @@
false
false
false
+ 0
14891
@@ -119134,6 +134025,7 @@
false
false
false
+ 0
14892
@@ -119142,6 +134034,7 @@
false
false
false
+ 0
14893
@@ -119150,6 +134043,7 @@
false
false
false
+ 0
14894
@@ -119158,6 +134052,7 @@
false
false
false
+ 0
14895
@@ -119166,6 +134061,7 @@
false
false
false
+ 0
14896
@@ -119174,6 +134070,7 @@
false
false
false
+ 0
14897
@@ -119182,6 +134079,7 @@
false
false
false
+ 0
14898
@@ -119190,6 +134088,7 @@
false
false
false
+ 0
14899
@@ -119198,6 +134097,7 @@
false
false
false
+ 0
14900
@@ -119206,6 +134106,7 @@
false
false
false
+ 0
14901
@@ -119214,6 +134115,7 @@
false
false
false
+ 0
14902
@@ -119222,6 +134124,7 @@
false
false
false
+ 0
14903
@@ -119230,6 +134133,7 @@
false
false
false
+ 0
14904
@@ -119238,6 +134142,7 @@
false
false
false
+ 0
14905
@@ -119246,6 +134151,7 @@
false
false
false
+ 0
14906
@@ -119254,6 +134160,7 @@
false
false
false
+ 0
14907
@@ -119262,6 +134169,7 @@
false
false
false
+ 0
14908
@@ -119270,6 +134178,7 @@
false
false
false
+ 0
14909
@@ -119278,6 +134187,7 @@
false
false
false
+ 0
14910
@@ -119286,6 +134196,7 @@
false
false
false
+ 0
14911
@@ -119294,6 +134205,7 @@
false
false
false
+ 0
14912
@@ -119302,6 +134214,7 @@
false
false
false
+ 0
14913
@@ -119310,6 +134223,7 @@
false
false
false
+ 0
14914
@@ -119318,6 +134232,7 @@
false
false
false
+ 0
14915
@@ -119326,6 +134241,7 @@
false
false
false
+ 0
14916
@@ -119334,6 +134250,7 @@
false
false
false
+ 0
14917
@@ -119342,6 +134259,7 @@
false
false
false
+ 0
14918
@@ -119350,6 +134268,7 @@
false
false
false
+ 0
14919
@@ -119358,6 +134277,7 @@
false
false
false
+ 0
14920
@@ -119366,6 +134286,7 @@
false
false
false
+ 0
14921
@@ -119374,6 +134295,7 @@
false
false
false
+ 0
14922
@@ -119382,6 +134304,7 @@
false
false
false
+ 0
14923
@@ -119390,6 +134313,7 @@
false
false
false
+ 0
14924
@@ -119398,6 +134322,7 @@
false
false
false
+ 0
14925
@@ -119406,6 +134331,7 @@
false
false
false
+ 0
14926
@@ -119414,6 +134340,7 @@
false
false
false
+ 0
14927
@@ -119422,6 +134349,7 @@
false
false
false
+ 0
14928
@@ -119430,6 +134358,7 @@
false
false
false
+ 0
14929
@@ -119438,6 +134367,7 @@
false
false
false
+ 0
14930
@@ -119446,6 +134376,7 @@
false
false
false
+ 0
14931
@@ -119454,6 +134385,7 @@
false
false
false
+ 0
14932
@@ -119462,6 +134394,7 @@
false
false
false
+ 0
14933
@@ -119470,6 +134403,7 @@
false
false
false
+ 0
14934
@@ -119478,6 +134412,7 @@
false
false
false
+ 0
14935
@@ -119486,6 +134421,7 @@
false
false
false
+ 0
14936
@@ -119494,6 +134430,7 @@
false
false
false
+ 0
14937
@@ -119502,6 +134439,7 @@
false
false
false
+ 0
14938
@@ -119510,6 +134448,7 @@
false
false
false
+ 0
14939
@@ -119518,6 +134457,7 @@
false
false
false
+ 0
14940
@@ -119526,6 +134466,7 @@
false
false
false
+ 0
14941
@@ -119534,6 +134475,7 @@
false
false
false
+ 0
14942
@@ -119542,6 +134484,7 @@
false
false
false
+ 0
14943
@@ -119550,6 +134493,7 @@
false
false
false
+ 0
14944
@@ -119558,6 +134502,7 @@
false
false
false
+ 0
14945
@@ -119566,6 +134511,7 @@
false
false
false
+ 0
14946
@@ -119574,6 +134520,7 @@
false
false
false
+ 0
14947
@@ -119582,6 +134529,7 @@
false
false
false
+ 0
14948
@@ -119590,6 +134538,7 @@
false
false
false
+ 0
14949
@@ -119598,6 +134547,7 @@
false
false
false
+ 0
14950
@@ -119606,6 +134556,7 @@
false
false
false
+ 0
14951
@@ -119614,6 +134565,7 @@
false
false
false
+ 0
14952
@@ -119622,6 +134574,7 @@
false
false
false
+ 0
14953
@@ -119630,6 +134583,7 @@
false
false
false
+ 0
14954
@@ -119638,6 +134592,7 @@
false
false
false
+ 0
14955
@@ -119646,6 +134601,7 @@
false
false
false
+ 0
14956
@@ -119654,6 +134610,7 @@
false
false
false
+ 0
14957
@@ -119662,6 +134619,7 @@
false
false
false
+ 0
14958
@@ -119670,6 +134628,7 @@
false
false
false
+ 0
14959
@@ -119678,6 +134637,7 @@
false
false
false
+ 0
14960
@@ -119686,6 +134646,7 @@
false
false
false
+ 0
14961
@@ -119694,6 +134655,7 @@
false
false
false
+ 0
14962
@@ -119702,6 +134664,7 @@
false
false
false
+ 0
14963
@@ -119710,6 +134673,7 @@
false
false
false
+ 0
14964
@@ -119718,6 +134682,7 @@
false
false
false
+ 0
14965
@@ -119726,6 +134691,7 @@
false
false
false
+ 0
14966
@@ -119734,6 +134700,7 @@
false
false
false
+ 0
14967
@@ -119742,6 +134709,7 @@
false
false
false
+ 0
14968
@@ -119750,6 +134718,7 @@
false
false
false
+ 0
14969
@@ -119758,6 +134727,7 @@
false
false
false
+ 0
14970
@@ -119766,6 +134736,7 @@
false
false
false
+ 0
14971
@@ -119774,6 +134745,7 @@
false
false
false
+ 0
14972
@@ -119782,6 +134754,7 @@
false
false
false
+ 0
14973
@@ -119790,6 +134763,7 @@
false
false
false
+ 0
14974
@@ -119798,6 +134772,7 @@
false
false
false
+ 0
14975
@@ -119806,6 +134781,7 @@
false
false
false
+ 0
14976
@@ -119814,6 +134790,7 @@
false
false
false
+ 0
14977
@@ -119822,6 +134799,7 @@
false
false
false
+ 0
14978
@@ -119830,6 +134808,7 @@
false
false
false
+ 0
14979
@@ -119838,6 +134817,7 @@
false
false
false
+ 0
14980
@@ -119846,6 +134826,7 @@
false
false
false
+ 0
14981
@@ -119854,6 +134835,7 @@
false
false
false
+ 0
14982
@@ -119862,6 +134844,7 @@
false
false
false
+ 0
14983
@@ -119870,6 +134853,7 @@
false
false
false
+ 0
14984
@@ -119878,6 +134862,7 @@
false
false
false
+ 0
14985
@@ -119886,6 +134871,7 @@
false
false
false
+ 0
14986
@@ -119894,6 +134880,7 @@
false
false
false
+ 0
14987
@@ -119902,6 +134889,7 @@
false
false
false
+ 0
14988
@@ -119910,6 +134898,7 @@
false
false
false
+ 0
14989
@@ -119918,6 +134907,7 @@
false
false
false
+ 0
14990
@@ -119926,6 +134916,7 @@
false
false
false
+ 0
14991
@@ -119934,6 +134925,7 @@
false
false
false
+ 0
14992
@@ -119942,6 +134934,7 @@
false
false
false
+ 0
14993
@@ -119950,6 +134943,7 @@
false
false
false
+ 0
14994
@@ -119958,6 +134952,7 @@
false
false
false
+ 0
14995
@@ -119966,6 +134961,7 @@
false
false
false
+ 0
14996
@@ -119974,6 +134970,7 @@
false
false
false
+ 0
14997
@@ -119982,6 +134979,7 @@
false
false
false
+ 0
14998
@@ -119990,6 +134988,7 @@
false
false
false
+ 0
14999
@@ -119998,6 +134997,7 @@
false
false
false
+ 0
15000
@@ -120006,6 +135006,7 @@
false
false
false
+ 0
15001
@@ -120014,6 +135015,7 @@
false
false
false
+ 0
15002
@@ -120022,6 +135024,7 @@
false
false
false
+ 0
15003
@@ -120030,6 +135033,7 @@
false
false
false
+ 0
15004
@@ -120038,6 +135042,7 @@
false
false
false
+ 0
15005
@@ -120046,6 +135051,7 @@
false
false
false
+ 0
15006
@@ -120054,6 +135060,7 @@
false
false
false
+ 0
15007
@@ -120062,6 +135069,7 @@
false
false
false
+ 0
15008
@@ -120070,6 +135078,7 @@
false
false
false
+ 0
15009
@@ -120078,6 +135087,7 @@
false
false
false
+ 0
15010
@@ -120086,6 +135096,7 @@
false
false
false
+ 0
15011
@@ -120094,6 +135105,7 @@
false
false
false
+ 0
15012
@@ -120102,6 +135114,7 @@
false
false
false
+ 0
15013
@@ -120110,6 +135123,7 @@
false
false
false
+ 0
15014
@@ -120118,6 +135132,7 @@
false
false
false
+ 0
15015
@@ -120126,6 +135141,7 @@
false
false
false
+ 0
15016
@@ -120134,6 +135150,7 @@
false
false
false
+ 0
15017
@@ -120142,6 +135159,7 @@
false
false
false
+ 0
15018
@@ -120150,6 +135168,7 @@
false
false
false
+ 0
15019
@@ -120158,6 +135177,7 @@
false
false
false
+ 0
15020
@@ -120166,6 +135186,7 @@
false
false
false
+ 0
15021
@@ -120174,6 +135195,7 @@
false
false
false
+ 0
15022
@@ -120182,6 +135204,7 @@
false
false
false
+ 0
15023
@@ -120190,6 +135213,7 @@
false
false
false
+ 0
15024
@@ -120198,6 +135222,7 @@
false
false
false
+ 0
15025
@@ -120206,6 +135231,7 @@
false
false
false
+ 0
15026
@@ -120214,6 +135240,7 @@
false
false
false
+ 0
15027
@@ -120222,6 +135249,7 @@
false
false
false
+ 0
15028
@@ -120230,6 +135258,7 @@
false
false
false
+ 0
15029
@@ -120238,6 +135267,7 @@
false
false
false
+ 0
15030
@@ -120246,6 +135276,7 @@
false
false
false
+ 0
15031
@@ -120254,6 +135285,7 @@
false
false
false
+ 0
15032
@@ -120262,6 +135294,7 @@
false
false
false
+ 0
15033
@@ -120270,6 +135303,7 @@
false
false
false
+ 0
15034
@@ -120278,6 +135312,7 @@
false
false
false
+ 0
15035
@@ -120286,6 +135321,7 @@
false
false
false
+ 0
15036
@@ -120294,6 +135330,7 @@
false
false
false
+ 0
15037
@@ -120302,6 +135339,7 @@
false
false
false
+ 0
15038
@@ -120310,6 +135348,7 @@
false
false
false
+ 0
15039
@@ -120318,6 +135357,7 @@
false
false
false
+ 0
15040
@@ -120326,6 +135366,7 @@
false
false
false
+ 0
15041
@@ -120334,6 +135375,7 @@
false
false
false
+ 0
15042
@@ -120342,6 +135384,7 @@
false
false
false
+ 0
15043
@@ -120350,6 +135393,7 @@
false
false
false
+ 0
15044
@@ -120358,6 +135402,7 @@
false
false
false
+ 0
15045
@@ -120366,6 +135411,7 @@
false
false
false
+ 0
15046
@@ -120374,6 +135420,7 @@
false
false
false
+ 0
15047
@@ -120382,6 +135429,7 @@
false
false
false
+ 0
15048
@@ -120390,6 +135438,7 @@
false
false
false
+ 0
15049
@@ -120398,6 +135447,7 @@
false
false
false
+ 0
15050
@@ -120406,6 +135456,7 @@
false
false
false
+ 0
15051
@@ -120414,6 +135465,7 @@
false
false
false
+ 0
15052
@@ -120422,6 +135474,7 @@
false
false
false
+ 0
15053
@@ -120430,6 +135483,7 @@
false
false
false
+ 0
15054
@@ -120438,6 +135492,7 @@
false
false
false
+ 0
15055
@@ -120446,6 +135501,7 @@
false
false
false
+ 0
15056
@@ -120454,6 +135510,7 @@
false
false
false
+ 0
15057
@@ -120462,6 +135519,7 @@
false
false
false
+ 0
15058
@@ -120470,6 +135528,7 @@
false
false
false
+ 0
15059
@@ -120478,6 +135537,7 @@
false
false
false
+ 0
15060
@@ -120486,6 +135546,7 @@
false
false
false
+ 0
15061
@@ -120494,6 +135555,7 @@
false
false
false
+ 0
15062
@@ -120502,6 +135564,7 @@
false
false
false
+ 0
15063
@@ -120510,6 +135573,7 @@
false
false
false
+ 0
15064
@@ -120518,6 +135582,7 @@
false
false
false
+ 0
15065
@@ -120526,6 +135591,7 @@
false
false
false
+ 0
15066
@@ -120534,6 +135600,7 @@
false
false
false
+ 0
15067
@@ -120542,6 +135609,7 @@
false
false
false
+ 0
15068
@@ -120550,6 +135618,7 @@
false
false
false
+ 0
15069
@@ -120558,6 +135627,7 @@
false
false
false
+ 0
15070
@@ -120566,6 +135636,7 @@
false
false
false
+ 0
15071
@@ -120574,6 +135645,7 @@
false
false
false
+ 0
15072
@@ -120582,6 +135654,7 @@
false
false
false
+ 0
15073
@@ -120590,6 +135663,7 @@
false
false
false
+ 0
15074
@@ -120598,6 +135672,7 @@
false
false
false
+ 0
15075
@@ -120606,6 +135681,7 @@
false
false
false
+ 0
15076
@@ -120614,6 +135690,7 @@
false
false
false
+ 0
15077
@@ -120622,6 +135699,7 @@
false
false
false
+ 0
15078
@@ -120630,6 +135708,7 @@
false
false
false
+ 0
15079
@@ -120638,6 +135717,7 @@
false
false
false
+ 0
15080
@@ -120646,6 +135726,7 @@
false
false
false
+ 0
15081
@@ -120654,6 +135735,7 @@
false
false
false
+ 0
15082
@@ -120662,6 +135744,7 @@
false
false
false
+ 0
15083
@@ -120670,6 +135753,7 @@
false
false
false
+ 0
15084
@@ -120678,6 +135762,7 @@
false
false
false
+ 0
15085
@@ -120686,6 +135771,7 @@
false
false
false
+ 0
15086
@@ -120694,6 +135780,7 @@
false
false
false
+ 0
15087
@@ -120702,6 +135789,7 @@
false
false
false
+ 0
15088
@@ -120710,6 +135798,7 @@
false
false
false
+ 0
15089
@@ -120718,6 +135807,7 @@
false
false
false
+ 0
15090
@@ -120726,6 +135816,7 @@
false
false
false
+ 0
15091
@@ -120734,6 +135825,7 @@
false
false
false
+ 0
15092
@@ -120742,6 +135834,7 @@
false
false
false
+ 0
15093
@@ -120750,6 +135843,7 @@
false
false
false
+ 0
15094
@@ -120758,6 +135852,7 @@
false
false
false
+ 0
15095
@@ -120766,6 +135861,7 @@
false
false
false
+ 0
15096
@@ -120774,6 +135870,7 @@
false
false
false
+ 0
15097
@@ -120782,6 +135879,7 @@
false
false
false
+ 0
15098
@@ -120790,6 +135888,7 @@
false
false
false
+ 0
15099
@@ -120798,6 +135897,7 @@
false
false
false
+ 0
15100
@@ -120806,6 +135906,7 @@
false
false
false
+ 0
15101
@@ -120814,6 +135915,7 @@
false
false
false
+ 0
15102
@@ -120822,6 +135924,7 @@
false
false
false
+ 0
15103
@@ -120830,6 +135933,7 @@
false
false
false
+ 0
15104
@@ -120838,6 +135942,7 @@
false
false
false
+ 0
15105
@@ -120846,6 +135951,7 @@
false
false
false
+ 0
15106
@@ -120854,6 +135960,7 @@
false
false
false
+ 0
15107
@@ -120862,6 +135969,7 @@
false
false
false
+ 0
15108
@@ -120870,6 +135978,7 @@
false
false
false
+ 0
15109
@@ -120878,6 +135987,7 @@
false
false
false
+ 0
15110
@@ -120886,6 +135996,7 @@
false
false
false
+ 0
15111
@@ -120894,6 +136005,7 @@
false
false
false
+ 0
15112
@@ -120902,6 +136014,7 @@
false
false
false
+ 0
15113
@@ -120910,6 +136023,7 @@
false
false
false
+ 0
15114
@@ -120918,6 +136032,7 @@
false
false
false
+ 0
15115
@@ -120926,6 +136041,7 @@
false
false
false
+ 0
15116
@@ -120934,6 +136050,7 @@
false
false
false
+ 0
15117
@@ -120942,6 +136059,7 @@
false
false
false
+ 0
15118
@@ -120950,6 +136068,7 @@
false
false
false
+ 0
15119
@@ -120958,6 +136077,7 @@
false
false
false
+ 0
15120
@@ -120966,6 +136086,7 @@
false
false
false
+ 0
15121
@@ -120974,6 +136095,7 @@
false
false
false
+ 0
15122
@@ -120982,6 +136104,7 @@
false
false
false
+ 0
15123
@@ -120990,6 +136113,7 @@
false
false
false
+ 0
15124
@@ -120998,6 +136122,7 @@
false
false
false
+ 0
15125
@@ -121006,6 +136131,7 @@
false
false
false
+ 0
15126
@@ -121014,6 +136140,7 @@
false
false
false
+ 0
15127
@@ -121022,6 +136149,7 @@
false
false
false
+ 0
15128
@@ -121030,6 +136158,7 @@
false
false
false
+ 0
15129
@@ -121038,6 +136167,7 @@
false
false
false
+ 0
15130
@@ -121046,6 +136176,7 @@
false
false
false
+ 0
15131
@@ -121054,6 +136185,7 @@
false
false
false
+ 0
15132
@@ -121062,6 +136194,7 @@
false
false
false
+ 0
15133
@@ -121070,6 +136203,7 @@
false
false
false
+ 0
15134
@@ -121078,6 +136212,7 @@
false
false
false
+ 0
15135
@@ -121086,6 +136221,7 @@
false
false
false
+ 0
15136
@@ -121094,6 +136230,7 @@
false
false
false
+ 0
15137
@@ -121102,6 +136239,7 @@
false
false
false
+ 0
15138
@@ -121110,6 +136248,7 @@
false
false
false
+ 0
15139
@@ -121118,6 +136257,7 @@
false
false
false
+ 0
15140
@@ -121126,6 +136266,7 @@
false
false
false
+ 0
15141
@@ -121134,6 +136275,7 @@
false
false
false
+ 0
15142
@@ -121142,6 +136284,7 @@
false
false
false
+ 0
15143
@@ -121150,6 +136293,7 @@
false
false
false
+ 0
15144
@@ -121158,6 +136302,7 @@
false
false
false
+ 0
15145
@@ -121166,6 +136311,7 @@
false
false
false
+ 0
15146
@@ -121174,6 +136320,7 @@
false
false
false
+ 0
15147
@@ -121182,6 +136329,7 @@
false
false
false
+ 0
15148
@@ -121190,6 +136338,7 @@
false
false
false
+ 0
15149
@@ -121198,6 +136347,7 @@
false
false
false
+ 0
15150
@@ -121206,6 +136356,7 @@
false
false
false
+ 0
15151
@@ -121214,6 +136365,7 @@
false
false
false
+ 0
15152
@@ -121222,6 +136374,7 @@
false
false
false
+ 0
15153
@@ -121230,6 +136383,7 @@
false
false
false
+ 0
15154
@@ -121238,6 +136392,7 @@
false
false
false
+ 0
15155
@@ -121246,6 +136401,7 @@
false
false
false
+ 0
15156
@@ -121254,6 +136410,7 @@
false
false
false
+ 0
15157
@@ -121262,6 +136419,7 @@
false
false
false
+ 0
15158
@@ -121270,6 +136428,7 @@
false
false
false
+ 0
15159
@@ -121278,6 +136437,7 @@
false
false
false
+ 0
15160
@@ -121286,6 +136446,7 @@
false
false
false
+ 0
15161
@@ -121294,6 +136455,7 @@
false
false
false
+ 0
15162
@@ -121302,6 +136464,7 @@
false
false
false
+ 0
15163
@@ -121310,6 +136473,7 @@
false
false
false
+ 0
15164
@@ -121318,6 +136482,7 @@
false
false
false
+ 0
15165
@@ -121326,6 +136491,7 @@
false
false
false
+ 0
15166
@@ -121334,6 +136500,7 @@
false
false
false
+ 0
15167
@@ -121342,6 +136509,7 @@
false
false
false
+ 0
15168
@@ -121350,6 +136518,7 @@
false
false
false
+ 0
15169
@@ -121358,6 +136527,7 @@
false
false
false
+ 0
15170
@@ -121366,6 +136536,7 @@
false
false
false
+ 0
15171
@@ -121374,6 +136545,7 @@
false
false
false
+ 0
15172
@@ -121382,6 +136554,7 @@
false
false
false
+ 0
15173
@@ -121390,6 +136563,7 @@
false
false
false
+ 0
15174
@@ -121398,6 +136572,7 @@
false
false
false
+ 0
15175
@@ -121406,6 +136581,7 @@
false
false
false
+ 0
15176
@@ -121414,6 +136590,7 @@
false
false
false
+ 0
15177
@@ -121422,6 +136599,7 @@
false
false
false
+ 0
15178
@@ -121430,6 +136608,7 @@
false
false
false
+ 0
15179
@@ -121438,6 +136617,7 @@
false
false
false
+ 0
15180
@@ -121446,6 +136626,7 @@
false
false
false
+ 0
15181
@@ -121454,6 +136635,7 @@
false
false
false
+ 0
15182
@@ -121462,6 +136644,7 @@
false
false
false
+ 0
15183
@@ -121470,6 +136653,7 @@
false
false
false
+ 0
15184
@@ -121478,6 +136662,7 @@
false
false
false
+ 0
15185
@@ -121486,6 +136671,7 @@
false
false
false
+ 0
15186
@@ -121494,6 +136680,7 @@
false
false
false
+ 0
15187
@@ -121502,6 +136689,7 @@
false
false
false
+ 0
15188
@@ -121510,6 +136698,7 @@
false
false
false
+ 0
15189
@@ -121518,6 +136707,7 @@
false
false
false
+ 0
15190
@@ -121526,6 +136716,7 @@
false
false
false
+ 0
15191
@@ -121534,6 +136725,7 @@
false
false
false
+ 0
15192
@@ -121542,6 +136734,7 @@
false
false
false
+ 0
15193
@@ -121550,6 +136743,7 @@
false
false
false
+ 0
15194
@@ -121558,6 +136752,7 @@
false
false
false
+ 0
15195
@@ -121566,6 +136761,7 @@
false
false
false
+ 0
15196
@@ -121574,6 +136770,7 @@
false
false
false
+ 0
15197
@@ -121582,6 +136779,7 @@
false
false
false
+ 0
15198
@@ -121590,6 +136788,7 @@
false
false
false
+ 0
15199
@@ -121598,6 +136797,7 @@
false
false
false
+ 0
15200
@@ -121606,6 +136806,7 @@
false
false
false
+ 0
15201
@@ -121614,6 +136815,7 @@
false
false
false
+ 0
15202
@@ -121622,6 +136824,7 @@
false
false
false
+ 0
15203
@@ -121630,6 +136833,7 @@
false
false
false
+ 0
15204
@@ -121638,6 +136842,7 @@
false
false
false
+ 0
15205
@@ -121646,6 +136851,7 @@
false
false
false
+ 0
15206
@@ -121654,6 +136860,7 @@
false
false
false
+ 0
15207
@@ -121662,6 +136869,7 @@
false
false
false
+ 0
15208
@@ -121670,6 +136878,7 @@
false
false
false
+ 0
15209
@@ -121678,6 +136887,7 @@
false
false
false
+ 0
15210
@@ -121686,6 +136896,7 @@
false
false
false
+ 0
15211
@@ -121694,6 +136905,7 @@
false
false
false
+ 0
15212
@@ -121702,6 +136914,7 @@
false
false
false
+ 0
15213
@@ -121710,6 +136923,7 @@
false
false
false
+ 0
15214
@@ -121718,6 +136932,7 @@
false
false
false
+ 0
15215
@@ -121726,6 +136941,7 @@
false
false
false
+ 0
15216
@@ -121734,6 +136950,7 @@
false
false
false
+ 0
15217
@@ -121742,6 +136959,7 @@
false
false
false
+ 0
15218
@@ -121750,6 +136968,7 @@
false
false
false
+ 0
15219
@@ -121758,6 +136977,7 @@
false
false
false
+ 0
15220
@@ -121766,6 +136986,7 @@
false
false
false
+ 0
15221
@@ -121774,6 +136995,7 @@
false
false
false
+ 0
15222
@@ -121782,6 +137004,7 @@
false
false
false
+ 0
15223
@@ -121790,6 +137013,7 @@
false
false
false
+ 0
15224
@@ -121798,6 +137022,7 @@
false
false
false
+ 0
15225
@@ -121806,6 +137031,7 @@
false
false
false
+ 0
15226
@@ -121814,6 +137040,7 @@
false
false
false
+ 0
15227
@@ -121822,6 +137049,7 @@
false
false
false
+ 0
15228
@@ -121830,6 +137058,7 @@
false
false
false
+ 0
15229
@@ -121838,6 +137067,7 @@
false
false
false
+ 0
15230
@@ -121846,6 +137076,7 @@
false
false
false
+ 0
15231
@@ -121854,6 +137085,7 @@
false
false
false
+ 0
15232
@@ -121862,6 +137094,7 @@
false
false
false
+ 0
15233
@@ -121870,6 +137103,7 @@
false
false
false
+ 0
15234
@@ -121878,6 +137112,7 @@
false
false
false
+ 0
15235
@@ -121886,6 +137121,7 @@
false
false
false
+ 0
15236
@@ -121894,6 +137130,7 @@
false
false
false
+ 0
15237
@@ -121902,6 +137139,7 @@
false
false
false
+ 0
15238
@@ -121910,6 +137148,7 @@
false
false
false
+ 0
15239
@@ -121918,6 +137157,7 @@
false
false
false
+ 0
15240
@@ -121926,6 +137166,7 @@
false
false
false
+ 0
15241
@@ -121934,6 +137175,7 @@
false
false
false
+ 0
15242
@@ -121942,6 +137184,7 @@
false
false
false
+ 0
15243
@@ -121950,6 +137193,7 @@
false
false
false
+ 0
15244
@@ -121958,6 +137202,7 @@
false
false
false
+ 0
15245
@@ -121966,6 +137211,7 @@
false
false
false
+ 0
15246
@@ -121974,6 +137220,7 @@
false
false
false
+ 0
15247
@@ -121982,6 +137229,7 @@
false
false
false
+ 0
15248
@@ -121990,6 +137238,7 @@
false
false
false
+ 0
15249
@@ -121998,6 +137247,7 @@
false
false
false
+ 0
15250
@@ -122006,6 +137256,7 @@
false
false
false
+ 0
15251
@@ -122014,6 +137265,7 @@
false
false
false
+ 0
15252
@@ -122022,6 +137274,7 @@
false
false
false
+ 0
15253
@@ -122030,6 +137283,7 @@
false
false
false
+ 0
15254
@@ -122038,6 +137292,7 @@
false
false
false
+ 0
15255
@@ -122046,6 +137301,7 @@
false
false
false
+ 0
15256
@@ -122054,6 +137310,7 @@
false
false
false
+ 0
15257
@@ -122062,6 +137319,7 @@
false
false
false
+ 0
15258
@@ -122070,6 +137328,7 @@
false
false
false
+ 0
15259
@@ -122078,6 +137337,7 @@
false
false
false
+ 0
15260
@@ -122086,6 +137346,7 @@
false
false
false
+ 0
15261
@@ -122094,6 +137355,7 @@
false
false
false
+ 0
15262
@@ -122102,6 +137364,7 @@
false
false
false
+ 0
15263
@@ -122110,6 +137373,7 @@
false
false
false
+ 0
15264
@@ -122118,6 +137382,7 @@
false
false
false
+ 0
15265
@@ -122126,6 +137391,7 @@
false
false
false
+ 0
15266
@@ -122134,6 +137400,7 @@
false
false
false
+ 0
15267
@@ -122142,6 +137409,7 @@
false
false
false
+ 0
15268
@@ -122150,6 +137418,7 @@
false
false
false
+ 0
15269
@@ -122158,6 +137427,7 @@
false
false
false
+ 0
15270
@@ -122166,6 +137436,7 @@
false
false
false
+ 0
15271
@@ -122174,6 +137445,7 @@
false
false
false
+ 0
15272
@@ -122182,6 +137454,7 @@
false
false
false
+ 0
15273
@@ -122190,6 +137463,7 @@
false
false
false
+ 0
15274
@@ -122198,6 +137472,7 @@
false
false
false
+ 0
15275
@@ -122206,6 +137481,7 @@
false
false
false
+ 0
15276
@@ -122214,6 +137490,7 @@
false
false
false
+ 0
15277
@@ -122222,6 +137499,7 @@
false
false
false
+ 0
15278
@@ -122230,6 +137508,7 @@
false
false
false
+ 0
15279
@@ -122238,6 +137517,7 @@
false
false
false
+ 0
15280
@@ -122246,6 +137526,7 @@
false
false
false
+ 0
15281
@@ -122254,6 +137535,7 @@
false
false
false
+ 0
15282
@@ -122262,6 +137544,7 @@
false
false
false
+ 0
15283
@@ -122270,6 +137553,7 @@
false
false
false
+ 0
15284
@@ -122278,6 +137562,7 @@
false
false
false
+ 0
15285
@@ -122286,6 +137571,7 @@
false
false
false
+ 0
15286
@@ -122294,6 +137580,7 @@
false
false
false
+ 0
15287
@@ -122302,6 +137589,7 @@
false
false
false
+ 0
15288
@@ -122310,6 +137598,7 @@
false
false
false
+ 0
15289
@@ -122318,6 +137607,7 @@
false
false
false
+ 0
15290
@@ -122326,6 +137616,7 @@
false
false
false
+ 0
15291
@@ -122334,6 +137625,7 @@
false
false
false
+ 0
15292
@@ -122342,6 +137634,7 @@
false
false
false
+ 0
15293
@@ -122350,6 +137643,7 @@
false
false
false
+ 0
15294
@@ -122358,6 +137652,7 @@
false
false
false
+ 0
15295
@@ -122366,6 +137661,7 @@
false
false
false
+ 0
15296
@@ -122374,6 +137670,7 @@
false
false
false
+ 0
15297
@@ -122382,6 +137679,7 @@
false
false
false
+ 0
15298
@@ -122390,6 +137688,7 @@
false
false
false
+ 0
15299
@@ -122398,6 +137697,7 @@
false
false
false
+ 0
15300
@@ -122406,6 +137706,7 @@
false
false
false
+ 0
15301
@@ -122414,6 +137715,7 @@
false
false
false
+ 0
15302
@@ -122422,6 +137724,7 @@
false
false
false
+ 0
15303
@@ -122430,6 +137733,7 @@
false
false
false
+ 0
15304
@@ -122438,6 +137742,7 @@
false
false
false
+ 0
15305
@@ -122446,6 +137751,7 @@
false
false
false
+ 0
15306
@@ -122454,6 +137760,7 @@
false
false
false
+ 0
15307
@@ -122462,6 +137769,7 @@
false
false
false
+ 0
15308
@@ -122470,6 +137778,7 @@
false
false
false
+ 0
15309
@@ -122478,6 +137787,7 @@
false
false
false
+ 0
15310
@@ -122486,6 +137796,7 @@
false
false
false
+ 0
15311
@@ -122494,6 +137805,7 @@
false
false
false
+ 0
15312
@@ -122502,6 +137814,7 @@
false
false
false
+ 0
15313
@@ -122510,6 +137823,7 @@
false
false
false
+ 0
15314
@@ -122518,6 +137832,7 @@
false
false
false
+ 0
15315
@@ -122526,6 +137841,7 @@
false
false
false
+ 0
15316
@@ -122534,6 +137850,7 @@
false
false
false
+ 0
15317
@@ -122542,6 +137859,7 @@
false
false
false
+ 0
15318
@@ -122550,6 +137868,7 @@
false
false
false
+ 0
15319
@@ -122558,6 +137877,7 @@
false
false
false
+ 0
15320
@@ -122566,6 +137886,7 @@
false
false
false
+ 0
15321
@@ -122574,6 +137895,7 @@
false
false
false
+ 0
15322
@@ -122582,6 +137904,7 @@
false
false
false
+ 0
15323
@@ -122590,6 +137913,7 @@
false
false
false
+ 0
15324
@@ -122598,6 +137922,7 @@
false
false
false
+ 0
15325
@@ -122606,6 +137931,7 @@
false
false
false
+ 0
15326
@@ -122614,6 +137940,7 @@
false
false
false
+ 0
15327
@@ -122622,6 +137949,7 @@
false
false
false
+ 0
15328
@@ -122630,6 +137958,7 @@
false
false
false
+ 0
15329
@@ -122638,6 +137967,7 @@
false
false
false
+ 0
15330
@@ -122646,6 +137976,7 @@
false
false
false
+ 0
15331
@@ -122654,6 +137985,7 @@
false
false
false
+ 0
15332
@@ -122662,6 +137994,7 @@
false
false
false
+ 0
15333
@@ -122670,6 +138003,7 @@
false
false
false
+ 0
15334
@@ -122678,6 +138012,7 @@
false
false
false
+ 0
15335
@@ -122686,6 +138021,7 @@
false
false
false
+ 0
15336
@@ -122694,6 +138030,7 @@
false
false
false
+ 0
15337
@@ -122702,6 +138039,7 @@
false
false
false
+ 0
15338
@@ -122710,6 +138048,7 @@
false
false
false
+ 0
15339
@@ -122718,6 +138057,7 @@
false
false
false
+ 0
15340
@@ -122726,6 +138066,7 @@
false
false
false
+ 0
15341
@@ -122734,6 +138075,7 @@
false
false
false
+ 0
15342
@@ -122742,6 +138084,7 @@
false
false
false
+ 0
15343
@@ -122750,6 +138093,7 @@
false
false
false
+ 0
15344
@@ -122758,6 +138102,7 @@
false
false
false
+ 0
15345
@@ -122766,6 +138111,7 @@
false
false
false
+ 0
15346
@@ -122774,6 +138120,7 @@
false
false
false
+ 0
15347
@@ -122782,6 +138129,7 @@
false
false
false
+ 0
15348
@@ -122790,6 +138138,7 @@
false
false
false
+ 0
15349
@@ -122798,6 +138147,7 @@
false
false
false
+ 0
15350
@@ -122806,6 +138156,7 @@
false
false
false
+ 0
15351
@@ -122814,6 +138165,7 @@
false
false
false
+ 0
15352
@@ -122822,6 +138174,7 @@
false
false
false
+ 0
15353
@@ -122830,6 +138183,7 @@
false
false
false
+ 0
15354
@@ -122838,6 +138192,7 @@
false
false
false
+ 0
15355
@@ -122846,6 +138201,7 @@
false
false
false
+ 0
15356
@@ -122854,6 +138210,7 @@
false
false
false
+ 0
15357
@@ -122862,6 +138219,7 @@
false
false
false
+ 0
15358
@@ -122870,6 +138228,7 @@
false
false
false
+ 0
15359
@@ -122878,6 +138237,7 @@
false
false
false
+ 0
15360
@@ -122886,6 +138246,7 @@
false
false
false
+ 0
15361
@@ -122894,6 +138255,7 @@
false
false
false
+ 0
15362
@@ -122902,6 +138264,7 @@
false
false
false
+ 0
15363
@@ -122910,6 +138273,7 @@
false
false
false
+ 0
15364
@@ -122918,6 +138282,7 @@
false
false
false
+ 0
15365
@@ -122926,6 +138291,7 @@
false
false
false
+ 0
15366
@@ -122934,6 +138300,7 @@
false
false
false
+ 0
15367
@@ -122942,6 +138309,7 @@
false
false
false
+ 0
15368
@@ -122950,6 +138318,7 @@
false
false
false
+ 0
15369
@@ -122958,6 +138327,7 @@
false
false
false
+ 0
15370
@@ -122966,6 +138336,7 @@
false
false
false
+ 0
15371
@@ -122974,6 +138345,7 @@
false
false
false
+ 0
15372
@@ -122982,6 +138354,7 @@
false
false
false
+ 0
15373
@@ -122990,6 +138363,7 @@
false
false
false
+ 0
15374
@@ -122998,6 +138372,7 @@
false
false
false
+ 0
15375
@@ -123006,6 +138381,7 @@
false
false
false
+ 0
15376
@@ -123014,6 +138390,7 @@
false
false
false
+ 0
15377
@@ -123022,6 +138399,7 @@
false
false
false
+ 0
15378
@@ -123030,6 +138408,7 @@
false
false
false
+ 0
15379
@@ -123038,6 +138417,7 @@
false
false
false
+ 0
15380
@@ -123046,6 +138426,7 @@
false
false
false
+ 0
15381
@@ -123054,6 +138435,7 @@
false
false
false
+ 0
15382
@@ -123062,6 +138444,7 @@
false
false
false
+ 0
15383
@@ -123070,6 +138453,7 @@
false
false
false
+ 0
15384
@@ -123078,6 +138462,7 @@
false
false
false
+ 0
15385
@@ -123086,6 +138471,7 @@
false
false
false
+ 0
15386
@@ -123094,6 +138480,7 @@
false
false
false
+ 0
15387
@@ -123102,6 +138489,7 @@
false
false
false
+ 0
15388
@@ -123110,6 +138498,7 @@
false
false
false
+ 0
15389
@@ -123118,6 +138507,7 @@
false
false
false
+ 0
15390
@@ -123126,6 +138516,7 @@
false
false
false
+ 0
15391
@@ -123134,6 +138525,7 @@
false
false
false
+ 0
15392
@@ -123142,6 +138534,7 @@
false
false
false
+ 0
15393
@@ -123150,6 +138543,7 @@
false
false
false
+ 0
15394
@@ -123158,6 +138552,7 @@
false
false
false
+ 0
15395
@@ -123166,6 +138561,7 @@
false
false
false
+ 0
15396
@@ -123174,6 +138570,7 @@
false
false
false
+ 0
15397
@@ -123182,6 +138579,7 @@
false
false
false
+ 0
15398
@@ -123190,6 +138588,7 @@
false
false
false
+ 0
15399
@@ -123198,6 +138597,7 @@
false
false
false
+ 0
15400
@@ -123206,6 +138606,7 @@
false
false
false
+ 0
15401
@@ -123214,6 +138615,7 @@
false
false
false
+ 0
15402
@@ -123222,6 +138624,7 @@
false
false
false
+ 0
15403
@@ -123230,6 +138633,7 @@
false
false
false
+ 0
15404
@@ -123238,6 +138642,7 @@
false
false
false
+ 0
15405
@@ -123246,6 +138651,7 @@
false
false
false
+ 0
15406
@@ -123254,6 +138660,7 @@
false
false
false
+ 0
15407
@@ -123262,6 +138669,7 @@
false
false
false
+ 0
15408
@@ -123270,6 +138678,7 @@
false
false
false
+ 0
15409
@@ -123278,6 +138687,7 @@
false
false
false
+ 0
15410
@@ -123286,6 +138696,7 @@
false
false
false
+ 0
15411
@@ -123294,6 +138705,7 @@
false
false
false
+ 0
15412
@@ -123302,6 +138714,7 @@
false
false
false
+ 0
15413
@@ -123310,6 +138723,7 @@
false
false
false
+ 0
15414
@@ -123318,6 +138732,7 @@
false
false
false
+ 0
15415
@@ -123326,6 +138741,7 @@
false
false
false
+ 0
15416
@@ -123334,6 +138750,7 @@
false
false
false
+ 0
15417
@@ -123342,6 +138759,7 @@
false
false
false
+ 0
15418
@@ -123350,6 +138768,7 @@
false
false
false
+ 0
15419
@@ -123358,6 +138777,7 @@
false
false
false
+ 0
15420
@@ -123366,6 +138786,7 @@
false
false
false
+ 0
15421
@@ -123374,6 +138795,7 @@
false
false
false
+ 0
15422
@@ -123382,6 +138804,7 @@
false
false
false
+ 0
15423
@@ -123390,6 +138813,7 @@
false
false
false
+ 0
15424
@@ -123398,6 +138822,7 @@
false
false
false
+ 0
15425
@@ -123406,6 +138831,7 @@
false
false
false
+ 0
15426
@@ -123414,6 +138840,7 @@
false
false
false
+ 0
15427
@@ -123422,6 +138849,7 @@
false
false
false
+ 0
15428
@@ -123430,6 +138858,7 @@
false
false
false
+ 0
15429
@@ -123438,6 +138867,7 @@
false
false
false
+ 0
15430
@@ -123446,6 +138876,7 @@
false
false
false
+ 0
15431
@@ -123454,6 +138885,7 @@
false
false
false
+ 0
15432
@@ -123462,6 +138894,7 @@
false
false
false
+ 0
15433
@@ -123470,6 +138903,7 @@
false
false
false
+ 0
15434
@@ -123478,6 +138912,7 @@
false
false
false
+ 0
15435
@@ -123486,6 +138921,7 @@
false
false
false
+ 0
15436
@@ -123494,6 +138930,7 @@
false
false
false
+ 0
15437
@@ -123502,6 +138939,7 @@
false
false
false
+ 0
15438
@@ -123510,6 +138948,7 @@
false
false
false
+ 0
15439
@@ -123518,6 +138957,7 @@
false
false
false
+ 0
15440
@@ -123526,6 +138966,7 @@
false
false
false
+ 0
15441
@@ -123534,6 +138975,7 @@
false
false
false
+ 0
15442
@@ -123542,6 +138984,7 @@
false
false
false
+ 0
15443
@@ -123550,6 +138993,7 @@
false
false
false
+ 0
15444
@@ -123558,6 +139002,7 @@
false
false
false
+ 0
15445
@@ -123566,6 +139011,7 @@
false
false
false
+ 0
15446
@@ -123574,6 +139020,7 @@
false
false
false
+ 0
15447
@@ -123582,6 +139029,7 @@
false
false
false
+ 0
15448
@@ -123590,6 +139038,7 @@
false
false
false
+ 0
15449
@@ -123598,6 +139047,7 @@
false
false
false
+ 0
15450
@@ -123606,6 +139056,7 @@
false
false
false
+ 0
15451
@@ -123614,6 +139065,7 @@
false
false
false
+ 0
15452
@@ -123622,6 +139074,7 @@
false
false
false
+ 0
15453
@@ -123630,6 +139083,7 @@
false
false
false
+ 0
15454
@@ -123638,6 +139092,7 @@
false
false
false
+ 0
15455
@@ -123646,6 +139101,7 @@
false
false
false
+ 0
15456
@@ -123654,6 +139110,7 @@
false
false
false
+ 0
15457
@@ -123662,6 +139119,7 @@
false
false
false
+ 0
15458
@@ -123670,6 +139128,7 @@
false
false
false
+ 0
15459
@@ -123678,6 +139137,7 @@
false
false
false
+ 0
15460
@@ -123686,6 +139146,7 @@
false
false
false
+ 0
15461
@@ -123694,6 +139155,7 @@
false
false
false
+ 0
15462
@@ -123702,6 +139164,7 @@
false
false
false
+ 0
15463
@@ -123710,6 +139173,7 @@
false
false
false
+ 0
15464
@@ -123718,6 +139182,7 @@
false
false
false
+ 0
15465
@@ -123726,6 +139191,7 @@
false
false
false
+ 0
15466
@@ -123734,6 +139200,7 @@
false
false
false
+ 0
15467
@@ -123742,6 +139209,7 @@
false
false
false
+ 0
15468
@@ -123750,6 +139218,7 @@
false
false
false
+ 0
15469
@@ -123758,6 +139227,7 @@
false
false
false
+ 0
15470
@@ -123766,6 +139236,7 @@
false
false
false
+ 0
15471
@@ -123774,6 +139245,7 @@
false
false
false
+ 0
15472
@@ -123782,6 +139254,7 @@
false
false
false
+ 0
15473
@@ -123790,6 +139263,7 @@
false
false
false
+ 0
15474
@@ -123798,6 +139272,7 @@
false
false
false
+ 0
15475
@@ -123806,6 +139281,7 @@
false
false
false
+ 0
15476
@@ -123814,6 +139290,7 @@
false
false
false
+ 0
15477
@@ -123822,6 +139299,7 @@
false
false
false
+ 0
15478
@@ -123830,6 +139308,7 @@
false
false
false
+ 0
15479
@@ -123838,6 +139317,7 @@
false
false
false
+ 0
15480
@@ -123846,6 +139326,7 @@
false
false
false
+ 0
15481
@@ -123854,6 +139335,7 @@
false
false
false
+ 0
15482
@@ -123862,6 +139344,7 @@
false
false
false
+ 0
15483
@@ -123870,6 +139353,7 @@
false
false
false
+ 0
15484
@@ -123878,6 +139362,7 @@
false
false
false
+ 0
15485
@@ -123886,6 +139371,7 @@
false
false
false
+ 0
15486
@@ -123894,6 +139380,7 @@
false
false
false
+ 0
15487
@@ -123902,6 +139389,7 @@
false
false
false
+ 0
15488
@@ -123910,6 +139398,7 @@
false
false
false
+ 0
15489
@@ -123918,6 +139407,7 @@
false
false
false
+ 0
15490
@@ -123926,6 +139416,7 @@
false
false
false
+ 0
15491
@@ -123934,6 +139425,7 @@
false
false
false
+ 0
15492
@@ -123942,6 +139434,7 @@
false
false
false
+ 0
15493
@@ -123950,6 +139443,7 @@
false
false
false
+ 0
15494
@@ -123958,6 +139452,7 @@
false
false
false
+ 0
15495
@@ -123966,6 +139461,7 @@
false
false
false
+ 0
15496
@@ -123974,6 +139470,7 @@
false
false
false
+ 0
15497
@@ -123982,6 +139479,7 @@
false
false
false
+ 0
15498
@@ -123990,6 +139488,7 @@
false
false
false
+ 0
15499
@@ -123998,6 +139497,7 @@
false
false
false
+ 0
15500
@@ -124006,6 +139506,7 @@
false
false
false
+ 0
15501
@@ -124014,6 +139515,7 @@
false
false
false
+ 0
15502
@@ -124022,6 +139524,7 @@
false
false
false
+ 0
15503
@@ -124030,6 +139533,7 @@
false
false
false
+ 0
15504
@@ -124038,6 +139542,7 @@
false
false
false
+ 0
15505
@@ -124046,6 +139551,7 @@
false
false
false
+ 0
15506
@@ -124054,6 +139560,7 @@
false
false
false
+ 0
15507
@@ -124062,6 +139569,7 @@
false
false
false
+ 0
15508
@@ -124070,6 +139578,7 @@
false
false
false
+ 0
15509
@@ -124078,6 +139587,7 @@
false
false
false
+ 0
15510
@@ -124086,6 +139596,7 @@
false
false
false
+ 0
15511
@@ -124094,6 +139605,7 @@
false
false
false
+ 0
15512
@@ -124102,6 +139614,7 @@
false
false
false
+ 0
15513
@@ -124110,6 +139623,7 @@
false
false
false
+ 0
15514
@@ -124118,6 +139632,7 @@
false
false
false
+ 0
15515
@@ -124126,6 +139641,7 @@
false
false
false
+ 0
15516
@@ -124134,6 +139650,7 @@
false
false
false
+ 0
15517
@@ -124142,6 +139659,7 @@
false
false
false
+ 0
15518
@@ -124150,6 +139668,7 @@
false
false
false
+ 0
15519
@@ -124158,6 +139677,7 @@
false
false
false
+ 0
15520
@@ -124166,6 +139686,7 @@
false
false
false
+ 0
15521
@@ -124174,6 +139695,7 @@
false
false
false
+ 0
15522
@@ -124182,6 +139704,7 @@
false
false
false
+ 0
15523
@@ -124190,6 +139713,7 @@
false
false
false
+ 0
15524
@@ -124198,6 +139722,7 @@
false
false
false
+ 0
15525
@@ -124206,6 +139731,7 @@
false
false
false
+ 0
15526
@@ -124214,6 +139740,7 @@
false
false
false
+ 0
15527
@@ -124222,6 +139749,7 @@
false
false
false
+ 0
15528
@@ -124230,6 +139758,7 @@
false
false
false
+ 0
15529
@@ -124238,6 +139767,7 @@
false
false
false
+ 0
15530
@@ -124246,6 +139776,7 @@
false
false
false
+ 0
15531
@@ -124254,6 +139785,7 @@
false
false
false
+ 0
15532
@@ -124262,6 +139794,7 @@
false
false
false
+ 0
15533
@@ -124270,6 +139803,7 @@
false
false
false
+ 0
15534
@@ -124278,6 +139812,7 @@
false
false
false
+ 0
15535
@@ -124286,6 +139821,7 @@
false
false
false
+ 0
15536
@@ -124294,6 +139830,7 @@
false
false
false
+ 0
15537
@@ -124302,6 +139839,7 @@
false
false
false
+ 0
15538
@@ -124310,6 +139848,7 @@
false
false
false
+ 0
15539
@@ -124318,6 +139857,7 @@
false
false
false
+ 0
15540
@@ -124326,6 +139866,7 @@
false
false
false
+ 0
15541
@@ -124334,6 +139875,7 @@
false
false
false
+ 0
15542
@@ -124342,6 +139884,7 @@
false
false
false
+ 0
15543
@@ -124350,6 +139893,7 @@
false
false
false
+ 0
15544
@@ -124358,6 +139902,7 @@
false
false
false
+ 0
15545
@@ -124366,6 +139911,7 @@
false
false
false
+ 0
15546
@@ -124374,6 +139920,7 @@
false
false
false
+ 0
15547
@@ -124382,6 +139929,7 @@
false
false
false
+ 0
15548
@@ -124390,6 +139938,7 @@
false
false
false
+ 0
15549
@@ -124398,6 +139947,7 @@
false
false
false
+ 0
15550
@@ -124406,6 +139956,7 @@
false
false
false
+ 0
15551
@@ -124414,6 +139965,7 @@
false
false
false
+ 0
15552
@@ -124422,6 +139974,7 @@
false
false
false
+ 0
15553
@@ -124430,6 +139983,7 @@
false
false
false
+ 0
15554
@@ -124438,6 +139992,7 @@
false
false
false
+ 0
15555
@@ -124446,6 +140001,7 @@
false
false
false
+ 0
15556
@@ -124454,6 +140010,7 @@
false
false
false
+ 0
15557
@@ -124462,6 +140019,7 @@
false
false
false
+ 0
15558
@@ -124470,6 +140028,7 @@
false
false
false
+ 0
15559
@@ -124478,6 +140037,7 @@
false
false
false
+ 0
15560
@@ -124486,6 +140046,7 @@
false
false
false
+ 0
15561
@@ -124494,6 +140055,7 @@
false
false
false
+ 0
15562
@@ -124502,6 +140064,7 @@
false
false
false
+ 0
15563
@@ -124510,6 +140073,7 @@
false
false
false
+ 0
15564
@@ -124518,6 +140082,7 @@
false
false
false
+ 0
15565
@@ -124526,6 +140091,7 @@
false
false
false
+ 0
15566
@@ -124534,6 +140100,7 @@
false
false
false
+ 0
15567
@@ -124542,6 +140109,7 @@
false
false
false
+ 0
15568
@@ -124550,6 +140118,7 @@
false
false
false
+ 0
15569
@@ -124558,6 +140127,7 @@
false
false
false
+ 0
15570
@@ -124566,6 +140136,7 @@
false
false
false
+ 0
15571
@@ -124574,6 +140145,7 @@
false
false
false
+ 0
15572
@@ -124582,6 +140154,7 @@
false
false
false
+ 0
15573
@@ -124590,6 +140163,7 @@
false
false
false
+ 0
15574
@@ -124598,6 +140172,7 @@
false
false
false
+ 0
15575
@@ -124606,6 +140181,7 @@
false
false
false
+ 0
15576
@@ -124614,6 +140190,7 @@
false
false
false
+ 0
15577
@@ -124622,6 +140199,7 @@
false
false
false
+ 0
15578
@@ -124630,6 +140208,7 @@
false
false
false
+ 0
15579
@@ -124638,6 +140217,7 @@
false
false
false
+ 0
15580
@@ -124646,6 +140226,7 @@
false
false
false
+ 0
15581
@@ -124654,6 +140235,7 @@
false
false
false
+ 0
15582
@@ -124662,6 +140244,7 @@
false
false
false
+ 0
15583
@@ -124670,6 +140253,7 @@
false
false
false
+ 0
15584
@@ -124678,6 +140262,7 @@
false
false
false
+ 0
15585
@@ -124686,6 +140271,7 @@
false
false
false
+ 0
15586
@@ -124694,6 +140280,7 @@
false
false
false
+ 0
15587
@@ -124702,6 +140289,7 @@
false
false
false
+ 0
15588
@@ -124710,6 +140298,7 @@
false
false
false
+ 0
15589
@@ -124718,6 +140307,7 @@
false
false
false
+ 0
15590
@@ -124726,6 +140316,7 @@
false
false
false
+ 0
15591
@@ -124734,6 +140325,7 @@
false
false
false
+ 0
15592
@@ -124742,6 +140334,7 @@
false
false
false
+ 0
15593
@@ -124750,6 +140343,7 @@
false
false
false
+ 0
15594
@@ -124758,6 +140352,7 @@
false
false
false
+ 0
15595
@@ -124766,6 +140361,7 @@
false
false
false
+ 0
15596
@@ -124774,6 +140370,7 @@
false
false
false
+ 0
15597
@@ -124782,6 +140379,7 @@
false
false
false
+ 0
15598
@@ -124790,6 +140388,7 @@
false
false
false
+ 0
15599
@@ -124798,6 +140397,7 @@
false
false
false
+ 0
15600
@@ -124806,6 +140406,7 @@
false
false
false
+ 0
15601
@@ -124814,6 +140415,7 @@
false
false
false
+ 0
15602
@@ -124822,6 +140424,7 @@
false
false
false
+ 0
15603
@@ -124830,6 +140433,7 @@
false
false
false
+ 0
15604
@@ -124838,6 +140442,7 @@
false
false
false
+ 0
15605
@@ -124846,6 +140451,7 @@
false
false
false
+ 0
15606
@@ -124854,6 +140460,7 @@
false
false
false
+ 0
15607
@@ -124862,6 +140469,7 @@
false
false
false
+ 0
15608
@@ -124870,6 +140478,7 @@
false
false
false
+ 0
15609
@@ -124878,6 +140487,7 @@
false
false
false
+ 0
15610
@@ -124886,6 +140496,7 @@
false
false
false
+ 0
15611
@@ -124894,6 +140505,7 @@
false
false
false
+ 0
15612
@@ -124902,6 +140514,7 @@
false
false
false
+ 0
15613
@@ -124910,6 +140523,7 @@
false
false
false
+ 0
15614
@@ -124918,6 +140532,7 @@
false
false
false
+ 0
15615
@@ -124926,6 +140541,7 @@
false
false
false
+ 0
15616
@@ -124934,6 +140550,7 @@
false
false
false
+ 0
15617
@@ -124942,6 +140559,7 @@
false
false
false
+ 0
15618
@@ -124950,6 +140568,7 @@
false
false
false
+ 0
15619
@@ -124958,6 +140577,7 @@
false
false
false
+ 0
15620
@@ -124966,6 +140586,7 @@
false
false
false
+ 0
15621
@@ -124974,6 +140595,7 @@
false
false
false
+ 0
15622
@@ -124982,6 +140604,7 @@
false
false
false
+ 0
15623
@@ -124990,6 +140613,7 @@
false
false
false
+ 0
15624
@@ -124998,6 +140622,7 @@
false
false
false
+ 0
15625
@@ -125006,6 +140631,7 @@
false
false
false
+ 0
15626
@@ -125014,6 +140640,7 @@
false
false
false
+ 0
15627
@@ -125022,6 +140649,7 @@
false
false
false
+ 0
15628
@@ -125030,6 +140658,7 @@
false
false
false
+ 0
15629
@@ -125038,6 +140667,7 @@
false
false
false
+ 0
15630
@@ -125046,6 +140676,7 @@
false
false
false
+ 0
15631
@@ -125054,6 +140685,7 @@
false
false
false
+ 0
15632
@@ -125062,6 +140694,7 @@
false
false
false
+ 0
15633
@@ -125070,6 +140703,7 @@
false
false
false
+ 0
15634
@@ -125078,6 +140712,7 @@
false
false
false
+ 0
15635
@@ -125086,6 +140721,7 @@
false
false
false
+ 0
15636
@@ -125094,6 +140730,7 @@
false
false
false
+ 0
15637
@@ -125102,6 +140739,7 @@
false
false
false
+ 0
15638
@@ -125110,6 +140748,7 @@
false
false
false
+ 0
15639
@@ -125118,6 +140757,7 @@
false
false
false
+ 0
15640
@@ -125126,6 +140766,7 @@
false
false
false
+ 0
15641
@@ -125134,6 +140775,7 @@
false
false
false
+ 0
15642
@@ -125142,6 +140784,7 @@
false
false
false
+ 0
15643
@@ -125150,6 +140793,7 @@
false
false
false
+ 0
15644
@@ -125158,6 +140802,7 @@
false
false
false
+ 0
15645
@@ -125166,6 +140811,7 @@
false
false
false
+ 0
15646
@@ -125174,6 +140820,7 @@
false
false
false
+ 0
15647
@@ -125182,6 +140829,7 @@
false
false
false
+ 0
15648
@@ -125190,6 +140838,7 @@
false
false
false
+ 0
15649
@@ -125198,6 +140847,7 @@
false
false
false
+ 0
15650
@@ -125206,6 +140856,7 @@
false
false
false
+ 0
15651
@@ -125214,6 +140865,7 @@
false
false
false
+ 0
15652
@@ -125222,6 +140874,7 @@
false
false
false
+ 0
15653
@@ -125230,6 +140883,7 @@
false
false
false
+ 0
15654
@@ -125238,6 +140892,7 @@
false
false
false
+ 0
15655
@@ -125246,6 +140901,7 @@
false
false
false
+ 0
15656
@@ -125254,6 +140910,7 @@
false
false
false
+ 0
15657
@@ -125262,6 +140919,7 @@
false
false
false
+ 0
15658
@@ -125270,6 +140928,7 @@
false
false
false
+ 0
15659
@@ -125278,6 +140937,7 @@
false
false
false
+ 0
15660
@@ -125286,6 +140946,7 @@
false
false
false
+ 0
15661
@@ -125294,6 +140955,7 @@
false
false
false
+ 0
15662
@@ -125302,6 +140964,7 @@
false
false
false
+ 0
15663
@@ -125310,6 +140973,7 @@
false
false
false
+ 0
15664
@@ -125318,6 +140982,7 @@
false
false
false
+ 0
15665
@@ -125326,6 +140991,7 @@
false
false
false
+ 0
15666
@@ -125334,6 +141000,7 @@
false
false
false
+ 0
15667
@@ -125342,6 +141009,7 @@
false
false
false
+ 0
15668
@@ -125350,6 +141018,7 @@
false
false
false
+ 0
15669
@@ -125358,6 +141027,7 @@
false
false
false
+ 0
15670
@@ -125366,6 +141036,7 @@
false
false
false
+ 0
15671
@@ -125374,6 +141045,7 @@
false
false
false
+ 0
15672
@@ -125382,6 +141054,7 @@
false
false
false
+ 0
15673
@@ -125390,6 +141063,7 @@
false
false
false
+ 0
15674
@@ -125398,6 +141072,7 @@
false
false
false
+ 0
15675
@@ -125406,6 +141081,7 @@
false
false
false
+ 0
15676
@@ -125414,6 +141090,7 @@
false
false
false
+ 0
15677
@@ -125422,6 +141099,7 @@
false
false
false
+ 0
15678
@@ -125430,6 +141108,7 @@
false
false
false
+ 0
15679
@@ -125438,6 +141117,7 @@
false
false
false
+ 0
15680
@@ -125446,6 +141126,7 @@
false
false
false
+ 0
15681
@@ -125454,6 +141135,7 @@
false
false
false
+ 0
15682
@@ -125462,6 +141144,7 @@
false
false
false
+ 0
15683
@@ -125470,6 +141153,7 @@
false
false
false
+ 0
15684
@@ -125478,6 +141162,7 @@
false
false
false
+ 0
15685
@@ -125486,6 +141171,7 @@
false
false
false
+ 0
15686
@@ -125494,6 +141180,7 @@
false
false
false
+ 0
15687
@@ -125502,6 +141189,7 @@
false
false
false
+ 0
15688
@@ -125510,6 +141198,7 @@
false
false
false
+ 0
15689
@@ -125518,6 +141207,7 @@
false
false
false
+ 0
15690
@@ -125526,6 +141216,7 @@
false
false
false
+ 0
15691
@@ -125534,6 +141225,7 @@
false
false
false
+ 0
15692
@@ -125542,6 +141234,7 @@
false
false
false
+ 0
15693
@@ -125550,6 +141243,7 @@
false
false
false
+ 0
15694
@@ -125558,6 +141252,7 @@
false
false
false
+ 0
15695
@@ -125566,6 +141261,7 @@
false
false
false
+ 0
15696
@@ -125574,6 +141270,7 @@
false
false
false
+ 0
15697
@@ -125582,6 +141279,7 @@
false
false
false
+ 0
15698
@@ -125590,6 +141288,7 @@
false
false
false
+ 0
15699
@@ -125598,6 +141297,7 @@
false
false
false
+ 0
15700
@@ -125606,6 +141306,7 @@
false
false
false
+ 0
15701
@@ -125614,6 +141315,7 @@
false
false
false
+ 0
15702
@@ -125622,6 +141324,7 @@
false
false
false
+ 0
15703
@@ -125630,6 +141333,7 @@
false
false
false
+ 0
15704
@@ -125638,6 +141342,7 @@
false
false
false
+ 0
15705
@@ -125646,6 +141351,7 @@
false
false
false
+ 0
15706
@@ -125654,6 +141360,7 @@
false
false
false
+ 0
15707
@@ -125662,6 +141369,7 @@
false
false
false
+ 0
15708
@@ -125670,6 +141378,7 @@
false
false
false
+ 0
15709
@@ -125678,6 +141387,7 @@
false
false
false
+ 0
15710
@@ -125686,6 +141396,7 @@
false
false
false
+ 0
15711
@@ -125694,6 +141405,7 @@
false
false
false
+ 0
15712
@@ -125702,6 +141414,7 @@
false
false
false
+ 0
15713
@@ -125710,6 +141423,7 @@
false
false
false
+ 0
15714
@@ -125718,6 +141432,7 @@
false
false
false
+ 0
15715
@@ -125726,6 +141441,7 @@
false
false
false
+ 0
15716
@@ -125734,6 +141450,7 @@
false
false
false
+ 0
15717
@@ -125742,6 +141459,7 @@
false
false
false
+ 0
15718
@@ -125750,6 +141468,7 @@
false
false
false
+ 0
15719
@@ -125758,6 +141477,7 @@
false
false
false
+ 0
15720
@@ -125766,6 +141486,7 @@
false
false
false
+ 0
15721
@@ -125774,6 +141495,7 @@
false
false
false
+ 0
15722
@@ -125782,6 +141504,7 @@
false
false
false
+ 0
15723
@@ -125790,6 +141513,7 @@
false
false
false
+ 0
15724
@@ -125798,6 +141522,7 @@
false
false
false
+ 0
15725
@@ -125806,6 +141531,7 @@
false
false
false
+ 0
15726
@@ -125814,6 +141540,7 @@
false
false
false
+ 0
15727
@@ -125822,6 +141549,7 @@
false
false
false
+ 0
15728
@@ -125830,6 +141558,7 @@
false
false
false
+ 0
15729
@@ -125838,6 +141567,7 @@
false
false
false
+ 0
15730
@@ -125846,6 +141576,7 @@
false
false
false
+ 0
15731
@@ -125854,6 +141585,7 @@
false
false
false
+ 0
15732
@@ -125862,6 +141594,7 @@
false
false
false
+ 0
15733
@@ -125870,6 +141603,7 @@
false
false
false
+ 0
15734
@@ -125878,6 +141612,7 @@
false
false
false
+ 0
15735
@@ -125886,6 +141621,7 @@
false
false
false
+ 0
15736
@@ -125894,6 +141630,7 @@
false
false
false
+ 0
15737
@@ -125902,6 +141639,7 @@
false
false
false
+ 0
15738
@@ -125910,6 +141648,7 @@
false
false
false
+ 0
15739
@@ -125918,6 +141657,7 @@
false
false
false
+ 0
15740
@@ -125926,6 +141666,7 @@
false
false
false
+ 0
15741
@@ -125934,6 +141675,7 @@
false
false
false
+ 0
15742
@@ -125942,6 +141684,7 @@
false
false
false
+ 0
15743
@@ -125950,6 +141693,7 @@
false
false
false
+ 0
15744
@@ -125958,6 +141702,7 @@
false
false
false
+ 0
15745
@@ -125966,6 +141711,7 @@
false
false
false
+ 0
15746
@@ -125974,6 +141720,7 @@
false
false
false
+ 0
15747
@@ -125982,6 +141729,7 @@
false
false
false
+ 0
15748
@@ -125990,6 +141738,7 @@
false
false
false
+ 0
15749
@@ -125998,6 +141747,7 @@
false
false
false
+ 0
15750
@@ -126006,6 +141756,7 @@
false
false
false
+ 0
15751
@@ -126014,6 +141765,7 @@
false
false
false
+ 0
15752
@@ -126022,6 +141774,7 @@
false
false
false
+ 0
15753
@@ -126030,6 +141783,7 @@
false
false
false
+ 0
15754
@@ -126038,6 +141792,7 @@
false
false
false
+ 0
15755
@@ -126046,6 +141801,7 @@
false
false
false
+ 0
15756
@@ -126054,6 +141810,7 @@
false
false
false
+ 0
15757
@@ -126062,6 +141819,7 @@
false
false
false
+ 0
15758
@@ -126070,6 +141828,7 @@
false
false
false
+ 0
15759
@@ -126078,6 +141837,7 @@
false
false
false
+ 0
15760
@@ -126086,6 +141846,7 @@
false
false
false
+ 0
15761
@@ -126094,6 +141855,7 @@
false
false
false
+ 0
15762
@@ -126102,6 +141864,7 @@
false
false
false
+ 0
15763
@@ -126110,6 +141873,7 @@
false
false
false
+ 0
15764
@@ -126118,6 +141882,7 @@
false
false
false
+ 0
15765
@@ -126126,6 +141891,7 @@
false
false
false
+ 0
15766
@@ -126134,6 +141900,7 @@
false
false
false
+ 0
15767
@@ -126142,6 +141909,7 @@
false
false
false
+ 0
15768
@@ -126150,6 +141918,7 @@
false
false
false
+ 0
15769
@@ -126158,6 +141927,7 @@
false
false
false
+ 0
15770
@@ -126166,6 +141936,7 @@
false
false
false
+ 0
15771
@@ -126174,6 +141945,7 @@
false
false
false
+ 0
15772
@@ -126182,6 +141954,7 @@
false
false
false
+ 0
15773
@@ -126190,6 +141963,7 @@
false
false
false
+ 0
15774
@@ -126198,6 +141972,7 @@
false
false
false
+ 0
15775
@@ -126206,6 +141981,7 @@
false
false
false
+ 0
15776
@@ -126214,6 +141990,7 @@
false
false
false
+ 0
15777
@@ -126222,6 +141999,7 @@
false
false
false
+ 0
15778
@@ -126230,6 +142008,7 @@
false
false
false
+ 0
15779
@@ -126238,6 +142017,7 @@
false
false
false
+ 0
15780
@@ -126246,6 +142026,7 @@
false
false
false
+ 0
15781
@@ -126254,6 +142035,7 @@
false
false
false
+ 0
15782
@@ -126262,6 +142044,7 @@
false
false
false
+ 0
15783
@@ -126270,6 +142053,7 @@
false
false
false
+ 0
15784
@@ -126278,6 +142062,7 @@
false
false
false
+ 0
15785
@@ -126286,6 +142071,7 @@
false
false
false
+ 0
15786
@@ -126294,6 +142080,7 @@
false
false
false
+ 0
15787
@@ -126302,6 +142089,7 @@
false
false
false
+ 0
15788
@@ -126310,6 +142098,7 @@
false
false
false
+ 0
15789
@@ -126318,6 +142107,7 @@
false
false
false
+ 0
15790
@@ -126326,6 +142116,7 @@
false
false
false
+ 0
15791
@@ -126334,6 +142125,7 @@
false
false
false
+ 0
15792
@@ -126342,6 +142134,7 @@
false
false
false
+ 0
15793
@@ -126350,6 +142143,7 @@
false
false
false
+ 0
15794
@@ -126358,6 +142152,7 @@
false
false
false
+ 0
15795
@@ -126366,6 +142161,7 @@
false
false
false
+ 0
15796
@@ -126374,6 +142170,7 @@
false
false
false
+ 0
15797
@@ -126382,6 +142179,7 @@
false
false
false
+ 0
15798
@@ -126390,6 +142188,7 @@
false
false
false
+ 0
15799
@@ -126398,6 +142197,7 @@
false
false
false
+ 0
15800
@@ -126406,6 +142206,7 @@
false
false
false
+ 0
15801
@@ -126414,6 +142215,7 @@
false
false
false
+ 0
15802
@@ -126422,6 +142224,7 @@
false
false
false
+ 0
15803
@@ -126430,6 +142233,7 @@
false
false
false
+ 0
15804
@@ -126438,6 +142242,7 @@
false
false
false
+ 0
15805
@@ -126446,6 +142251,7 @@
false
false
false
+ 0
15806
@@ -126454,6 +142260,7 @@
false
false
false
+ 0
15807
@@ -126462,6 +142269,7 @@
false
false
false
+ 0
15808
@@ -126470,6 +142278,7 @@
false
false
false
+ 0
15809
@@ -126478,6 +142287,7 @@
false
false
false
+ 0
15810
@@ -126486,6 +142296,7 @@
false
false
false
+ 0
15811
@@ -126494,6 +142305,7 @@
false
false
false
+ 0
15812
@@ -126502,6 +142314,7 @@
false
false
false
+ 0
15813
@@ -126510,6 +142323,7 @@
false
false
false
+ 0
15814
@@ -126518,6 +142332,7 @@
false
false
false
+ 0
15815
@@ -126526,6 +142341,7 @@
false
false
false
+ 0
15816
@@ -126534,6 +142350,7 @@
false
false
false
+ 0
15817
@@ -126542,6 +142359,7 @@
false
false
false
+ 0
15818
@@ -126550,6 +142368,7 @@
false
false
false
+ 0
15819
@@ -126558,6 +142377,7 @@
false
false
false
+ 0
15820
@@ -126566,6 +142386,7 @@
false
false
false
+ 0
15821
@@ -126574,6 +142395,7 @@
false
false
false
+ 0
15822
@@ -126582,6 +142404,7 @@
false
false
false
+ 0
15823
@@ -126590,6 +142413,7 @@
false
false
false
+ 0
15824
@@ -126598,6 +142422,7 @@
false
false
false
+ 0
15825
@@ -126606,6 +142431,7 @@
false
false
false
+ 0
15826
@@ -126614,6 +142440,7 @@
false
false
false
+ 0
15827
@@ -126622,6 +142449,7 @@
false
false
false
+ 0
15828
@@ -126630,6 +142458,7 @@
false
false
false
+ 0
15829
@@ -126638,6 +142467,7 @@
false
false
false
+ 0
15830
@@ -126646,6 +142476,7 @@
false
false
false
+ 0
15831
@@ -126654,6 +142485,7 @@
false
false
false
+ 0
15832
@@ -126662,6 +142494,7 @@
false
false
false
+ 0
15833
@@ -126670,6 +142503,7 @@
false
false
false
+ 0
15834
@@ -126678,6 +142512,7 @@
false
false
false
+ 0
15835
@@ -126686,6 +142521,7 @@
false
false
false
+ 0
15836
@@ -126694,6 +142530,7 @@
false
false
false
+ 0
15837
@@ -126702,6 +142539,7 @@
false
false
false
+ 0
15838
@@ -126710,6 +142548,7 @@
false
false
false
+ 0
15839
@@ -126718,6 +142557,7 @@
false
false
false
+ 0
15840
@@ -126726,6 +142566,7 @@
false
false
false
+ 0
15841
@@ -126734,6 +142575,7 @@
false
false
false
+ 0
15842
@@ -126742,6 +142584,7 @@
false
false
false
+ 0
15843
@@ -126750,6 +142593,7 @@
false
false
false
+ 0
15844
@@ -126758,6 +142602,7 @@
false
false
false
+ 0
15845
@@ -126766,6 +142611,7 @@
false
false
false
+ 0
15846
@@ -126774,6 +142620,7 @@
false
false
false
+ 0
15847
@@ -126782,6 +142629,7 @@
false
false
false
+ 0
15848
@@ -126790,6 +142638,7 @@
false
false
false
+ 0
15849
@@ -126798,6 +142647,7 @@
false
false
false
+ 0
15850
@@ -126806,6 +142656,7 @@
false
false
false
+ 0
15851
@@ -126814,6 +142665,7 @@
false
false
false
+ 0
15852
@@ -126822,6 +142674,7 @@
false
false
false
+ 0
15853
@@ -126830,6 +142683,7 @@
false
false
false
+ 0
15854
@@ -126838,6 +142692,7 @@
false
false
false
+ 0
15855
@@ -126846,6 +142701,7 @@
false
false
false
+ 0
15856
@@ -126854,6 +142710,7 @@
false
false
false
+ 0
15857
@@ -126862,6 +142719,7 @@
false
false
false
+ 0
15858
@@ -126870,6 +142728,7 @@
false
false
false
+ 0
15859
@@ -126878,6 +142737,7 @@
false
false
false
+ 0
15860
@@ -126886,6 +142746,7 @@
false
false
false
+ 0
15861
@@ -126894,6 +142755,7 @@
false
false
false
+ 0
15862
@@ -126902,6 +142764,7 @@
false
false
false
+ 0
15863
@@ -126910,6 +142773,7 @@
false
false
false
+ 0
15864
@@ -126918,6 +142782,7 @@
false
false
false
+ 0
15865
@@ -126926,6 +142791,7 @@
false
false
false
+ 0
15866
@@ -126934,6 +142800,7 @@
false
false
false
+ 0
15867
@@ -126942,6 +142809,7 @@
false
false
false
+ 0
15868
@@ -126950,6 +142818,7 @@
false
false
false
+ 0
15869
@@ -126958,6 +142827,7 @@
false
false
false
+ 0
15870
@@ -126966,6 +142836,7 @@
false
false
false
+ 0
15871
@@ -126974,6 +142845,7 @@
false
false
false
+ 0
15872
@@ -126982,6 +142854,7 @@
false
false
false
+ 0
15873
@@ -126990,6 +142863,7 @@
false
false
false
+ 0
15874
@@ -126998,6 +142872,7 @@
false
false
false
+ 0
15875
@@ -127006,6 +142881,7 @@
false
false
false
+ 0
15876
@@ -127014,6 +142890,7 @@
false
false
false
+ 0
15877
@@ -127022,6 +142899,7 @@
false
false
false
+ 0
15878
@@ -127030,6 +142908,7 @@
false
false
false
+ 0
15879
@@ -127038,6 +142917,7 @@
false
false
false
+ 0
15880
@@ -127046,6 +142926,7 @@
false
false
false
+ 0
15881
@@ -127054,6 +142935,7 @@
false
false
false
+ 0
15882
@@ -127062,6 +142944,7 @@
false
false
false
+ 0
15883
@@ -127070,6 +142953,7 @@
false
false
false
+ 0
15884
@@ -127078,6 +142962,7 @@
false
false
false
+ 0
15885
@@ -127086,6 +142971,7 @@
false
false
false
+ 0
15886
@@ -127094,6 +142980,7 @@
false
false
false
+ 0
15887
@@ -127102,6 +142989,7 @@
false
false
false
+ 0
15888
@@ -127110,6 +142998,7 @@
false
false
false
+ 0
15889
@@ -127118,6 +143007,7 @@
false
false
false
+ 0
15890
@@ -127126,6 +143016,7 @@
false
false
false
+ 0
15891
@@ -127134,6 +143025,7 @@
false
false
false
+ 0
15892
@@ -127142,6 +143034,7 @@
false
false
false
+ 0
15893
@@ -127150,6 +143043,7 @@
false
false
false
+ 0
15894
@@ -127158,6 +143052,7 @@
false
false
false
+ 0
15895
@@ -127166,6 +143061,7 @@
false
false
false
+ 0
15896
@@ -127174,6 +143070,7 @@
false
false
false
+ 0
15897
@@ -127182,6 +143079,7 @@
false
false
false
+ 0
15898
@@ -127190,6 +143088,7 @@
false
false
false
+ 0
15899
@@ -127198,6 +143097,7 @@
false
false
false
+ 0
15900
@@ -127206,6 +143106,7 @@
false
false
false
+ 0
15901
@@ -127214,6 +143115,7 @@
false
false
false
+ 0
15902
@@ -127222,6 +143124,7 @@
false
false
false
+ 0
15903
@@ -127230,6 +143133,7 @@
false
false
false
+ 0
15904
@@ -127238,6 +143142,7 @@
false
false
false
+ 0
15905
@@ -127246,6 +143151,7 @@
false
false
false
+ 0
15906
@@ -127254,6 +143160,7 @@
false
false
false
+ 0
15907
@@ -127262,6 +143169,7 @@
false
false
false
+ 0
15908
@@ -127270,6 +143178,7 @@
false
false
false
+ 0
15909
@@ -127278,6 +143187,7 @@
false
false
false
+ 0
15910
@@ -127286,6 +143196,7 @@
false
false
false
+ 0
15911
@@ -127294,6 +143205,7 @@
false
false
false
+ 0
15912
@@ -127302,6 +143214,7 @@
false
false
false
+ 0
15913
@@ -127310,6 +143223,7 @@
false
false
false
+ 0
15914
@@ -127318,6 +143232,7 @@
false
false
false
+ 0
15915
@@ -127326,6 +143241,7 @@
false
false
false
+ 0
15916
@@ -127334,6 +143250,7 @@
false
false
false
+ 0
15917
@@ -127342,6 +143259,7 @@
false
false
false
+ 0
15918
@@ -127350,6 +143268,7 @@
false
false
false
+ 0
15919
@@ -127358,6 +143277,7 @@
false
false
false
+ 0
15920
@@ -127366,6 +143286,7 @@
false
false
false
+ 0
15921
@@ -127374,6 +143295,7 @@
false
false
false
+ 0
15922
@@ -127382,6 +143304,7 @@
false
false
false
+ 0
15923
@@ -127390,6 +143313,7 @@
false
false
false
+ 0
15924
@@ -127398,6 +143322,7 @@
false
false
false
+ 0
15925
@@ -127406,6 +143331,7 @@
false
false
false
+ 0
15926
@@ -127414,6 +143340,7 @@
false
false
false
+ 0
15927
@@ -127422,6 +143349,7 @@
false
false
false
+ 0
15928
@@ -127430,6 +143358,7 @@
false
false
false
+ 0
15929
@@ -127438,6 +143367,7 @@
false
false
false
+ 0
15930
@@ -127446,6 +143376,7 @@
false
false
false
+ 0
15931
@@ -127454,6 +143385,7 @@
false
false
false
+ 0
15932
@@ -127462,6 +143394,7 @@
false
false
false
+ 0
15933
@@ -127470,6 +143403,7 @@
false
false
false
+ 0
15934
@@ -127478,6 +143412,7 @@
false
false
false
+ 0
15935
@@ -127486,6 +143421,7 @@
false
false
false
+ 0
15936
@@ -127494,6 +143430,7 @@
false
false
false
+ 0
15937
@@ -127502,6 +143439,7 @@
false
false
false
+ 0
15938
@@ -127510,6 +143448,7 @@
false
false
false
+ 0
15939
@@ -127518,6 +143457,7 @@
false
false
false
+ 0
15940
@@ -127526,6 +143466,7 @@
false
false
false
+ 0
15941
@@ -127534,6 +143475,7 @@
false
false
false
+ 0
15942
@@ -127542,6 +143484,7 @@
false
false
false
+ 0
15943
@@ -127550,6 +143493,7 @@
false
false
false
+ 0
15944
@@ -127558,6 +143502,7 @@
false
false
false
+ 0
15945
@@ -127566,6 +143511,7 @@
false
false
false
+ 0
15946
@@ -127574,6 +143520,7 @@
false
false
false
+ 0
15947
@@ -127582,6 +143529,7 @@
false
false
false
+ 0
15948
@@ -127590,6 +143538,7 @@
false
false
false
+ 0
15949
@@ -127598,6 +143547,7 @@
false
false
false
+ 0
15950
@@ -127606,6 +143556,7 @@
false
false
false
+ 0
15951
@@ -127614,6 +143565,7 @@
false
false
false
+ 0
15952
@@ -127622,6 +143574,7 @@
false
false
false
+ 0
15953
@@ -127630,6 +143583,7 @@
false
false
false
+ 0
15954
@@ -127638,6 +143592,7 @@
false
false
false
+ 0
15955
@@ -127646,6 +143601,7 @@
false
false
false
+ 0
15956
@@ -127654,6 +143610,7 @@
false
false
false
+ 0
15957
@@ -127662,6 +143619,7 @@
false
false
false
+ 0
15958
@@ -127670,6 +143628,7 @@
false
false
false
+ 0
15959
@@ -127678,6 +143637,7 @@
false
false
false
+ 0
15960
@@ -127686,6 +143646,7 @@
false
false
false
+ 0
15961
@@ -127694,6 +143655,7 @@
false
false
false
+ 0
15962
@@ -127702,6 +143664,7 @@
false
false
false
+ 0
15963
@@ -127710,6 +143673,7 @@
false
false
false
+ 0
15964
@@ -127718,6 +143682,7 @@
false
false
false
+ 0
15965
@@ -127726,6 +143691,7 @@
false
false
false
+ 0
15966
@@ -127734,6 +143700,7 @@
false
false
false
+ 0
15967
@@ -127742,6 +143709,7 @@
false
false
false
+ 0
15968
@@ -127750,6 +143718,7 @@
false
false
false
+ 0
15969
@@ -127758,6 +143727,7 @@
false
false
false
+ 0
15970
@@ -127766,6 +143736,7 @@
false
false
false
+ 0
15971
@@ -127774,6 +143745,7 @@
false
false
false
+ 0
15972
@@ -127782,6 +143754,7 @@
false
false
false
+ 0
15973
@@ -127790,6 +143763,7 @@
false
false
false
+ 0
15974
@@ -127798,6 +143772,7 @@
false
false
false
+ 0
15975
@@ -127806,6 +143781,7 @@
false
false
false
+ 0
15976
@@ -127814,6 +143790,7 @@
false
false
false
+ 0
15977
@@ -127822,6 +143799,7 @@
false
false
false
+ 0
15978
@@ -127830,6 +143808,7 @@
false
false
false
+ 0
15979
@@ -127838,6 +143817,7 @@
false
false
false
+ 0
15980
@@ -127846,6 +143826,7 @@
false
false
false
+ 0
15981
@@ -127854,6 +143835,7 @@
false
false
false
+ 0
15982
@@ -127862,6 +143844,7 @@
false
false
false
+ 0
15983
@@ -127870,6 +143853,7 @@
false
false
false
+ 0
15984
@@ -127878,6 +143862,7 @@
false
false
false
+ 0
15985
@@ -127886,6 +143871,7 @@
false
false
false
+ 0
15986
@@ -127894,6 +143880,7 @@
false
false
false
+ 0
15987
@@ -127902,6 +143889,7 @@
false
false
false
+ 0
15988
@@ -127910,6 +143898,7 @@
false
false
false
+ 0
15989
@@ -127918,6 +143907,7 @@
false
false
false
+ 0
15990
@@ -127926,6 +143916,7 @@
false
false
false
+ 0
15991
@@ -127934,6 +143925,7 @@
false
false
false
+ 0
15992
@@ -127942,6 +143934,7 @@
false
false
false
+ 0
15993
@@ -127950,6 +143943,7 @@
false
false
false
+ 0
15994
@@ -127958,6 +143952,7 @@
false
false
false
+ 0
15995
@@ -127966,6 +143961,7 @@
false
false
false
+ 0
15996
@@ -127974,6 +143970,7 @@
false
false
false
+ 0
15997
@@ -127982,6 +143979,7 @@
false
false
false
+ 0
15998
@@ -127990,6 +143988,7 @@
false
false
false
+ 0
15999
@@ -127998,6 +143997,7 @@
false
false
false
+ 0
16000
@@ -128006,6 +144006,7 @@
false
false
false
+ 0
16001
@@ -128014,6 +144015,7 @@
false
false
false
+ 0
16002
@@ -128022,6 +144024,7 @@
false
false
false
+ 0
16003
@@ -128030,6 +144033,7 @@
false
false
false
+ 0
16004
@@ -128038,6 +144042,7 @@
false
false
false
+ 0
16005
@@ -128046,6 +144051,7 @@
false
false
false
+ 0
16006
@@ -128054,6 +144060,7 @@
false
false
false
+ 0
16007
@@ -128062,6 +144069,7 @@
false
false
false
+ 0
16008
@@ -128070,6 +144078,7 @@
false
false
false
+ 0
16009
@@ -128078,6 +144087,7 @@
false
false
false
+ 0
16010
@@ -128086,6 +144096,7 @@
false
false
false
+ 0
16011
@@ -128094,6 +144105,7 @@
false
false
false
+ 0
16012
@@ -128102,6 +144114,7 @@
false
false
false
+ 0
16013
@@ -128110,6 +144123,7 @@
false
false
false
+ 0
16014
@@ -128118,6 +144132,7 @@
false
false
false
+ 0
16015
@@ -128126,6 +144141,7 @@
false
false
false
+ 0
16016
@@ -128134,6 +144150,7 @@
false
false
false
+ 0
16017
@@ -128142,6 +144159,7 @@
false
false
false
+ 0
16018
@@ -128150,6 +144168,7 @@
false
false
false
+ 0
16019
@@ -128158,6 +144177,7 @@
false
false
false
+ 0
16020
@@ -128166,6 +144186,7 @@
false
false
false
+ 0
16021
@@ -128174,6 +144195,7 @@
false
false
false
+ 0
16022
@@ -128182,6 +144204,7 @@
false
false
false
+ 0
16023
@@ -128190,6 +144213,7 @@
false
false
false
+ 0
16024
@@ -128198,6 +144222,7 @@
false
false
false
+ 0
16025
@@ -128206,6 +144231,7 @@
false
false
false
+ 0
16026
@@ -128214,6 +144240,7 @@
false
false
false
+ 0
16027
@@ -128222,6 +144249,7 @@
false
false
false
+ 0
16028
@@ -128230,6 +144258,7 @@
false
false
false
+ 0
16029
@@ -128238,6 +144267,7 @@
false
false
false
+ 0
16030
@@ -128246,6 +144276,7 @@
false
false
false
+ 0
16031
@@ -128254,6 +144285,7 @@
false
false
false
+ 0
16032
@@ -128262,6 +144294,7 @@
false
false
false
+ 0
16033
@@ -128270,6 +144303,7 @@
false
false
false
+ 0
16034
@@ -128278,6 +144312,7 @@
false
false
false
+ 0
16035
@@ -128286,6 +144321,7 @@
false
false
false
+ 0
16036
@@ -128294,6 +144330,7 @@
false
false
false
+ 0
16037
@@ -128302,6 +144339,7 @@
false
false
false
+ 0
16038
@@ -128310,6 +144348,7 @@
false
false
false
+ 0
16039
@@ -128318,6 +144357,7 @@
false
false
false
+ 0
16040
@@ -128326,6 +144366,7 @@
false
false
false
+ 0
16041
@@ -128334,6 +144375,7 @@
false
false
false
+ 0
16042
@@ -128342,6 +144384,7 @@
false
false
false
+ 0
16043
@@ -128350,6 +144393,7 @@
false
false
false
+ 0
16044
@@ -128358,6 +144402,7 @@
false
false
false
+ 0
16045
@@ -128366,6 +144411,7 @@
false
false
false
+ 0
16046
@@ -128374,6 +144420,7 @@
false
false
false
+ 0
16047
@@ -128382,6 +144429,7 @@
false
false
false
+ 0
16048
@@ -128390,6 +144438,7 @@
false
false
false
+ 0
16049
@@ -128398,6 +144447,7 @@
false
false
false
+ 0
16050
@@ -128406,6 +144456,7 @@
false
false
false
+ 0
16051
@@ -128414,6 +144465,7 @@
false
false
false
+ 0
16052
@@ -128422,6 +144474,7 @@
false
false
false
+ 0
16053
@@ -128430,6 +144483,7 @@
false
false
false
+ 0
16054
@@ -128438,6 +144492,7 @@
false
false
false
+ 0
16055
@@ -128446,6 +144501,7 @@
false
false
false
+ 0
16056
@@ -128454,6 +144510,7 @@
false
false
false
+ 0
16057
@@ -128462,6 +144519,7 @@
false
false
false
+ 0
16058
@@ -128470,6 +144528,7 @@
false
false
false
+ 0
16059
@@ -128478,6 +144537,7 @@
false
false
false
+ 0
16060
@@ -128486,6 +144546,7 @@
false
false
false
+ 0
16061
@@ -128494,6 +144555,7 @@
false
false
false
+ 0
16062
@@ -128502,6 +144564,7 @@
false
false
false
+ 0
16063
@@ -128510,6 +144573,7 @@
false
false
false
+ 0
16064
@@ -128518,6 +144582,7 @@
false
false
false
+ 0
16065
@@ -128526,6 +144591,7 @@
false
false
false
+ 0
16066
@@ -128534,6 +144600,7 @@
false
false
false
+ 0
16067
@@ -128542,6 +144609,7 @@
false
false
false
+ 0
16068
@@ -128550,6 +144618,7 @@
false
false
false
+ 0
16069
@@ -128558,6 +144627,7 @@
false
false
false
+ 0
16070
@@ -128566,6 +144636,7 @@
false
false
false
+ 0
16071
@@ -128574,6 +144645,7 @@
false
false
false
+ 0
16072
@@ -128582,6 +144654,7 @@
false
false
false
+ 0
16073
@@ -128590,6 +144663,7 @@
false
false
false
+ 0
16074
@@ -128598,6 +144672,7 @@
false
false
false
+ 0
16075
@@ -128606,6 +144681,7 @@
false
false
false
+ 0
16076
@@ -128614,6 +144690,7 @@
false
false
false
+ 0
16077
@@ -128622,6 +144699,7 @@
false
false
false
+ 0
16078
@@ -128630,6 +144708,7 @@
false
false
false
+ 0
16079
@@ -128638,6 +144717,7 @@
false
false
false
+ 0
16080
@@ -128646,6 +144726,7 @@
false
false
false
+ 0
16081
@@ -128654,6 +144735,7 @@
false
false
false
+ 0
16082
@@ -128662,6 +144744,7 @@
false
false
false
+ 0
16083
@@ -128670,6 +144753,7 @@
false
false
false
+ 0
16084
@@ -128678,6 +144762,7 @@
false
false
false
+ 0
16085
@@ -128686,6 +144771,7 @@
false
false
false
+ 0
16086
@@ -128694,6 +144780,7 @@
false
false
false
+ 0
16087
@@ -128702,6 +144789,7 @@
false
false
false
+ 0
16088
@@ -128710,6 +144798,7 @@
false
false
false
+ 0
16089
@@ -128718,6 +144807,7 @@
false
false
false
+ 0
16090
@@ -128726,6 +144816,7 @@
false
false
false
+ 0
16091
@@ -128734,6 +144825,7 @@
false
false
false
+ 0
16092
@@ -128742,6 +144834,7 @@
false
false
false
+ 0
16093
@@ -128750,6 +144843,7 @@
false
false
false
+ 0
16094
@@ -128758,6 +144852,7 @@
false
false
false
+ 0
16095
@@ -128766,6 +144861,7 @@
false
false
false
+ 0
16096
@@ -128774,6 +144870,7 @@
false
false
false
+ 0
16097
@@ -128782,6 +144879,7 @@
false
false
false
+ 0
16098
@@ -128790,6 +144888,7 @@
false
false
false
+ 0
16099
@@ -128798,6 +144897,7 @@
false
false
false
+ 0
16100
@@ -128806,6 +144906,7 @@
false
false
false
+ 0
16101
@@ -128814,6 +144915,7 @@
false
false
false
+ 0
16102
@@ -128822,6 +144924,7 @@
false
false
false
+ 0
16103
@@ -128830,6 +144933,7 @@
false
false
false
+ 0
16104
@@ -128838,6 +144942,7 @@
false
false
false
+ 0
16105
@@ -128846,6 +144951,7 @@
false
false
false
+ 0
16106
@@ -128854,6 +144960,7 @@
false
false
false
+ 0
16107
@@ -128862,6 +144969,7 @@
false
false
false
+ 0
16108
@@ -128870,6 +144978,7 @@
false
false
false
+ 0
16109
@@ -128878,6 +144987,7 @@
false
false
false
+ 0
16110
@@ -128886,6 +144996,7 @@
false
false
false
+ 0
16111
@@ -128894,6 +145005,7 @@
false
false
false
+ 0
16112
@@ -128902,6 +145014,7 @@
false
false
false
+ 0
16113
@@ -128910,6 +145023,7 @@
false
false
false
+ 0
16114
@@ -128918,6 +145032,7 @@
false
false
false
+ 0
16115
@@ -128926,6 +145041,7 @@
false
false
false
+ 0
16116
@@ -128934,6 +145050,7 @@
false
false
false
+ 0
16117
@@ -128942,6 +145059,7 @@
false
false
false
+ 0
16118
@@ -128950,6 +145068,7 @@
false
false
false
+ 0
16119
@@ -128958,6 +145077,7 @@
false
false
false
+ 0
16120
@@ -128966,6 +145086,7 @@
false
false
false
+ 0
16121
@@ -128974,6 +145095,7 @@
false
false
false
+ 0
16122
@@ -128982,6 +145104,7 @@
false
false
false
+ 0
16123
@@ -128990,6 +145113,7 @@
false
false
false
+ 0
16124
@@ -128998,6 +145122,7 @@
false
false
false
+ 0
16125
@@ -129006,6 +145131,7 @@
false
false
false
+ 0
16126
@@ -129014,6 +145140,7 @@
false
false
false
+ 0
16127
@@ -129022,6 +145149,7 @@
false
false
false
+ 0
16128
@@ -129030,6 +145158,7 @@
false
false
false
+ 0
16129
@@ -129038,6 +145167,7 @@
false
false
false
+ 0
16130
@@ -129046,6 +145176,7 @@
false
false
false
+ 0
16131
@@ -129054,6 +145185,7 @@
false
false
false
+ 0
16132
@@ -129062,6 +145194,7 @@
false
false
false
+ 0
16133
@@ -129070,6 +145203,7 @@
false
false
false
+ 0
16134
@@ -129078,6 +145212,7 @@
false
false
false
+ 0
16135
@@ -129086,6 +145221,7 @@
false
false
false
+ 0
16136
@@ -129094,6 +145230,7 @@
false
false
false
+ 0
16137
@@ -129102,6 +145239,7 @@
false
false
false
+ 0
16138
@@ -129110,6 +145248,7 @@
false
false
false
+ 0
16139
@@ -129118,6 +145257,7 @@
false
false
false
+ 0
16140
@@ -129126,6 +145266,7 @@
false
false
false
+ 0
16141
@@ -129134,6 +145275,7 @@
false
false
false
+ 0
16142
@@ -129142,6 +145284,7 @@
false
false
false
+ 0
16143
@@ -129150,6 +145293,7 @@
false
false
false
+ 0
16144
@@ -129158,6 +145302,7 @@
false
false
false
+ 0
16145
@@ -129166,6 +145311,7 @@
false
false
false
+ 0
16146
@@ -129174,6 +145320,7 @@
false
false
false
+ 0
16147
@@ -129182,6 +145329,7 @@
false
false
false
+ 0
16148
@@ -129190,6 +145338,7 @@
false
false
false
+ 0
16149
@@ -129198,6 +145347,7 @@
false
false
false
+ 0
16150
@@ -129206,6 +145356,7 @@
false
false
false
+ 0
16151
@@ -129214,6 +145365,7 @@
false
false
false
+ 0
16152
@@ -129222,6 +145374,7 @@
false
false
false
+ 0
16153
@@ -129230,6 +145383,7 @@
false
false
false
+ 0
16154
@@ -129238,6 +145392,7 @@
false
false
false
+ 0
16155
@@ -129246,6 +145401,7 @@
false
false
false
+ 0
16156
@@ -129254,6 +145410,7 @@
false
false
false
+ 0
16157
@@ -129262,6 +145419,7 @@
false
false
false
+ 0
16158
@@ -129270,6 +145428,7 @@
false
false
false
+ 0
16159
@@ -129278,6 +145437,7 @@
false
false
false
+ 0
16160
@@ -129286,6 +145446,7 @@
false
false
false
+ 0
16161
@@ -129294,6 +145455,7 @@
false
false
false
+ 0
16162
@@ -129302,6 +145464,7 @@
false
false
false
+ 0
16163
@@ -129310,6 +145473,7 @@
false
false
false
+ 0
16164
@@ -129318,6 +145482,7 @@
false
false
false
+ 0
16165
@@ -129326,6 +145491,7 @@
false
false
false
+ 0
16166
@@ -129334,6 +145500,7 @@
false
false
false
+ 0
16167
@@ -129342,6 +145509,7 @@
false
false
false
+ 0
16168
@@ -129350,6 +145518,7 @@
false
false
false
+ 0
16169
@@ -129358,6 +145527,7 @@
false
false
false
+ 0
16170
@@ -129366,6 +145536,7 @@
false
false
false
+ 0
16171
@@ -129374,6 +145545,7 @@
false
false
false
+ 0
16172
@@ -129382,6 +145554,7 @@
false
false
false
+ 0
16173
@@ -129390,6 +145563,7 @@
false
false
false
+ 0
16174
@@ -129398,6 +145572,7 @@
false
false
false
+ 0
16175
@@ -129406,6 +145581,7 @@
false
false
false
+ 0
16176
@@ -129414,6 +145590,7 @@
false
false
false
+ 0
16177
@@ -129422,6 +145599,7 @@
false
false
false
+ 0
16178
@@ -129430,6 +145608,7 @@
false
false
false
+ 0
16179
@@ -129438,6 +145617,7 @@
false
false
false
+ 0
16180
@@ -129446,6 +145626,7 @@
false
false
false
+ 0
16181
@@ -129454,6 +145635,7 @@
false
false
false
+ 0
16182
@@ -129462,6 +145644,7 @@
false
false
false
+ 0
16183
@@ -129470,6 +145653,7 @@
false
false
false
+ 0
16184
@@ -129478,6 +145662,7 @@
false
false
false
+ 0
16185
@@ -129486,6 +145671,7 @@
false
false
false
+ 0
16186
@@ -129494,6 +145680,7 @@
false
false
false
+ 0
16187
@@ -129502,6 +145689,7 @@
false
false
false
+ 0
16188
@@ -129510,6 +145698,7 @@
false
false
false
+ 0
16189
@@ -129518,6 +145707,7 @@
false
false
false
+ 0
16190
@@ -129526,6 +145716,7 @@
false
false
false
+ 0
16191
@@ -129534,6 +145725,7 @@
false
false
false
+ 0
16192
@@ -129542,6 +145734,7 @@
false
false
false
+ 0
16193
@@ -129550,6 +145743,7 @@
false
false
false
+ 0
16194
@@ -129558,6 +145752,7 @@
false
false
false
+ 0
16195
@@ -129566,6 +145761,7 @@
false
false
false
+ 0
16196
@@ -129574,6 +145770,7 @@
false
false
false
+ 0
16197
@@ -129582,6 +145779,7 @@
false
false
false
+ 0
16198
@@ -129590,6 +145788,7 @@
false
false
false
+ 0
16199
@@ -129598,6 +145797,7 @@
false
false
false
+ 0
16200
@@ -129606,6 +145806,7 @@
false
false
false
+ 0
16201
@@ -129614,6 +145815,7 @@
false
false
false
+ 0
16202
@@ -129622,6 +145824,7 @@
false
false
false
+ 0
16203
@@ -129630,6 +145833,7 @@
false
false
false
+ 0
16204
@@ -129638,6 +145842,7 @@
false
false
false
+ 0
16205
@@ -129646,6 +145851,7 @@
false
false
false
+ 0
16206
@@ -129654,6 +145860,7 @@
false
false
false
+ 0
16207
@@ -129662,6 +145869,7 @@
false
false
false
+ 0
16208
@@ -129670,6 +145878,7 @@
false
false
false
+ 0
16209
@@ -129678,6 +145887,7 @@
false
false
false
+ 0
16210
@@ -129686,6 +145896,7 @@
false
false
false
+ 0
16211
@@ -129694,6 +145905,7 @@
false
false
false
+ 0
16212
@@ -129702,6 +145914,7 @@
false
false
false
+ 0
16213
@@ -129710,6 +145923,7 @@
false
false
false
+ 0
16214
@@ -129718,6 +145932,7 @@
false
false
false
+ 0
16215
@@ -129726,6 +145941,7 @@
false
false
false
+ 0
16216
@@ -129734,6 +145950,7 @@
false
false
false
+ 0
16217
@@ -129742,6 +145959,7 @@
false
false
false
+ 0
16218
@@ -129750,6 +145968,7 @@
false
false
false
+ 0
16219
@@ -129758,6 +145977,7 @@
false
false
false
+ 0
16220
@@ -129766,6 +145986,7 @@
false
false
false
+ 0
16221
@@ -129774,6 +145995,7 @@
false
false
false
+ 0
16222
@@ -129782,6 +146004,7 @@
false
false
false
+ 0
16223
@@ -129790,6 +146013,7 @@
false
false
false
+ 0
16224
@@ -129798,6 +146022,7 @@
false
false
false
+ 0
16225
@@ -129806,6 +146031,7 @@
false
false
false
+ 0
16226
@@ -129814,6 +146040,7 @@
false
false
false
+ 0
16227
@@ -129822,6 +146049,7 @@
false
false
false
+ 0
16228
@@ -129830,6 +146058,7 @@
false
false
false
+ 0
16229
@@ -129838,6 +146067,7 @@
false
false
false
+ 0
16230
@@ -129846,6 +146076,7 @@
false
false
false
+ 0
16231
@@ -129854,6 +146085,7 @@
false
false
false
+ 0
16232
@@ -129862,6 +146094,7 @@
false
false
false
+ 0
16233
@@ -129870,6 +146103,7 @@
false
false
false
+ 0
16234
@@ -129878,6 +146112,7 @@
false
false
false
+ 0
16235
@@ -129886,6 +146121,7 @@
false
false
false
+ 0
16236
@@ -129894,6 +146130,7 @@
false
false
false
+ 0
16237
@@ -129902,6 +146139,7 @@
false
false
false
+ 0
16238
@@ -129910,6 +146148,7 @@
false
false
false
+ 0
16239
@@ -129918,6 +146157,7 @@
false
false
false
+ 0
16240
@@ -129926,6 +146166,7 @@
false
false
false
+ 0
16241
@@ -129934,6 +146175,7 @@
false
false
false
+ 0
16242
@@ -129942,6 +146184,7 @@
false
false
false
+ 0
16243
@@ -129950,6 +146193,7 @@
false
false
false
+ 0
16244
@@ -129958,6 +146202,7 @@
false
false
false
+ 0
16245
@@ -129966,6 +146211,7 @@
false
false
false
+ 0
16246
@@ -129974,6 +146220,7 @@
false
false
false
+ 0
16247
@@ -129982,6 +146229,7 @@
false
false
false
+ 0
16248
@@ -129990,6 +146238,7 @@
false
false
false
+ 0
16249
@@ -129998,6 +146247,7 @@
false
false
false
+ 0
16250
@@ -130006,6 +146256,7 @@
false
false
false
+ 0
16251
@@ -130014,6 +146265,7 @@
false
false
false
+ 0
16252
@@ -130022,6 +146274,7 @@
false
false
false
+ 0
16253
@@ -130030,6 +146283,7 @@
false
false
false
+ 0
16254
@@ -130038,6 +146292,7 @@
false
false
false
+ 0
16255
@@ -130046,6 +146301,7 @@
false
false
false
+ 0
16256
@@ -130054,6 +146310,7 @@
false
false
false
+ 0
16257
@@ -130062,6 +146319,7 @@
false
false
false
+ 0
16258
@@ -130070,6 +146328,7 @@
false
false
false
+ 0
16259
@@ -130078,6 +146337,7 @@
false
false
false
+ 0
16260
@@ -130086,6 +146346,7 @@
false
false
false
+ 0
16261
@@ -130094,6 +146355,7 @@
false
false
false
+ 0
16262
@@ -130102,6 +146364,7 @@
false
false
false
+ 0
16263
@@ -130110,6 +146373,7 @@
false
false
false
+ 0
16264
@@ -130118,6 +146382,7 @@
false
false
false
+ 0
16265
@@ -130126,6 +146391,7 @@
false
false
false
+ 0
16266
@@ -130134,6 +146400,7 @@
false
false
false
+ 0
16267
@@ -130142,6 +146409,7 @@
false
false
false
+ 0
16268
@@ -130150,6 +146418,7 @@
false
false
false
+ 0
16269
@@ -130158,6 +146427,7 @@
false
false
false
+ 0
16270
@@ -130166,6 +146436,7 @@
false
false
false
+ 0
16271
@@ -130174,6 +146445,7 @@
false
false
false
+ 0
16272
@@ -130182,6 +146454,7 @@
false
false
false
+ 0
16273
@@ -130190,6 +146463,7 @@
false
false
false
+ 0
16274
@@ -130198,6 +146472,7 @@
false
false
false
+ 0
16275
@@ -130206,6 +146481,7 @@
false
false
false
+ 0
16276
@@ -130214,6 +146490,7 @@
false
false
false
+ 0
16277
@@ -130222,6 +146499,7 @@
false
false
false
+ 0
16278
@@ -130230,6 +146508,7 @@
false
false
false
+ 0
16279
@@ -130238,6 +146517,7 @@
false
false
false
+ 0
16280
@@ -130246,6 +146526,7 @@
false
false
false
+ 0
16281
@@ -130254,6 +146535,7 @@
false
false
false
+ 0
16282
@@ -130262,6 +146544,7 @@
false
false
false
+ 0
16283
@@ -130270,6 +146553,7 @@
false
false
false
+ 0
16284
@@ -130278,6 +146562,7 @@
false
false
false
+ 0
16285
@@ -130286,6 +146571,7 @@
false
false
false
+ 0
16286
@@ -130294,6 +146580,7 @@
false
false
false
+ 0
16287
@@ -130302,6 +146589,7 @@
false
false
false
+ 0
16288
@@ -130310,6 +146598,7 @@
false
false
false
+ 0
16289
@@ -130318,6 +146607,7 @@
false
false
false
+ 0
16290
@@ -130326,6 +146616,7 @@
false
false
false
+ 0
16291
@@ -130334,6 +146625,7 @@
false
false
false
+ 0
16292
@@ -130342,6 +146634,7 @@
false
false
false
+ 0
16293
@@ -130350,6 +146643,7 @@
false
false
false
+ 0
16294
@@ -130358,6 +146652,7 @@
false
false
false
+ 0
16295
@@ -130366,6 +146661,7 @@
false
false
false
+ 0
16296
@@ -130374,6 +146670,7 @@
false
false
false
+ 0
16297
@@ -130382,6 +146679,7 @@
false
false
false
+ 0
16298
@@ -130390,6 +146688,7 @@
false
false
false
+ 0
16299
@@ -130398,6 +146697,7 @@
false
false
false
+ 0
16300
@@ -130406,6 +146706,7 @@
false
false
false
+ 0
16301
@@ -130414,6 +146715,7 @@
false
false
false
+ 0
16302
@@ -130422,6 +146724,7 @@
false
false
false
+ 0
16303
@@ -130430,6 +146733,7 @@
false
false
false
+ 0
16304
@@ -130438,6 +146742,7 @@
false
false
false
+ 0
16305
@@ -130446,6 +146751,7 @@
false
false
false
+ 0
16306
@@ -130454,6 +146760,7 @@
false
false
false
+ 0
16307
@@ -130462,6 +146769,7 @@
false
false
false
+ 0
16308
@@ -130470,6 +146778,7 @@
false
false
false
+ 0
16309
@@ -130478,6 +146787,7 @@
false
false
false
+ 0
16310
@@ -130486,6 +146796,7 @@
false
false
false
+ 0
16311
@@ -130494,6 +146805,7 @@
false
false
false
+ 0
16312
@@ -130502,6 +146814,7 @@
false
false
false
+ 0
16313
@@ -130510,6 +146823,7 @@
false
false
false
+ 0
16314
@@ -130518,6 +146832,7 @@
false
false
false
+ 0
16315
@@ -130526,6 +146841,7 @@
false
false
false
+ 0
16316
@@ -130534,6 +146850,7 @@
false
false
false
+ 0
16317
@@ -130542,6 +146859,7 @@
false
false
false
+ 0
16318
@@ -130550,6 +146868,7 @@
false
false
false
+ 0
16319
@@ -130558,6 +146877,7 @@
false
false
false
+ 0
16320
@@ -130566,6 +146886,7 @@
false
false
false
+ 0
16321
@@ -130574,6 +146895,7 @@
false
false
false
+ 0
16322
@@ -130582,6 +146904,7 @@
false
false
false
+ 0
16323
@@ -130590,6 +146913,7 @@
false
false
false
+ 0
16324
@@ -130598,6 +146922,7 @@
false
false
false
+ 0
16325
@@ -130606,6 +146931,7 @@
false
false
false
+ 0
16326
@@ -130614,6 +146940,7 @@
false
false
false
+ 0
16327
@@ -130622,6 +146949,7 @@
false
false
false
+ 0
16328
@@ -130630,6 +146958,7 @@
false
false
false
+ 0
16329
@@ -130638,6 +146967,7 @@
false
false
false
+ 0
16330
@@ -130646,6 +146976,7 @@
false
false
false
+ 0
16331
@@ -130654,6 +146985,7 @@
false
false
false
+ 0
16332
@@ -130662,6 +146994,7 @@
false
false
false
+ 0
16333
@@ -130670,6 +147003,7 @@
false
false
false
+ 0
16334
@@ -130678,6 +147012,7 @@
false
false
false
+ 0
16335
@@ -130686,6 +147021,7 @@
false
false
false
+ 0
16336
@@ -130694,6 +147030,7 @@
false
false
false
+ 0
16337
@@ -130702,6 +147039,7 @@
false
false
false
+ 0
16338
@@ -130710,6 +147048,7 @@
false
false
false
+ 0
16339
@@ -130718,6 +147057,7 @@
false
false
false
+ 0
16340
@@ -130726,6 +147066,7 @@
false
false
false
+ 0
16341
@@ -130734,6 +147075,7 @@
false
false
false
+ 0
16342
@@ -130742,6 +147084,7 @@
false
false
false
+ 0
16343
@@ -130750,6 +147093,7 @@
false
false
false
+ 0
16344
@@ -130758,6 +147102,7 @@
false
false
false
+ 0
16345
@@ -130766,6 +147111,7 @@
false
false
false
+ 0
16346
@@ -130774,6 +147120,7 @@
false
false
false
+ 0
16347
@@ -130782,6 +147129,7 @@
false
false
false
+ 0
16348
@@ -130790,6 +147138,7 @@
false
false
false
+ 0
16349
@@ -130798,6 +147147,7 @@
false
false
false
+ 0
16350
@@ -130806,6 +147156,7 @@
false
false
false
+ 0
16351
@@ -130814,6 +147165,7 @@
false
false
false
+ 0
16352
@@ -130822,6 +147174,7 @@
false
false
false
+ 0
16353
@@ -130830,6 +147183,7 @@
false
false
false
+ 0
16354
@@ -130838,6 +147192,7 @@
false
false
false
+ 0
16355
@@ -130846,6 +147201,7 @@
false
false
false
+ 0
16356
@@ -130854,6 +147210,7 @@
false
false
false
+ 0
16357
@@ -130862,6 +147219,7 @@
false
false
false
+ 0
16358
@@ -130870,6 +147228,7 @@
false
false
false
+ 0
16359
@@ -130878,6 +147237,7 @@
false
false
false
+ 0
16360
@@ -130886,6 +147246,7 @@
false
false
false
+ 0
16361
@@ -130894,6 +147255,7 @@
false
false
false
+ 0
16362
@@ -130902,6 +147264,7 @@
false
false
false
+ 0
16363
@@ -130910,6 +147273,7 @@
false
false
false
+ 0
16364
@@ -130918,6 +147282,7 @@
false
false
false
+ 0
16365
@@ -130926,6 +147291,7 @@
false
false
false
+ 0
16366
@@ -130934,6 +147300,7 @@
false
false
false
+ 0
16367
@@ -130942,6 +147309,7 @@
false
false
false
+ 0
16368
@@ -130950,6 +147318,7 @@
false
false
false
+ 0
16369
@@ -130958,6 +147327,7 @@
false
false
false
+ 0
16370
@@ -130966,6 +147336,7 @@
false
false
false
+ 0
16371
@@ -130974,6 +147345,7 @@
false
false
false
+ 0
16372
@@ -130982,6 +147354,7 @@
false
false
false
+ 0
16373
@@ -130990,6 +147363,7 @@
false
false
false
+ 0
16374
@@ -130998,6 +147372,7 @@
false
false
false
+ 0
16375
@@ -131006,6 +147381,7 @@
false
false
false
+ 0
16376
@@ -131014,6 +147390,7 @@
false
false
false
+ 0
16377
@@ -131022,6 +147399,7 @@
false
false
false
+ 0
16378
@@ -131030,6 +147408,7 @@
false
false
false
+ 0
16379
@@ -131038,6 +147417,7 @@
false
false
false
+ 0
16380
@@ -131046,6 +147426,7 @@
false
false
false
+ 0
16381
@@ -131054,6 +147435,7 @@
false
false
false
+ 0
16382
@@ -131062,6 +147444,7 @@
false
false
false
+ 0
16383
@@ -131070,6 +147453,7 @@
false
false
false
+ 0
16384
@@ -131078,6 +147462,7 @@
false
false
false
+ 0
16385
@@ -131086,6 +147471,7 @@
false
false
false
+ 0
16386
@@ -131094,6 +147480,7 @@
false
false
false
+ 0
16387
@@ -131102,6 +147489,7 @@
false
false
false
+ 0
16388
@@ -131110,6 +147498,7 @@
false
false
false
+ 0
16389
@@ -131118,6 +147507,7 @@
false
false
false
+ 0
16390
@@ -131126,6 +147516,7 @@
false
false
false
+ 0
16391
@@ -131134,6 +147525,7 @@
false
false
false
+ 0
16392
@@ -131142,6 +147534,7 @@
false
false
false
+ 0
16393
@@ -131150,6 +147543,7 @@
false
false
false
+ 0
16394
@@ -131158,6 +147552,7 @@
false
false
false
+ 0
16395
@@ -131166,6 +147561,7 @@
false
false
false
+ 0
16396
@@ -131174,6 +147570,7 @@
false
false
false
+ 0
16397
@@ -131182,6 +147579,7 @@
false
false
false
+ 0
16398
@@ -131190,6 +147588,7 @@
false
false
false
+ 0
16399
@@ -131198,6 +147597,7 @@
false
false
false
+ 0
16400
@@ -131206,6 +147606,7 @@
false
false
false
+ 0
16401
@@ -131214,6 +147615,7 @@
false
false
false
+ 0
16402
@@ -131222,6 +147624,7 @@
false
false
false
+ 0
16403
@@ -131230,6 +147633,7 @@
false
false
false
+ 0
16404
@@ -131238,6 +147642,7 @@
false
false
false
+ 0
16405
@@ -131246,6 +147651,7 @@
false
false
false
+ 0
16406
@@ -131254,6 +147660,7 @@
false
false
false
+ 0
16407
@@ -131262,6 +147669,7 @@
false
false
false
+ 0
16408
@@ -131270,6 +147678,7 @@
false
false
false
+ 0
16409
@@ -131278,6 +147687,7 @@
false
false
false
+ 0
16410
@@ -131286,6 +147696,7 @@
false
false
false
+ 0
16411
@@ -131294,6 +147705,7 @@
false
false
false
+ 0
16412
@@ -131302,6 +147714,7 @@
false
false
false
+ 0
16413
@@ -131310,6 +147723,7 @@
false
false
false
+ 0
16414
@@ -131318,6 +147732,7 @@
false
false
false
+ 0
16415
@@ -131326,6 +147741,7 @@
false
false
false
+ 0
16416
@@ -131334,6 +147750,7 @@
false
false
false
+ 0
16417
@@ -131342,6 +147759,7 @@
false
false
false
+ 0
16418
@@ -131350,6 +147768,7 @@
false
false
false
+ 0
16419
@@ -131358,6 +147777,7 @@
false
false
false
+ 0
16420
@@ -131366,6 +147786,7 @@
false
false
false
+ 0
16421
@@ -131374,6 +147795,7 @@
false
false
false
+ 0
16422
@@ -131382,6 +147804,7 @@
false
false
false
+ 0
16423
@@ -131390,6 +147813,7 @@
false
false
false
+ 0
16424
@@ -131398,6 +147822,7 @@
false
false
false
+ 0
16425
@@ -131406,6 +147831,7 @@
false
false
false
+ 0
16426
@@ -131414,6 +147840,7 @@
false
false
false
+ 0
16427
@@ -131422,6 +147849,7 @@
false
false
false
+ 0
16428
@@ -131430,6 +147858,7 @@
false
false
false
+ 0
16429
@@ -131438,6 +147867,7 @@
false
false
false
+ 0
16430
@@ -131446,6 +147876,7 @@
false
false
false
+ 0
16431
@@ -131454,6 +147885,7 @@
false
false
false
+ 0
16432
@@ -131462,6 +147894,7 @@
false
false
false
+ 0
16433
@@ -131470,6 +147903,7 @@
false
false
false
+ 0
16434
@@ -131478,6 +147912,7 @@
false
false
false
+ 0
16435
@@ -131486,6 +147921,7 @@
false
false
false
+ 0
16436
@@ -131494,6 +147930,7 @@
false
false
false
+ 0
16437
@@ -131502,6 +147939,7 @@
false
false
false
+ 0
16438
@@ -131510,6 +147948,7 @@
false
false
false
+ 0
16439
@@ -131518,6 +147957,7 @@
false
false
false
+ 0
16440
@@ -131526,6 +147966,7 @@
false
false
false
+ 0
16441
@@ -131534,6 +147975,7 @@
false
false
false
+ 0
16442
@@ -131542,6 +147984,7 @@
false
false
false
+ 0
16443
@@ -131550,6 +147993,7 @@
false
false
false
+ 0
16444
@@ -131558,6 +148002,7 @@
false
false
false
+ 0
16445
@@ -131566,6 +148011,7 @@
false
false
false
+ 0
16446
@@ -131574,6 +148020,7 @@
false
false
false
+ 0
16447
@@ -131582,6 +148029,7 @@
false
false
false
+ 0
16448
@@ -131590,6 +148038,7 @@
false
false
false
+ 0
16449
@@ -131598,6 +148047,7 @@
false
false
false
+ 0
16450
@@ -131606,6 +148056,7 @@
false
false
false
+ 0
16451
@@ -131614,6 +148065,7 @@
false
false
false
+ 0
16452
@@ -131622,6 +148074,7 @@
false
false
false
+ 0
16453
@@ -131630,6 +148083,7 @@
false
false
false
+ 0
16454
@@ -131638,6 +148092,7 @@
false
false
false
+ 0
16455
@@ -131646,6 +148101,7 @@
false
false
false
+ 0
16456
@@ -131654,6 +148110,7 @@
false
false
false
+ 0
16457
@@ -131662,6 +148119,7 @@
false
false
false
+ 0
16458
@@ -131670,6 +148128,7 @@
false
false
false
+ 0
16459
@@ -131678,6 +148137,7 @@
false
false
false
+ 0
16460
@@ -131686,6 +148146,7 @@
false
false
false
+ 0
16461
@@ -131694,6 +148155,7 @@
false
false
false
+ 0
16462
@@ -131702,6 +148164,7 @@
false
false
false
+ 0
16463
@@ -131710,6 +148173,7 @@
false
false
false
+ 0
16464
@@ -131718,6 +148182,7 @@
false
false
false
+ 0
16465
@@ -131726,6 +148191,7 @@
false
false
false
+ 0
16466
@@ -131734,6 +148200,7 @@
false
false
false
+ 0
16467
@@ -131742,6 +148209,7 @@
false
false
false
+ 0
16468
@@ -131750,6 +148218,7 @@
false
false
false
+ 0
16469
@@ -131758,6 +148227,7 @@
false
false
false
+ 0
16470
@@ -131766,6 +148236,7 @@
false
false
false
+ 0
16471
@@ -131774,6 +148245,7 @@
false
false
false
+ 0
16472
@@ -131782,6 +148254,7 @@
false
false
false
+ 0
16473
@@ -131790,6 +148263,7 @@
false
false
false
+ 0
16474
@@ -131798,6 +148272,7 @@
false
false
false
+ 0
16475
@@ -131806,6 +148281,7 @@
false
false
false
+ 0
16476
@@ -131814,6 +148290,7 @@
false
false
false
+ 0
16477
@@ -131822,6 +148299,7 @@
false
false
false
+ 0
16478
@@ -131830,6 +148308,7 @@
false
false
false
+ 0
16479
@@ -131838,6 +148317,7 @@
false
false
false
+ 0
16480
@@ -131846,6 +148326,7 @@
false
false
false
+ 0
16481
@@ -131854,6 +148335,7 @@
false
false
false
+ 0
16482
@@ -131862,6 +148344,7 @@
false
false
false
+ 0
16483
@@ -131870,6 +148353,7 @@
false
false
false
+ 0
16484
@@ -131878,6 +148362,7 @@
false
false
false
+ 0
16485
@@ -131886,6 +148371,7 @@
false
false
false
+ 0
16486
@@ -131894,6 +148380,7 @@
false
false
false
+ 0
16487
@@ -131902,6 +148389,7 @@
false
false
false
+ 0
16488
@@ -131910,6 +148398,7 @@
false
false
false
+ 0
16489
@@ -131918,6 +148407,7 @@
false
false
false
+ 0
16490
@@ -131926,6 +148416,7 @@
false
false
false
+ 0
16491
@@ -131934,6 +148425,7 @@
false
false
false
+ 0
16492
@@ -131942,6 +148434,7 @@
false
false
false
+ 0
16493
@@ -131950,6 +148443,7 @@
false
false
false
+ 0
16494
@@ -131958,6 +148452,7 @@
false
false
false
+ 0
16495
@@ -131966,6 +148461,7 @@
false
false
false
+ 0
16496
@@ -131974,6 +148470,7 @@
false
false
false
+ 0
16497
@@ -131982,6 +148479,7 @@
false
false
false
+ 0
16498
@@ -131990,6 +148488,7 @@
false
false
false
+ 0
16499
@@ -131998,6 +148497,7 @@
false
false
false
+ 0
16500
@@ -132006,6 +148506,7 @@
false
false
false
+ 0
16501
@@ -132014,6 +148515,7 @@
false
false
false
+ 0
16502
@@ -132022,6 +148524,7 @@
false
false
false
+ 0
16503
@@ -132030,6 +148533,7 @@
false
false
false
+ 0
16504
@@ -132038,6 +148542,7 @@
false
false
false
+ 0
16505
@@ -132046,6 +148551,7 @@
false
false
false
+ 0
16506
@@ -132054,6 +148560,7 @@
false
false
false
+ 0
16507
@@ -132062,6 +148569,7 @@
false
false
false
+ 0
16508
@@ -132070,6 +148578,7 @@
false
false
false
+ 0
16509
@@ -132078,6 +148587,7 @@
false
false
false
+ 0
16510
@@ -132086,6 +148596,7 @@
false
false
false
+ 0
16511
@@ -132094,6 +148605,7 @@
false
false
false
+ 0
16512
@@ -132102,6 +148614,7 @@
false
false
false
+ 0
16513
@@ -132110,6 +148623,7 @@
false
false
false
+ 0
16514
@@ -132118,6 +148632,7 @@
false
false
false
+ 0
16515
@@ -132126,6 +148641,7 @@
false
false
false
+ 0
16516
@@ -132134,6 +148650,7 @@
false
false
false
+ 0
16517
@@ -132142,6 +148659,7 @@
false
false
false
+ 0
16518
@@ -132150,6 +148668,7 @@
false
false
false
+ 0
16519
@@ -132158,6 +148677,7 @@
false
false
false
+ 0
16520
@@ -132166,6 +148686,7 @@
false
false
false
+ 0
16521
@@ -132174,6 +148695,7 @@
false
false
false
+ 0
16522
@@ -132182,6 +148704,7 @@
false
false
false
+ 0
16523
@@ -132190,6 +148713,7 @@
false
false
false
+ 0
16524
@@ -132198,6 +148722,7 @@
false
false
false
+ 0
16525
@@ -132206,6 +148731,7 @@
false
false
false
+ 0
16526
@@ -132214,6 +148740,7 @@
false
false
false
+ 0
16527
@@ -132222,6 +148749,7 @@
false
false
false
+ 0
16528
@@ -132230,6 +148758,7 @@
false
false
false
+ 0
16529
@@ -132238,6 +148767,7 @@
false
false
false
+ 0
16530
@@ -132246,6 +148776,7 @@
false
false
false
+ 0
16531
@@ -132254,6 +148785,7 @@
false
false
false
+ 0
16532
@@ -132262,6 +148794,7 @@
false
false
false
+ 0
16533
@@ -132270,6 +148803,7 @@
false
false
false
+ 0
16534
@@ -132278,6 +148812,7 @@
false
false
false
+ 0
16535
@@ -132286,6 +148821,7 @@
false
false
false
+ 0
16536
@@ -132294,6 +148830,7 @@
false
false
false
+ 0
16537
@@ -132302,6 +148839,7 @@
false
false
false
+ 0
16538
@@ -132310,6 +148848,7 @@
false
false
false
+ 0
16539
@@ -132318,6 +148857,7 @@
false
false
false
+ 0
16540
@@ -132326,6 +148866,7 @@
false
false
false
+ 0
16541
@@ -132334,6 +148875,7 @@
false
false
false
+ 0
16542
@@ -132342,6 +148884,7 @@
false
false
false
+ 0
16543
@@ -132350,6 +148893,7 @@
false
false
false
+ 0
16544
@@ -132358,6 +148902,7 @@
false
false
false
+ 0
16545
@@ -132366,6 +148911,7 @@
false
false
false
+ 0
16546
@@ -132374,6 +148920,7 @@
false
false
false
+ 0
16547
@@ -132382,6 +148929,7 @@
false
false
false
+ 0
16548
@@ -132390,6 +148938,7 @@
false
false
false
+ 0
16549
@@ -132398,6 +148947,7 @@
false
false
false
+ 0
16550
@@ -132406,6 +148956,7 @@
false
false
false
+ 0
16551
@@ -132414,6 +148965,7 @@
false
false
false
+ 0
16552
@@ -132422,6 +148974,7 @@
false
false
false
+ 0
16553
@@ -132430,6 +148983,7 @@
false
false
false
+ 0
16554
@@ -132438,6 +148992,7 @@
false
false
false
+ 0
16555
@@ -132446,6 +149001,7 @@
false
false
false
+ 0
16556
@@ -132454,6 +149010,7 @@
false
false
false
+ 0
16557
@@ -132462,6 +149019,7 @@
false
false
false
+ 0
16558
@@ -132470,6 +149028,7 @@
false
false
false
+ 0
16559
@@ -132478,6 +149037,7 @@
false
false
false
+ 0
16560
@@ -132486,6 +149046,7 @@
false
false
false
+ 0
16561
@@ -132494,6 +149055,7 @@
false
false
false
+ 0
16562
@@ -132502,6 +149064,7 @@
false
false
false
+ 0
16563
@@ -132510,6 +149073,7 @@
false
false
false
+ 0
16564
@@ -132518,6 +149082,7 @@
false
false
false
+ 0
16565
@@ -132526,6 +149091,7 @@
false
false
false
+ 0
16566
@@ -132534,6 +149100,7 @@
false
false
false
+ 0
16567
@@ -132542,6 +149109,7 @@
false
false
false
+ 0
16568
@@ -132550,6 +149118,7 @@
false
false
false
+ 0
16569
@@ -132558,6 +149127,7 @@
false
false
false
+ 0
16570
@@ -132566,6 +149136,7 @@
false
false
false
+ 0
16571
@@ -132574,6 +149145,7 @@
false
false
false
+ 0
16572
@@ -132582,6 +149154,7 @@
false
false
false
+ 0
16573
@@ -132590,6 +149163,7 @@
false
false
false
+ 0
16574
@@ -132598,6 +149172,7 @@
false
false
false
+ 0
16575
@@ -132606,6 +149181,7 @@
false
false
false
+ 0
16576
@@ -132614,6 +149190,7 @@
false
false
false
+ 0
16577
@@ -132622,6 +149199,7 @@
false
false
false
+ 0
16578
@@ -132630,6 +149208,7 @@
false
false
false
+ 0
16579
@@ -132638,6 +149217,7 @@
false
false
false
+ 0
16580
@@ -132646,6 +149226,7 @@
false
false
false
+ 0
16581
@@ -132654,6 +149235,7 @@
false
false
false
+ 0
16582
@@ -132662,6 +149244,7 @@
false
false
false
+ 0
16583
@@ -132670,6 +149253,7 @@
false
false
false
+ 0
16584
@@ -132678,6 +149262,7 @@
false
false
false
+ 0
16585
@@ -132686,6 +149271,7 @@
false
false
false
+ 0
16586
@@ -132694,6 +149280,7 @@
false
false
false
+ 0
16587
@@ -132702,6 +149289,7 @@
false
false
false
+ 0
16588
@@ -132710,6 +149298,7 @@
false
false
false
+ 0
16589
@@ -132718,6 +149307,7 @@
false
false
false
+ 0
16590
@@ -132726,6 +149316,7 @@
false
false
false
+ 0
16591
@@ -132734,6 +149325,7 @@
false
false
false
+ 0
16592
@@ -132742,6 +149334,7 @@
false
false
false
+ 0
16593
@@ -132750,6 +149343,7 @@
false
false
false
+ 0
16594
@@ -132758,6 +149352,7 @@
false
false
false
+ 0
16595
@@ -132766,6 +149361,7 @@
false
false
false
+ 0
16596
@@ -132774,6 +149370,7 @@
false
false
false
+ 0
16597
@@ -132782,6 +149379,7 @@
false
false
false
+ 0
16598
@@ -132790,6 +149388,7 @@
false
false
false
+ 0
16599
@@ -132798,6 +149397,7 @@
false
false
false
+ 0
16600
@@ -132806,6 +149406,7 @@
false
false
false
+ 0
16601
@@ -132814,6 +149415,7 @@
false
false
false
+ 0
16602
@@ -132822,6 +149424,7 @@
false
false
false
+ 0
16603
@@ -132830,6 +149433,7 @@
false
false
false
+ 0
16604
@@ -132838,6 +149442,7 @@
false
false
false
+ 0
16605
@@ -132846,6 +149451,7 @@
false
false
false
+ 0
16606
@@ -132854,6 +149460,7 @@
false
false
false
+ 0
16607
@@ -132862,6 +149469,7 @@
false
false
false
+ 0
16608
@@ -132870,6 +149478,7 @@
false
false
false
+ 0
16609
@@ -132878,6 +149487,7 @@
false
false
false
+ 0
16610
@@ -132886,6 +149496,7 @@
false
false
false
+ 0
16611
@@ -132894,6 +149505,7 @@
false
false
false
+ 0
16612
@@ -132902,6 +149514,7 @@
false
false
false
+ 0
16613
@@ -132910,6 +149523,7 @@
false
false
false
+ 0
16614
@@ -132918,6 +149532,7 @@
false
false
false
+ 0
16615
@@ -132926,6 +149541,7 @@
false
false
false
+ 0
16616
@@ -132934,6 +149550,7 @@
false
false
false
+ 0
16617
@@ -132942,6 +149559,7 @@
false
false
false
+ 0
16618
@@ -132950,6 +149568,7 @@
false
false
false
+ 0
16619
@@ -132958,6 +149577,7 @@
false
false
false
+ 0
16620
@@ -132966,6 +149586,7 @@
false
false
false
+ 0
16621
@@ -132974,6 +149595,7 @@
false
false
false
+ 0
16622
@@ -132982,6 +149604,7 @@
false
false
false
+ 0
16623
@@ -132990,6 +149613,7 @@
false
false
false
+ 0
16624
@@ -132998,6 +149622,7 @@
false
false
false
+ 0
16625
@@ -133006,6 +149631,7 @@
false
false
false
+ 0
16626
@@ -133014,6 +149640,7 @@
false
false
false
+ 0
16627
@@ -133022,6 +149649,7 @@
false
false
false
+ 0
16628
@@ -133030,6 +149658,7 @@
false
false
false
+ 0
16629
@@ -133038,6 +149667,7 @@
false
false
false
+ 0
16630
@@ -133046,6 +149676,7 @@
false
false
false
+ 0
16631
@@ -133054,6 +149685,7 @@
false
false
false
+ 0
16632
@@ -133062,6 +149694,7 @@
false
false
false
+ 0
16633
@@ -133070,6 +149703,7 @@
false
false
false
+ 0
16634
@@ -133078,6 +149712,7 @@
false
false
false
+ 0
16635
@@ -133086,6 +149721,7 @@
false
false
false
+ 0
16636
@@ -133094,6 +149730,7 @@
false
false
false
+ 0
16637
@@ -133102,6 +149739,7 @@
false
false
false
+ 0
16638
@@ -133110,6 +149748,7 @@
false
false
false
+ 0
16639
@@ -133118,6 +149757,7 @@
false
false
false
+ 0
16640
@@ -133126,6 +149766,7 @@
false
false
false
+ 0
16641
@@ -133134,6 +149775,7 @@
false
false
false
+ 0
16642
@@ -133142,6 +149784,7 @@
false
false
false
+ 0
16643
@@ -133150,6 +149793,7 @@
false
false
false
+ 0
16644
@@ -133158,6 +149802,7 @@
false
false
false
+ 0
16645
@@ -133166,6 +149811,7 @@
false
false
false
+ 0
16646
@@ -133174,6 +149820,7 @@
false
false
false
+ 0
16647
@@ -133182,6 +149829,7 @@
false
false
false
+ 0
16648
@@ -133190,6 +149838,7 @@
false
false
false
+ 0
16649
@@ -133198,6 +149847,7 @@
false
false
false
+ 0
16650
@@ -133206,6 +149856,7 @@
false
false
false
+ 0
16651
@@ -133214,6 +149865,7 @@
false
false
false
+ 0
16652
@@ -133222,6 +149874,7 @@
false
false
false
+ 0
16653
@@ -133230,6 +149883,7 @@
false
false
false
+ 0
16654
@@ -133238,6 +149892,7 @@
false
false
false
+ 0
16655
@@ -133246,6 +149901,7 @@
false
false
false
+ 0
16656
@@ -133254,6 +149910,7 @@
false
false
false
+ 0
16657
@@ -133262,6 +149919,7 @@
false
false
false
+ 0
16658
@@ -133270,6 +149928,7 @@
false
false
false
+ 0
16659
@@ -133278,6 +149937,7 @@
false
false
false
+ 0
16660
@@ -133286,6 +149946,7 @@
false
false
false
+ 0
16661
@@ -133294,6 +149955,7 @@
false
false
false
+ 0
16662
@@ -133302,6 +149964,7 @@
false
false
false
+ 0
16663
@@ -133310,6 +149973,7 @@
false
false
false
+ 0
16664
@@ -133318,6 +149982,7 @@
false
false
false
+ 0
16665
@@ -133326,6 +149991,7 @@
false
false
false
+ 0
16666
@@ -133334,6 +150000,7 @@
false
false
false
+ 0
16667
@@ -133342,6 +150009,7 @@
false
false
false
+ 0
16668
@@ -133350,6 +150018,7 @@
false
false
false
+ 0
16669
@@ -133358,6 +150027,7 @@
false
false
false
+ 0
16670
@@ -133366,6 +150036,7 @@
false
false
false
+ 0
16671
@@ -133374,6 +150045,7 @@
false
false
false
+ 0
16672
@@ -133382,6 +150054,7 @@
false
false
false
+ 0
16673
@@ -133390,6 +150063,7 @@
false
false
false
+ 0
16674
@@ -133398,6 +150072,7 @@
false
false
false
+ 0
16675
@@ -133406,6 +150081,7 @@
false
false
false
+ 0
16676
@@ -133414,6 +150090,7 @@
false
false
false
+ 0
16677
@@ -133422,6 +150099,7 @@
false
false
false
+ 0
16678
@@ -133430,6 +150108,7 @@
false
false
false
+ 0
16679
@@ -133438,6 +150117,7 @@
false
false
false
+ 0
16680
@@ -133446,6 +150126,7 @@
false
false
false
+ 0
16681
@@ -133454,6 +150135,7 @@
false
false
false
+ 0
16682
@@ -133462,6 +150144,7 @@
false
false
false
+ 0
16683
@@ -133470,6 +150153,7 @@
false
false
false
+ 0
16684
@@ -133478,6 +150162,7 @@
false
false
false
+ 0
16685
@@ -133486,6 +150171,7 @@
false
false
false
+ 0
16686
@@ -133494,6 +150180,7 @@
false
false
false
+ 0
16687
@@ -133502,6 +150189,7 @@
false
false
false
+ 0
16688
@@ -133510,6 +150198,7 @@
false
false
false
+ 0
16689
@@ -133518,6 +150207,7 @@
false
false
false
+ 0
16690
@@ -133526,6 +150216,7 @@
false
false
false
+ 0
16691
@@ -133534,6 +150225,7 @@
false
false
false
+ 0
16692
@@ -133542,6 +150234,7 @@
false
false
false
+ 0
16693
@@ -133550,6 +150243,7 @@
false
false
false
+ 0
16694
@@ -133558,6 +150252,7 @@
false
false
false
+ 0
16695
@@ -133566,6 +150261,7 @@
false
false
false
+ 0
16696
@@ -133574,6 +150270,7 @@
false
false
false
+ 0
16697
@@ -133582,6 +150279,7 @@
false
false
false
+ 0
16698
@@ -133590,6 +150288,7 @@
false
false
false
+ 0
16699
@@ -133598,6 +150297,7 @@
false
false
false
+ 0
16700
@@ -133606,6 +150306,7 @@
false
false
false
+ 0
16701
@@ -133614,6 +150315,7 @@
false
false
false
+ 0
16702
@@ -133622,6 +150324,7 @@
false
false
false
+ 0
16703
@@ -133630,6 +150333,7 @@
false
false
false
+ 0
16704
@@ -133638,6 +150342,7 @@
false
false
false
+ 0
16705
@@ -133646,6 +150351,7 @@
false
false
false
+ 0
16706
@@ -133654,6 +150360,7 @@
false
false
false
+ 0
16707
@@ -133662,6 +150369,7 @@
false
false
false
+ 0
16708
@@ -133670,6 +150378,7 @@
false
false
false
+ 0
16709
@@ -133678,6 +150387,7 @@
false
false
false
+ 0
16710
@@ -133686,6 +150396,7 @@
false
false
false
+ 0
16711
@@ -133694,6 +150405,7 @@
false
false
false
+ 0
16712
@@ -133702,6 +150414,7 @@
false
false
false
+ 0
16713
@@ -133710,6 +150423,7 @@
false
false
false
+ 0
16714
@@ -133718,6 +150432,7 @@
false
false
false
+ 0
16715
@@ -133726,6 +150441,7 @@
false
false
false
+ 0
16716
@@ -133734,6 +150450,7 @@
false
false
false
+ 0
16717
@@ -133742,6 +150459,7 @@
false
false
false
+ 0
16718
@@ -133750,6 +150468,7 @@
false
false
false
+ 0
16719
@@ -133758,6 +150477,7 @@
false
false
false
+ 0
16720
@@ -133766,6 +150486,7 @@
false
false
false
+ 0
16721
@@ -133774,6 +150495,7 @@
false
false
false
+ 0
16722
@@ -133782,6 +150504,7 @@
false
false
false
+ 0
16723
@@ -133790,6 +150513,7 @@
false
false
false
+ 0
16724
@@ -133798,6 +150522,7 @@
false
false
false
+ 0
16725
@@ -133806,6 +150531,7 @@
false
false
false
+ 0
16726
@@ -133814,6 +150540,7 @@
false
false
false
+ 0
16727
@@ -133822,6 +150549,7 @@
false
false
false
+ 0
16728
@@ -133830,6 +150558,7 @@
false
false
false
+ 0
16729
@@ -133838,6 +150567,7 @@
false
false
false
+ 0
16730
@@ -133846,6 +150576,7 @@
false
false
false
+ 0
16731
@@ -133854,6 +150585,7 @@
false
false
false
+ 0
16732
@@ -133862,6 +150594,7 @@
false
false
false
+ 0
16733
@@ -133870,6 +150603,7 @@
false
false
false
+ 0
16734
@@ -133878,6 +150612,7 @@
false
false
false
+ 0
16735
@@ -133886,6 +150621,7 @@
false
false
false
+ 0
16736
@@ -133894,6 +150630,7 @@
false
false
false
+ 0
16737
@@ -133902,6 +150639,7 @@
false
false
false
+ 0
16738
@@ -133910,6 +150648,7 @@
false
false
false
+ 0
16739
@@ -133918,6 +150657,7 @@
false
false
false
+ 0
16740
@@ -133926,6 +150666,7 @@
false
false
false
+ 0
16741
@@ -133934,6 +150675,7 @@
false
false
false
+ 0
16742
@@ -133942,6 +150684,7 @@
false
false
false
+ 0
16743
@@ -133950,6 +150693,7 @@
false
false
false
+ 0
16744
@@ -133958,6 +150702,7 @@
false
false
false
+ 0
16745
@@ -133966,6 +150711,7 @@
false
false
false
+ 0
16746
@@ -133974,6 +150720,7 @@
false
false
false
+ 0
16747
@@ -133982,6 +150729,7 @@
false
false
false
+ 0
16748
@@ -133990,6 +150738,7 @@
false
false
false
+ 0
16749
@@ -133998,6 +150747,7 @@
false
false
false
+ 0
16750
@@ -134006,6 +150756,7 @@
false
false
false
+ 0
16751
@@ -134014,6 +150765,7 @@
false
false
false
+ 0
16752
@@ -134022,6 +150774,7 @@
false
false
false
+ 0
16753
@@ -134030,6 +150783,7 @@
false
false
false
+ 0
16754
@@ -134038,6 +150792,7 @@
false
false
false
+ 0
16755
@@ -134046,6 +150801,7 @@
false
false
false
+ 0
16756
@@ -134054,6 +150810,7 @@
false
false
false
+ 0
16757
@@ -134062,6 +150819,7 @@
false
false
false
+ 0
16758
@@ -134070,6 +150828,7 @@
false
false
false
+ 0
16759
@@ -134078,6 +150837,7 @@
false
false
false
+ 0
16760
@@ -134086,6 +150846,7 @@
false
false
false
+ 0
16761
@@ -134094,6 +150855,7 @@
false
false
false
+ 0
16762
@@ -134102,6 +150864,7 @@
false
false
false
+ 0
16763
@@ -134110,6 +150873,7 @@
false
false
false
+ 0
16764
@@ -134118,6 +150882,7 @@
false
false
false
+ 0
16765
@@ -134126,6 +150891,7 @@
false
false
false
+ 0
16766
@@ -134134,6 +150900,7 @@
false
false
false
+ 0
16767
@@ -134142,6 +150909,7 @@
false
false
false
+ 0
16768
@@ -134150,6 +150918,7 @@
false
false
false
+ 0
16769
@@ -134158,6 +150927,7 @@
false
false
false
+ 0
16770
@@ -134166,6 +150936,7 @@
false
false
false
+ 0
16771
@@ -134174,6 +150945,7 @@
false
false
false
+ 0
16772
@@ -134182,6 +150954,7 @@
false
false
false
+ 0
16773
@@ -134190,6 +150963,7 @@
false
false
false
+ 0
16774
@@ -134198,6 +150972,7 @@
false
false
false
+ 0
16775
@@ -134206,6 +150981,7 @@
false
false
false
+ 0
16776
@@ -134214,6 +150990,7 @@
false
false
false
+ 0
16777
@@ -134222,6 +150999,7 @@
false
false
false
+ 0
16778
@@ -134230,6 +151008,7 @@
false
false
false
+ 0
16779
@@ -134238,6 +151017,7 @@
false
false
false
+ 0
16780
@@ -134246,6 +151026,7 @@
false
false
false
+ 0
16781
@@ -134254,6 +151035,7 @@
false
false
false
+ 0
16782
@@ -134262,6 +151044,7 @@
false
false
false
+ 0
16783
@@ -134270,6 +151053,7 @@
false
false
false
+ 0
16784
@@ -134278,6 +151062,7 @@
false
false
false
+ 0
16785
@@ -134286,6 +151071,7 @@
false
false
false
+ 0
16786
@@ -134294,6 +151080,7 @@
false
false
false
+ 0
16787
@@ -134302,6 +151089,7 @@
false
false
false
+ 0
16788
@@ -134310,6 +151098,7 @@
false
false
false
+ 0
16789
@@ -134318,6 +151107,7 @@
false
false
false
+ 0
16790
@@ -134326,6 +151116,7 @@
false
false
false
+ 0
16791
@@ -134334,6 +151125,7 @@
false
false
false
+ 0
16792
@@ -134342,6 +151134,7 @@
false
false
false
+ 0
16793
@@ -134350,6 +151143,7 @@
false
false
false
+ 0
16794
@@ -134358,6 +151152,7 @@
false
false
false
+ 0
16795
@@ -134366,6 +151161,7 @@
false
false
false
+ 0
16796
@@ -134374,6 +151170,7 @@
false
false
false
+ 0
16797
@@ -134382,6 +151179,7 @@
false
false
false
+ 0
16798
@@ -134390,6 +151188,7 @@
false
false
false
+ 0
16799
@@ -134398,6 +151197,7 @@
false
false
false
+ 0
16800
@@ -134406,6 +151206,7 @@
false
false
false
+ 0
16801
@@ -134414,6 +151215,7 @@
false
false
false
+ 0
16802
@@ -134422,6 +151224,7 @@
false
false
false
+ 0
16803
@@ -134430,6 +151233,7 @@
false
false
false
+ 0
16804
@@ -134438,6 +151242,7 @@
false
false
false
+ 0
16805
@@ -134446,6 +151251,7 @@
false
false
false
+ 0
16806
@@ -134454,6 +151260,7 @@
false
false
false
+ 0
16807
@@ -134462,6 +151269,7 @@
false
false
false
+ 0
16808
@@ -134470,6 +151278,7 @@
false
false
false
+ 0
16809
@@ -134478,6 +151287,7 @@
false
false
false
+ 0
16810
@@ -134486,6 +151296,7 @@
false
false
false
+ 0
16811
@@ -134494,6 +151305,7 @@
false
false
false
+ 0
16812
@@ -134502,6 +151314,7 @@
false
false
false
+ 0
16813
@@ -134510,6 +151323,7 @@
false
false
false
+ 0
16814
@@ -134518,6 +151332,7 @@
false
false
false
+ 0
16815
@@ -134526,6 +151341,7 @@
false
false
false
+ 0
16816
@@ -134534,6 +151350,7 @@
false
false
false
+ 0
16817
@@ -134542,6 +151359,7 @@
false
false
false
+ 0
16818
@@ -134550,6 +151368,7 @@
false
false
false
+ 0
16819
@@ -134558,6 +151377,7 @@
false
false
false
+ 0
16820
@@ -134566,6 +151386,7 @@
false
false
false
+ 0
16821
@@ -134574,6 +151395,7 @@
false
false
false
+ 0
16822
@@ -134582,6 +151404,7 @@
false
false
false
+ 0
16823
@@ -134590,6 +151413,7 @@
false
false
false
+ 0
16824
@@ -134598,6 +151422,7 @@
false
false
false
+ 0
16825
@@ -134606,6 +151431,7 @@
false
false
false
+ 0
16826
@@ -134614,6 +151440,7 @@
false
false
false
+ 0
16827
@@ -134622,6 +151449,7 @@
false
false
false
+ 0
16828
@@ -134630,6 +151458,7 @@
false
false
false
+ 0
16829
@@ -134638,6 +151467,7 @@
false
false
false
+ 0
16830
@@ -134646,6 +151476,7 @@
false
false
false
+ 0
16831
@@ -134654,6 +151485,7 @@
false
false
false
+ 0
16832
@@ -134662,6 +151494,7 @@
false
false
false
+ 0
16833
@@ -134670,6 +151503,7 @@
false
false
false
+ 0
16834
@@ -134678,6 +151512,7 @@
false
false
false
+ 0
16835
@@ -134686,6 +151521,7 @@
false
false
false
+ 0
16836
@@ -134694,6 +151530,7 @@
false
false
false
+ 0
16837
@@ -134702,6 +151539,7 @@
false
false
false
+ 0
16838
@@ -134710,6 +151548,7 @@
false
false
false
+ 0
16839
@@ -134718,6 +151557,7 @@
false
false
false
+ 0
16840
@@ -134726,6 +151566,7 @@
false
false
false
+ 0
16841
@@ -134734,6 +151575,7 @@
false
false
false
+ 0
16842
@@ -134742,6 +151584,7 @@
false
false
false
+ 0
16843
@@ -134750,6 +151593,7 @@
false
false
false
+ 0
16844
@@ -134758,6 +151602,7 @@
false
false
false
+ 0
16845
@@ -134766,6 +151611,7 @@
false
false
false
+ 0
16846
@@ -134774,6 +151620,7 @@
false
false
false
+ 0
16847
@@ -134782,6 +151629,7 @@
false
false
false
+ 0
16848
@@ -134790,6 +151638,7 @@
false
false
false
+ 0
16849
@@ -134798,6 +151647,7 @@
false
false
false
+ 0
16850
@@ -134806,6 +151656,7 @@
false
false
false
+ 0
16851
@@ -134814,6 +151665,7 @@
false
false
false
+ 0
16852
@@ -134822,6 +151674,7 @@
false
false
false
+ 0
16853
@@ -134830,6 +151683,7 @@
false
false
false
+ 0
16854
@@ -134838,6 +151692,7 @@
false
false
false
+ 0
16855
@@ -134846,6 +151701,7 @@
false
false
false
+ 0
16856
@@ -134854,6 +151710,7 @@
false
false
false
+ 0
16857
@@ -134862,6 +151719,7 @@
false
false
false
+ 0
16858
@@ -134870,6 +151728,7 @@
false
false
false
+ 0
16859
@@ -134878,6 +151737,7 @@
false
false
false
+ 0
16860
@@ -134886,6 +151746,7 @@
false
false
false
+ 0
16861
@@ -134894,6 +151755,7 @@
false
false
false
+ 0
16862
@@ -134902,6 +151764,7 @@
false
false
false
+ 0
16863
@@ -134910,6 +151773,7 @@
false
false
false
+ 0
16864
@@ -134918,6 +151782,7 @@
false
false
false
+ 0
16865
@@ -134926,6 +151791,7 @@
false
false
false
+ 0
16866
@@ -134934,6 +151800,7 @@
false
false
false
+ 0
16867
@@ -134942,6 +151809,7 @@
false
false
false
+ 0
16868
@@ -134950,6 +151818,7 @@
false
false
false
+ 0
16869
@@ -134958,6 +151827,7 @@
false
false
false
+ 0
16870
@@ -134966,6 +151836,7 @@
false
false
false
+ 0
16871
@@ -134974,6 +151845,7 @@
false
false
false
+ 0
16872
@@ -134982,6 +151854,7 @@
false
false
false
+ 0
16873
@@ -134990,6 +151863,7 @@
false
false
false
+ 0
16874
@@ -134998,6 +151872,7 @@
false
false
false
+ 0
16875
@@ -135006,6 +151881,7 @@
false
false
false
+ 0
16876
@@ -135014,6 +151890,7 @@
false
false
false
+ 0
16877
@@ -135022,6 +151899,7 @@
false
false
false
+ 0
16878
@@ -135030,6 +151908,7 @@
false
false
false
+ 0
16879
@@ -135038,6 +151917,7 @@
false
false
false
+ 0
16880
@@ -135046,6 +151926,7 @@
false
false
false
+ 0
16881
@@ -135054,6 +151935,7 @@
false
false
false
+ 0
16882
@@ -135062,6 +151944,7 @@
false
false
false
+ 0
16883
@@ -135070,6 +151953,7 @@
false
false
false
+ 0
16884
@@ -135078,6 +151962,7 @@
false
false
false
+ 0
16885
@@ -135086,6 +151971,7 @@
false
false
false
+ 0
16886
@@ -135094,6 +151980,7 @@
false
false
false
+ 0
16887
@@ -135102,6 +151989,7 @@
false
false
false
+ 0
16888
@@ -135110,6 +151998,7 @@
false
false
false
+ 0
16889
@@ -135118,6 +152007,7 @@
false
false
false
+ 0
16890
@@ -135126,6 +152016,7 @@
false
false
false
+ 0
16891
@@ -135134,6 +152025,7 @@
false
false
false
+ 0
16892
@@ -135142,6 +152034,7 @@
false
false
false
+ 0
16893
@@ -135150,6 +152043,7 @@
false
false
false
+ 0
16894
@@ -135158,6 +152052,7 @@
false
false
false
+ 0
16895
@@ -135166,6 +152061,7 @@
false
false
false
+ 0
16896
@@ -135174,6 +152070,7 @@
false
false
false
+ 0
16897
@@ -135182,6 +152079,7 @@
false
false
false
+ 0
16898
@@ -135190,6 +152088,7 @@
false
false
false
+ 0
16899
@@ -135198,6 +152097,7 @@
false
false
false
+ 0
16900
@@ -135206,6 +152106,7 @@
false
false
false
+ 0
16901
@@ -135214,6 +152115,7 @@
false
false
false
+ 0
16902
@@ -135222,6 +152124,7 @@
false
false
false
+ 0
16903
@@ -135230,6 +152133,7 @@
false
false
false
+ 0
16904
@@ -135238,6 +152142,7 @@
false
false
false
+ 0
16905
@@ -135246,6 +152151,7 @@
false
false
false
+ 0
16906
@@ -135254,6 +152160,7 @@
false
false
false
+ 0
16907
@@ -135262,6 +152169,7 @@
false
false
false
+ 0
16908
@@ -135270,6 +152178,7 @@
false
false
false
+ 0
16909
@@ -135278,6 +152187,7 @@
false
false
false
+ 0
16910
@@ -135286,6 +152196,7 @@
false
false
false
+ 0
16911
@@ -135294,6 +152205,7 @@
false
false
false
+ 0
16912
@@ -135302,6 +152214,7 @@
false
false
false
+ 0
16913
@@ -135310,6 +152223,7 @@
false
false
false
+ 0
16914
@@ -135318,6 +152232,7 @@
false
false
false
+ 0
16915
@@ -135326,6 +152241,7 @@
false
false
false
+ 0
16916
@@ -135334,6 +152250,7 @@
false
false
false
+ 0
16917
@@ -135342,6 +152259,7 @@
false
false
false
+ 0
16918
@@ -135350,6 +152268,7 @@
false
false
false
+ 0
16919
@@ -135358,6 +152277,7 @@
false
false
false
+ 0
16920
@@ -135366,6 +152286,7 @@
false
false
false
+ 0
16921
@@ -135374,6 +152295,7 @@
false
false
false
+ 0
16922
@@ -135382,6 +152304,7 @@
false
false
false
+ 0
16923
@@ -135390,6 +152313,7 @@
false
false
false
+ 0
16924
@@ -135398,6 +152322,7 @@
false
false
false
+ 0
16925
@@ -135406,6 +152331,7 @@
false
false
false
+ 0
16926
@@ -135414,6 +152340,7 @@
false
false
false
+ 0
16927
@@ -135422,6 +152349,7 @@
false
false
false
+ 0
16928
@@ -135430,6 +152358,7 @@
false
false
false
+ 0
16929
@@ -135438,6 +152367,7 @@
false
false
false
+ 0
16930
@@ -135446,6 +152376,7 @@
false
false
false
+ 0
16931
@@ -135454,6 +152385,7 @@
false
false
false
+ 0
16932
@@ -135462,6 +152394,7 @@
false
false
false
+ 0
16933
@@ -135470,6 +152403,7 @@
false
false
false
+ 0
16934
@@ -135478,6 +152412,7 @@
false
false
false
+ 0
16935
@@ -135486,6 +152421,7 @@
false
false
false
+ 0
16936
@@ -135494,6 +152430,7 @@
false
false
false
+ 0
16937
@@ -135502,6 +152439,7 @@
false
false
false
+ 0
16938
@@ -135510,6 +152448,7 @@
false
false
false
+ 0
16939
@@ -135518,6 +152457,7 @@
false
false
false
+ 0
16940
@@ -135526,6 +152466,7 @@
false
false
false
+ 0
16941
@@ -135534,6 +152475,7 @@
false
false
false
+ 0
16942
@@ -135542,6 +152484,7 @@
false
false
false
+ 0
16943
@@ -135550,6 +152493,7 @@
false
false
false
+ 0
16944
@@ -135558,6 +152502,7 @@
false
false
false
+ 0
16945
@@ -135566,6 +152511,7 @@
false
false
false
+ 0
16946
@@ -135574,6 +152520,7 @@
false
false
false
+ 0
16947
@@ -135582,6 +152529,7 @@
false
false
false
+ 0
16948
@@ -135590,6 +152538,7 @@
false
false
false
+ 0
16949
@@ -135598,6 +152547,7 @@
false
false
false
+ 0
16950
@@ -135606,6 +152556,7 @@
false
false
false
+ 0
16951
@@ -135614,6 +152565,7 @@
false
false
false
+ 0
16952
@@ -135622,6 +152574,7 @@
false
false
false
+ 0
16953
@@ -135630,6 +152583,7 @@
false
false
false
+ 0
16954
@@ -135638,6 +152592,7 @@
false
false
false
+ 0
16955
@@ -135646,6 +152601,7 @@
false
false
false
+ 0
16956
@@ -135654,6 +152610,7 @@
false
false
false
+ 0
16957
@@ -135662,6 +152619,7 @@
false
false
false
+ 0
16958
@@ -135670,6 +152628,7 @@
false
false
false
+ 0
16959
@@ -135678,6 +152637,7 @@
false
false
false
+ 0
16960
@@ -135686,6 +152646,7 @@
false
false
false
+ 0
16961
@@ -135694,6 +152655,7 @@
false
false
false
+ 0
16962
@@ -135702,6 +152664,7 @@
false
false
false
+ 0
16963
@@ -135710,6 +152673,7 @@
false
false
false
+ 0
16964
@@ -135718,6 +152682,7 @@
false
false
false
+ 0
16965
@@ -135726,6 +152691,7 @@
false
false
false
+ 0
16966
@@ -135734,6 +152700,7 @@
false
false
false
+ 0
16967
@@ -135742,6 +152709,7 @@
false
false
false
+ 0
16968
@@ -135750,6 +152718,7 @@
false
false
false
+ 0
16969
@@ -135758,6 +152727,7 @@
false
false
false
+ 0
16970
@@ -135766,6 +152736,7 @@
false
false
false
+ 0
16971
@@ -135774,6 +152745,7 @@
false
false
false
+ 0
16972
@@ -135782,6 +152754,7 @@
false
false
false
+ 0
16973
@@ -135790,6 +152763,7 @@
false
false
false
+ 0
16974
@@ -135798,6 +152772,7 @@
false
false
false
+ 0
16975
@@ -135806,6 +152781,7 @@
false
false
false
+ 0
16976
@@ -135814,6 +152790,7 @@
false
false
false
+ 0
16977
@@ -135822,6 +152799,7 @@
false
false
false
+ 0
16978
@@ -135830,6 +152808,7 @@
false
false
false
+ 0
16979
@@ -135838,6 +152817,7 @@
false
false
false
+ 0
16980
@@ -135846,6 +152826,7 @@
false
false
false
+ 0
16981
@@ -135854,6 +152835,7 @@
false
false
false
+ 0
16982
@@ -135862,6 +152844,7 @@
false
false
false
+ 0
16983
@@ -135870,6 +152853,7 @@
false
false
false
+ 0
16984
@@ -135878,6 +152862,7 @@
false
false
false
+ 0
16985
@@ -135886,6 +152871,7 @@
false
false
false
+ 0
16986
@@ -135894,6 +152880,7 @@
false
false
false
+ 0
16987
@@ -135902,6 +152889,7 @@
false
false
false
+ 0
16988
@@ -135910,6 +152898,7 @@
false
false
false
+ 0
16989
@@ -135918,6 +152907,7 @@
false
false
false
+ 0
16990
@@ -135926,6 +152916,7 @@
false
false
false
+ 0
16991
@@ -135934,6 +152925,7 @@
false
false
false
+ 0
16992
@@ -135942,6 +152934,7 @@
false
false
false
+ 0
16993
@@ -135950,6 +152943,7 @@
false
false
false
+ 0
16994
@@ -135958,6 +152952,7 @@
false
false
false
+ 0
16995
@@ -135966,6 +152961,7 @@
false
false
false
+ 0
16996
@@ -135974,6 +152970,7 @@
false
false
false
+ 0
16997
@@ -135982,6 +152979,7 @@
false
false
false
+ 0
16998
@@ -135990,6 +152988,7 @@
false
false
false
+ 0
16999
@@ -135998,6 +152997,7 @@
false
false
false
+ 0
17000
@@ -136006,6 +153006,7 @@
false
false
false
+ 0
17001
@@ -136014,6 +153015,7 @@
false
false
false
+ 0
17002
@@ -136022,6 +153024,7 @@
false
false
false
+ 0
17003
@@ -136030,6 +153033,7 @@
false
false
false
+ 0
17004
@@ -136038,6 +153042,7 @@
false
false
false
+ 0
17005
@@ -136046,6 +153051,7 @@
false
false
false
+ 0
17006
@@ -136054,6 +153060,7 @@
false
false
false
+ 0
17007
@@ -136062,6 +153069,7 @@
false
false
false
+ 0
17008
@@ -136070,6 +153078,7 @@
false
false
false
+ 0
17009
@@ -136078,6 +153087,7 @@
false
false
false
+ 0
17010
@@ -136086,6 +153096,7 @@
false
false
false
+ 0
17011
@@ -136094,6 +153105,7 @@
false
false
false
+ 0
17012
@@ -136102,6 +153114,7 @@
false
false
false
+ 0
17013
@@ -136110,6 +153123,7 @@
false
false
false
+ 0
17014
@@ -136118,6 +153132,7 @@
false
false
false
+ 0
17015
@@ -136126,6 +153141,7 @@
false
false
false
+ 0
17016
@@ -136134,6 +153150,7 @@
false
false
false
+ 0
17017
@@ -136142,6 +153159,7 @@
false
false
false
+ 0
17018
@@ -136150,6 +153168,7 @@
false
false
false
+ 0
17019
@@ -136158,6 +153177,7 @@
false
false
false
+ 0
17020
@@ -136166,6 +153186,7 @@
false
false
false
+ 0
17021
@@ -136174,6 +153195,7 @@
false
false
false
+ 0
17022
@@ -136182,6 +153204,7 @@
false
false
false
+ 0
17023
@@ -136190,6 +153213,7 @@
false
false
false
+ 0
17024
@@ -136198,6 +153222,7 @@
false
false
false
+ 0
17025
@@ -136206,6 +153231,7 @@
false
false
false
+ 0
17026
@@ -136214,6 +153240,7 @@
false
false
false
+ 0
17027
@@ -136222,6 +153249,7 @@
false
false
false
+ 0
17028
@@ -136230,6 +153258,7 @@
false
false
false
+ 0
17029
@@ -136238,6 +153267,7 @@
false
false
false
+ 0
17030
@@ -136246,6 +153276,7 @@
false
false
false
+ 0
17031
@@ -136254,6 +153285,7 @@
false
false
false
+ 0
17032
@@ -136262,6 +153294,7 @@
false
false
false
+ 0
17033
@@ -136270,6 +153303,7 @@
false
false
false
+ 0
17034
@@ -136278,6 +153312,7 @@
false
false
false
+ 0
17035
@@ -136286,6 +153321,7 @@
false
false
false
+ 0
17036
@@ -136294,6 +153330,7 @@
false
false
false
+ 0
17037
@@ -136302,6 +153339,7 @@
false
false
false
+ 0
17038
@@ -136310,6 +153348,7 @@
false
false
false
+ 0
17039
@@ -136318,6 +153357,7 @@
false
false
false
+ 0
17040
@@ -136326,6 +153366,7 @@
false
false
false
+ 0
17041
@@ -136334,6 +153375,7 @@
false
false
false
+ 0
17042
@@ -136342,6 +153384,7 @@
false
false
false
+ 0
17043
@@ -136350,6 +153393,7 @@
false
false
false
+ 0
17044
@@ -136358,6 +153402,7 @@
false
false
false
+ 0
17045
@@ -136366,6 +153411,7 @@
false
false
false
+ 0
17046
@@ -136374,6 +153420,7 @@
false
false
false
+ 0
17047
@@ -136382,6 +153429,7 @@
false
false
false
+ 0
17048
@@ -136390,6 +153438,7 @@
false
false
false
+ 0
17049
@@ -136398,6 +153447,7 @@
false
false
false
+ 0
17050
@@ -136406,6 +153456,7 @@
false
false
false
+ 0
17051
@@ -136414,6 +153465,7 @@
false
false
false
+ 0
17052
@@ -136422,6 +153474,7 @@
false
false
false
+ 0
17053
@@ -136430,6 +153483,7 @@
false
false
false
+ 0
17054
@@ -136438,6 +153492,7 @@
false
false
false
+ 0
17055
@@ -136446,6 +153501,7 @@
false
false
false
+ 0
17056
@@ -136454,6 +153510,7 @@
false
false
false
+ 0
17057
@@ -136462,6 +153519,7 @@
false
false
false
+ 0
17058
@@ -136470,6 +153528,7 @@
false
false
false
+ 0
17059
@@ -136478,6 +153537,7 @@
false
false
false
+ 0
17060
@@ -136486,6 +153546,7 @@
false
false
false
+ 0
17061
@@ -136494,6 +153555,7 @@
false
false
false
+ 0
17062
@@ -136502,6 +153564,7 @@
false
false
false
+ 0
17063
@@ -136510,6 +153573,7 @@
false
false
false
+ 0
17064
@@ -136518,6 +153582,7 @@
false
false
false
+ 0
17065
@@ -136526,6 +153591,7 @@
false
false
false
+ 0
17066
@@ -136534,6 +153600,7 @@
false
false
false
+ 0
17067
@@ -136542,6 +153609,7 @@
false
false
false
+ 0
17068
@@ -136550,6 +153618,7 @@
false
false
false
+ 0
17069
@@ -136558,6 +153627,7 @@
false
false
false
+ 0
17070
@@ -136566,6 +153636,7 @@
false
false
false
+ 0
17071
@@ -136574,6 +153645,7 @@
false
false
false
+ 0
17072
@@ -136582,6 +153654,7 @@
false
false
false
+ 0
17073
@@ -136590,6 +153663,7 @@
false
false
false
+ 0
17074
@@ -136598,6 +153672,7 @@
false
false
false
+ 0
17075
@@ -136606,6 +153681,7 @@
false
false
false
+ 0
17076
@@ -136614,6 +153690,7 @@
false
false
false
+ 0
17077
@@ -136622,6 +153699,7 @@
false
false
false
+ 0
17078
@@ -136630,6 +153708,7 @@
false
false
false
+ 0
17079
@@ -136638,6 +153717,7 @@
false
false
false
+ 0
17080
@@ -136646,6 +153726,7 @@
false
false
false
+ 0
17081
@@ -136654,6 +153735,7 @@
false
false
false
+ 0
17082
@@ -136662,6 +153744,7 @@
false
false
false
+ 0
17083
@@ -136670,6 +153753,7 @@
false
false
false
+ 0
17084
@@ -136678,6 +153762,7 @@
false
false
false
+ 0
17085
@@ -136686,6 +153771,7 @@
false
false
false
+ 0
17086
@@ -136694,6 +153780,7 @@
false
false
false
+ 0
17087
@@ -136702,6 +153789,7 @@
false
false
false
+ 0
17088
@@ -136710,6 +153798,7 @@
false
false
false
+ 0
17089
@@ -136718,6 +153807,7 @@
false
false
false
+ 0
17090
@@ -136726,6 +153816,7 @@
false
false
false
+ 0
17091
@@ -136734,6 +153825,7 @@
false
false
false
+ 0
17092
@@ -136742,6 +153834,7 @@
false
false
false
+ 0
17093
@@ -136750,6 +153843,7 @@
false
false
false
+ 0
17094
@@ -136758,6 +153852,7 @@
false
false
false
+ 0
17095
@@ -136766,6 +153861,7 @@
false
false
false
+ 0
17096
@@ -136774,6 +153870,7 @@
false
false
false
+ 0
17097
@@ -136782,6 +153879,7 @@
false
false
false
+ 0
17098
@@ -136790,6 +153888,7 @@
false
false
false
+ 0
17099
@@ -136798,6 +153897,7 @@
false
false
false
+ 0
17100
@@ -136806,6 +153906,7 @@
false
false
false
+ 0
17101
@@ -136814,6 +153915,7 @@
false
false
false
+ 0
17102
@@ -136822,6 +153924,7 @@
false
false
false
+ 0
17103
@@ -136830,6 +153933,7 @@
false
false
false
+ 0
17104
@@ -136838,6 +153942,7 @@
false
false
false
+ 0
17105
@@ -136846,6 +153951,7 @@
false
false
false
+ 0
17106
@@ -136854,6 +153960,7 @@
false
false
false
+ 0
17107
@@ -136862,6 +153969,7 @@
false
false
false
+ 0
17108
@@ -136870,6 +153978,7 @@
false
false
false
+ 0
17109
@@ -136878,6 +153987,7 @@
false
false
false
+ 0
17110
@@ -136886,6 +153996,7 @@
false
false
false
+ 0
17111
@@ -136894,6 +154005,7 @@
false
false
false
+ 0
17112
@@ -136902,6 +154014,7 @@
false
false
false
+ 0
17113
@@ -136910,6 +154023,7 @@
false
false
false
+ 0
17114
@@ -136918,6 +154032,7 @@
false
false
false
+ 0
17115
@@ -136926,6 +154041,7 @@
false
false
false
+ 0
17116
@@ -136934,6 +154050,7 @@
false
false
false
+ 0
17117
@@ -136942,6 +154059,7 @@
false
false
false
+ 0
17118
@@ -136950,6 +154068,7 @@
false
false
false
+ 0
17119
@@ -136958,6 +154077,7 @@
false
false
false
+ 0
17120
@@ -136966,6 +154086,7 @@
false
false
false
+ 0
17121
@@ -136974,6 +154095,7 @@
false
false
false
+ 0
17122
@@ -136982,6 +154104,7 @@
false
false
false
+ 0
17123
@@ -136990,6 +154113,7 @@
false
false
false
+ 0
17124
@@ -136998,6 +154122,7 @@
false
false
false
+ 0
17125
@@ -137006,6 +154131,7 @@
false
false
false
+ 0
17126
@@ -137014,6 +154140,7 @@
false
false
false
+ 0
17127
@@ -137022,6 +154149,7 @@
false
false
false
+ 0
17128
@@ -137030,6 +154158,7 @@
false
false
false
+ 0
17129
@@ -137038,6 +154167,7 @@
false
false
false
+ 0
17130
@@ -137046,6 +154176,7 @@
false
false
false
+ 0
17131
@@ -137054,6 +154185,7 @@
false
false
false
+ 0
17132
@@ -137062,6 +154194,7 @@
false
false
false
+ 0
17133
@@ -137070,6 +154203,7 @@
false
false
false
+ 0
17134
@@ -137078,6 +154212,7 @@
false
false
false
+ 0
17135
@@ -137086,6 +154221,7 @@
false
false
false
+ 0
17136
@@ -137094,6 +154230,7 @@
false
false
false
+ 0
17137
@@ -137102,6 +154239,7 @@
false
false
false
+ 0
17138
@@ -137110,6 +154248,7 @@
false
false
false
+ 0
17139
@@ -137118,6 +154257,7 @@
false
false
false
+ 0
17140
@@ -137126,6 +154266,7 @@
false
false
false
+ 0
17141
@@ -137134,6 +154275,7 @@
false
false
false
+ 0
17142
@@ -137142,6 +154284,7 @@
false
false
false
+ 0
17143
@@ -137150,6 +154293,7 @@
false
false
false
+ 0
17144
@@ -137158,6 +154302,7 @@
false
false
false
+ 0
17145
@@ -137166,6 +154311,7 @@
false
false
false
+ 0
17146
@@ -137174,6 +154320,7 @@
false
false
false
+ 0
17147
@@ -137182,6 +154329,7 @@
false
false
false
+ 0
17148
@@ -137190,6 +154338,7 @@
false
false
false
+ 0
17149
@@ -137198,6 +154347,7 @@
false
false
false
+ 0
17150
@@ -137206,6 +154356,7 @@
false
false
false
+ 0
17151
@@ -137214,6 +154365,7 @@
false
false
false
+ 0
17152
@@ -137222,6 +154374,7 @@
false
false
false
+ 0
17153
@@ -137230,6 +154383,7 @@
false
false
false
+ 0
17154
@@ -137238,6 +154392,7 @@
false
false
false
+ 0
17155
@@ -137246,6 +154401,7 @@
false
false
false
+ 0
17156
@@ -137254,6 +154410,7 @@
false
false
false
+ 0
17157
@@ -137262,6 +154419,7 @@
false
false
false
+ 0
17158
@@ -137270,6 +154428,7 @@
false
false
false
+ 0
17159
@@ -137278,6 +154437,7 @@
false
false
false
+ 0
17160
@@ -137286,6 +154446,7 @@
false
false
false
+ 0
17161
@@ -137294,6 +154455,7 @@
false
false
false
+ 0
17162
@@ -137302,6 +154464,7 @@
false
false
false
+ 0
17163
@@ -137310,6 +154473,7 @@
false
false
false
+ 0
17164
@@ -137318,6 +154482,7 @@
false
false
false
+ 0
17165
@@ -137326,6 +154491,7 @@
false
false
false
+ 0
17166
@@ -137334,6 +154500,7 @@
false
false
false
+ 0
17167
@@ -137342,6 +154509,7 @@
false
false
false
+ 0
17168
@@ -137350,6 +154518,7 @@
false
false
false
+ 0
17169
@@ -137358,6 +154527,7 @@
false
false
false
+ 0
17170
@@ -137366,6 +154536,7 @@
false
false
false
+ 0
17171
@@ -137374,6 +154545,7 @@
false
false
false
+ 0
17172
@@ -137382,6 +154554,7 @@
false
false
false
+ 0
17173
@@ -137390,6 +154563,7 @@
false
false
false
+ 0
17174
@@ -137398,6 +154572,7 @@
false
false
false
+ 0
17175
@@ -137406,6 +154581,7 @@
false
false
false
+ 0
17176
@@ -137414,6 +154590,7 @@
false
false
false
+ 0
17177
@@ -137422,6 +154599,7 @@
false
false
false
+ 0
17178
@@ -137430,6 +154608,7 @@
false
false
false
+ 0
17179
@@ -137438,6 +154617,7 @@
false
false
false
+ 0
17180
@@ -137446,6 +154626,7 @@
false
false
false
+ 0
17181
@@ -137454,6 +154635,7 @@
false
false
false
+ 0
17182
@@ -137462,6 +154644,7 @@
false
false
false
+ 0
17183
@@ -137470,6 +154653,7 @@
false
false
false
+ 0
17184
@@ -137478,6 +154662,7 @@
false
false
false
+ 0
17185
@@ -137486,6 +154671,7 @@
false
false
false
+ 0
17186
@@ -137494,6 +154680,7 @@
false
false
false
+ 0
17187
@@ -137502,6 +154689,7 @@
false
false
false
+ 0
17188
@@ -137510,6 +154698,7 @@
false
false
false
+ 0
17189
@@ -137518,6 +154707,7 @@
false
false
false
+ 0
17190
@@ -137526,6 +154716,7 @@
false
false
false
+ 0
17191
@@ -137534,6 +154725,7 @@
false
false
false
+ 0
17192
@@ -137542,6 +154734,7 @@
false
false
false
+ 0
17193
@@ -137550,6 +154743,7 @@
false
false
false
+ 0
17194
@@ -137558,6 +154752,7 @@
false
false
false
+ 0
17195
@@ -137566,6 +154761,7 @@
false
false
false
+ 0
17196
@@ -137574,6 +154770,7 @@
false
false
false
+ 0
17197
@@ -137582,6 +154779,7 @@
false
false
false
+ 0
17198
@@ -137590,6 +154788,7 @@
false
false
false
+ 0
17199
@@ -137598,6 +154797,7 @@
false
false
false
+ 0
17200
@@ -137606,6 +154806,7 @@
false
false
false
+ 0
17201
@@ -137614,6 +154815,7 @@
false
false
false
+ 0
17202
@@ -137622,6 +154824,7 @@
false
false
false
+ 0
17203
@@ -137630,6 +154833,7 @@
false
false
false
+ 0
17204
@@ -137638,6 +154842,7 @@
false
false
false
+ 0
17205
@@ -137646,6 +154851,7 @@
false
false
false
+ 0
17206
@@ -137654,6 +154860,7 @@
false
false
false
+ 0
17207
@@ -137662,6 +154869,7 @@
false
false
false
+ 0
17208
@@ -137670,6 +154878,7 @@
false
false
false
+ 0
17209
@@ -137678,6 +154887,7 @@
false
false
false
+ 0
17210
@@ -137686,6 +154896,7 @@
false
false
false
+ 0
17211
@@ -137694,6 +154905,7 @@
false
false
false
+ 0
17212
@@ -137702,6 +154914,7 @@
false
false
false
+ 0
17213
@@ -137710,6 +154923,7 @@
false
false
false
+ 0
17214
@@ -137718,6 +154932,7 @@
false
false
false
+ 0
17215
@@ -137726,6 +154941,7 @@
false
false
false
+ 0
17216
@@ -137734,6 +154950,7 @@
false
false
false
+ 0
17217
@@ -137742,6 +154959,7 @@
false
false
false
+ 0
17218
@@ -137750,6 +154968,7 @@
false
false
false
+ 0
17219
@@ -137758,6 +154977,7 @@
false
false
false
+ 0
17220
@@ -137766,6 +154986,7 @@
false
false
false
+ 0
17221
@@ -137774,6 +154995,7 @@
false
false
false
+ 0
17222
@@ -137782,6 +155004,7 @@
false
false
false
+ 0
17223
@@ -137790,6 +155013,7 @@
false
false
false
+ 0
17224
@@ -137798,6 +155022,7 @@
false
false
false
+ 0
17225
@@ -137806,6 +155031,7 @@
false
false
false
+ 0
17226
@@ -137814,6 +155040,7 @@
false
false
false
+ 0
17227
@@ -137822,6 +155049,7 @@
false
false
false
+ 0
17228
@@ -137830,6 +155058,7 @@
false
false
false
+ 0
17229
@@ -137838,6 +155067,7 @@
false
false
false
+ 0
17230
@@ -137846,6 +155076,7 @@
false
false
false
+ 0
17231
@@ -137854,6 +155085,7 @@
false
false
false
+ 0
17232
@@ -137862,6 +155094,7 @@
false
false
false
+ 0
17233
@@ -137870,6 +155103,7 @@
false
false
false
+ 0
17234
@@ -137878,6 +155112,7 @@
false
false
false
+ 0
17235
@@ -137886,6 +155121,7 @@
false
false
false
+ 0
17236
@@ -137894,6 +155130,7 @@
false
false
false
+ 0
17237
@@ -137902,6 +155139,7 @@
false
false
false
+ 0
17238
@@ -137910,6 +155148,7 @@
false
false
false
+ 0
17239
@@ -137918,6 +155157,7 @@
false
false
false
+ 0
17240
@@ -137926,6 +155166,7 @@
false
false
false
+ 0
17241
@@ -137934,6 +155175,7 @@
false
false
false
+ 0
17242
@@ -137942,6 +155184,7 @@
false
false
false
+ 0
17243
@@ -137950,6 +155193,7 @@
false
false
false
+ 0
17244
@@ -137958,6 +155202,7 @@
false
false
false
+ 0
17245
@@ -137966,6 +155211,7 @@
false
false
false
+ 0
17246
@@ -137974,6 +155220,7 @@
false
false
false
+ 0
17247
@@ -137982,6 +155229,7 @@
false
false
false
+ 0
17248
@@ -137990,6 +155238,7 @@
false
false
false
+ 0
17249
@@ -137998,6 +155247,7 @@
false
false
false
+ 0
17250
@@ -138006,6 +155256,7 @@
false
false
false
+ 0
17251
@@ -138014,6 +155265,7 @@
false
false
false
+ 0
17252
@@ -138022,6 +155274,7 @@
false
false
false
+ 0
17253
@@ -138030,6 +155283,7 @@
false
false
false
+ 0
17254
@@ -138038,6 +155292,7 @@
false
false
false
+ 0
17255
@@ -138046,6 +155301,7 @@
false
false
false
+ 0
17256
@@ -138054,6 +155310,7 @@
false
false
false
+ 0
17257
@@ -138062,6 +155319,7 @@
false
false
false
+ 0
17258
@@ -138070,6 +155328,7 @@
false
false
false
+ 0
17259
@@ -138078,6 +155337,7 @@
false
false
false
+ 0
17260
@@ -138086,6 +155346,7 @@
false
false
false
+ 0
17261
@@ -138094,6 +155355,7 @@
false
false
false
+ 0
17262
@@ -138102,6 +155364,7 @@
false
false
false
+ 0
17263
@@ -138110,6 +155373,7 @@
false
false
false
+ 0
17264
@@ -138118,6 +155382,7 @@
false
false
false
+ 0
17265
@@ -138126,6 +155391,7 @@
false
false
false
+ 0
17266
@@ -138134,6 +155400,7 @@
false
false
false
+ 0
17267
@@ -138142,6 +155409,7 @@
false
false
false
+ 0
17268
@@ -138150,6 +155418,7 @@
false
false
false
+ 0
17269
@@ -138158,6 +155427,7 @@
false
false
false
+ 0
17270
@@ -138166,6 +155436,7 @@
false
false
false
+ 0
17271
@@ -138174,6 +155445,7 @@
false
false
false
+ 0
17272
@@ -138182,6 +155454,7 @@
false
false
false
+ 0
17273
@@ -138190,6 +155463,7 @@
false
false
false
+ 0
17274
@@ -138198,6 +155472,7 @@
false
false
false
+ 0
17275
@@ -138206,6 +155481,7 @@
false
false
false
+ 0
17276
@@ -138214,6 +155490,7 @@
false
false
false
+ 0
17277
@@ -138222,6 +155499,7 @@
false
false
false
+ 0
17278
@@ -138230,6 +155508,7 @@
false
false
false
+ 0
17279
@@ -138238,6 +155517,7 @@
false
false
false
+ 0
17280
@@ -138246,6 +155526,7 @@
false
false
false
+ 0
17281
@@ -138254,6 +155535,7 @@
false
false
false
+ 0
17282
@@ -138262,6 +155544,7 @@
false
false
false
+ 0
17283
@@ -138270,6 +155553,7 @@
false
false
false
+ 0
17284
@@ -138278,6 +155562,7 @@
false
false
false
+ 0
17285
@@ -138286,6 +155571,7 @@
false
false
false
+ 0
17286
@@ -138294,6 +155580,7 @@
false
false
false
+ 0
17287
@@ -138302,6 +155589,7 @@
false
false
false
+ 0
17288
@@ -138310,6 +155598,7 @@
false
false
false
+ 0
17289
@@ -138318,6 +155607,7 @@
false
false
false
+ 0
17290
@@ -138326,6 +155616,7 @@
false
false
false
+ 0
17291
@@ -138334,6 +155625,7 @@
false
false
false
+ 0
17292
@@ -138342,6 +155634,7 @@
false
false
false
+ 0
17293
@@ -138350,6 +155643,7 @@
false
false
false
+ 0
17294
@@ -138358,6 +155652,7 @@
false
false
false
+ 0
17295
@@ -138366,6 +155661,7 @@
false
false
false
+ 0
17296
@@ -138374,6 +155670,7 @@
false
false
false
+ 0
17297
@@ -138382,6 +155679,7 @@
false
false
false
+ 0
17298
@@ -138390,6 +155688,7 @@
false
false
false
+ 0
17299
@@ -138398,6 +155697,7 @@
false
false
false
+ 0
17300
@@ -138406,6 +155706,7 @@
false
false
false
+ 0
17301
@@ -138414,6 +155715,7 @@
false
false
false
+ 0
17302
@@ -138422,6 +155724,7 @@
false
false
false
+ 0
17303
@@ -138430,6 +155733,7 @@
false
false
false
+ 0
17304
@@ -138438,6 +155742,7 @@
false
false
false
+ 0
17305
@@ -138446,6 +155751,7 @@
false
false
false
+ 0
17306
@@ -138454,6 +155760,7 @@
false
false
false
+ 0
17307
@@ -138462,6 +155769,7 @@
false
false
false
+ 0
17308
@@ -138470,6 +155778,7 @@
false
false
false
+ 0
17309
@@ -138478,6 +155787,7 @@
false
false
false
+ 0
17310
@@ -138486,6 +155796,7 @@
false
false
false
+ 0
17311
@@ -138494,6 +155805,7 @@
false
false
false
+ 0
17312
@@ -138502,6 +155814,7 @@
false
false
false
+ 0
17313
@@ -138510,6 +155823,7 @@
false
false
false
+ 0
17314
@@ -138518,6 +155832,7 @@
false
false
false
+ 0
17315
@@ -138526,6 +155841,7 @@
false
false
false
+ 0
17316
@@ -138534,6 +155850,7 @@
false
false
false
+ 0
17317
@@ -138542,6 +155859,7 @@
false
false
false
+ 0
17318
@@ -138550,6 +155868,7 @@
false
false
false
+ 0
17319
@@ -138558,6 +155877,7 @@
false
false
false
+ 0
17320
@@ -138566,6 +155886,7 @@
false
false
false
+ 0
17321
@@ -138574,6 +155895,7 @@
false
false
false
+ 0
17322
@@ -138582,6 +155904,7 @@
false
false
false
+ 0
17323
@@ -138590,6 +155913,7 @@
false
false
false
+ 0
17324
@@ -138598,6 +155922,7 @@
false
false
false
+ 0
17325
@@ -138606,6 +155931,7 @@
false
false
false
+ 0
17326
@@ -138614,6 +155940,7 @@
false
false
false
+ 0
17327
@@ -138622,6 +155949,7 @@
false
false
false
+ 0
17328
@@ -138630,6 +155958,7 @@
false
false
false
+ 0
17329
@@ -138638,6 +155967,7 @@
false
false
false
+ 0
17330
@@ -138646,6 +155976,7 @@
false
false
false
+ 0
17331
@@ -138654,6 +155985,7 @@
false
false
false
+ 0
17332
@@ -138662,6 +155994,7 @@
false
false
false
+ 0
17333
@@ -138670,6 +156003,7 @@
false
false
false
+ 0
17334
@@ -138678,6 +156012,7 @@
false
false
false
+ 0
17335
@@ -138686,6 +156021,7 @@
false
false
false
+ 0
17336
@@ -138694,6 +156030,7 @@
false
false
false
+ 0
17337
@@ -138702,6 +156039,7 @@
false
false
false
+ 0
17338
@@ -138710,6 +156048,7 @@
false
false
false
+ 0
17339
@@ -138718,6 +156057,7 @@
false
false
false
+ 0
17340
@@ -138726,6 +156066,7 @@
false
false
false
+ 0
17341
@@ -138734,6 +156075,7 @@
false
false
false
+ 0
17342
@@ -138742,6 +156084,7 @@
false
false
false
+ 0
17343
@@ -138750,6 +156093,7 @@
false
false
false
+ 0
17344
@@ -138758,6 +156102,7 @@
false
false
false
+ 0
17345
@@ -138766,6 +156111,7 @@
false
false
false
+ 0
17346
@@ -138774,6 +156120,7 @@
false
false
false
+ 0
17347
@@ -138782,6 +156129,7 @@
false
false
false
+ 0
17348
@@ -138790,6 +156138,7 @@
false
false
false
+ 0
17349
@@ -138798,6 +156147,7 @@
false
false
false
+ 0
17350
@@ -138806,6 +156156,7 @@
false
false
false
+ 0
17351
@@ -138814,6 +156165,7 @@
false
false
false
+ 0
17352
@@ -138822,6 +156174,7 @@
false
false
false
+ 0
17353
@@ -138830,6 +156183,7 @@
false
false
false
+ 0
17354
@@ -138838,6 +156192,7 @@
false
false
false
+ 0
17355
@@ -138846,6 +156201,7 @@
false
false
false
+ 0
17356
@@ -138854,6 +156210,7 @@
false
false
false
+ 0
17357
@@ -138862,6 +156219,7 @@
false
false
false
+ 0
17358
@@ -138870,6 +156228,7 @@
false
false
false
+ 0
17359
@@ -138878,6 +156237,7 @@
false
false
false
+ 0
17360
@@ -138886,6 +156246,7 @@
false
false
false
+ 0
17361
@@ -138894,6 +156255,7 @@
false
false
false
+ 0
17362
@@ -138902,6 +156264,7 @@
false
false
false
+ 0
17363
@@ -138910,6 +156273,7 @@
false
false
false
+ 0
17364
@@ -138918,6 +156282,7 @@
false
false
false
+ 0
17365
@@ -138926,6 +156291,7 @@
false
false
false
+ 0
17366
@@ -138934,6 +156300,7 @@
false
false
false
+ 0
17367
@@ -138942,6 +156309,7 @@
false
false
false
+ 0
17368
@@ -138950,6 +156318,7 @@
false
false
false
+ 0
17369
@@ -138958,6 +156327,7 @@
false
false
false
+ 0
17370
@@ -138966,6 +156336,7 @@
false
false
false
+ 0
17371
@@ -138974,6 +156345,7 @@
false
false
false
+ 0
17372
@@ -138982,6 +156354,7 @@
false
false
false
+ 0
17373
@@ -138990,6 +156363,7 @@
false
false
false
+ 0
17374
@@ -138998,6 +156372,7 @@
false
false
false
+ 0
17375
@@ -139006,6 +156381,7 @@
false
false
false
+ 0
17376
@@ -139014,6 +156390,7 @@
false
false
false
+ 0
17377
@@ -139022,6 +156399,7 @@
false
false
false
+ 0
17378
@@ -139030,6 +156408,7 @@
false
false
false
+ 0
17379
@@ -139038,6 +156417,7 @@
false
false
false
+ 0
17380
@@ -139046,6 +156426,7 @@
false
false
false
+ 0
17381
@@ -139054,6 +156435,7 @@
false
false
false
+ 0
17382
@@ -139062,6 +156444,7 @@
false
false
false
+ 0
17383
@@ -139070,6 +156453,7 @@
false
false
false
+ 0
17384
@@ -139078,6 +156462,7 @@
false
false
false
+ 0
17385
@@ -139086,6 +156471,7 @@
false
false
false
+ 0
17386
@@ -139094,6 +156480,7 @@
false
false
false
+ 0
17387
@@ -139102,6 +156489,7 @@
false
false
false
+ 0
17388
@@ -139110,6 +156498,7 @@
false
false
false
+ 0
17389
@@ -139118,6 +156507,7 @@
false
false
false
+ 0
17390
@@ -139126,6 +156516,7 @@
false
false
false
+ 0
17391
@@ -139134,6 +156525,7 @@
false
false
false
+ 0
17392
@@ -139142,6 +156534,7 @@
false
false
false
+ 0
17393
@@ -139150,6 +156543,7 @@
false
false
false
+ 0
17394
@@ -139158,6 +156552,7 @@
false
false
false
+ 0
17395
@@ -139166,6 +156561,7 @@
false
false
false
+ 0
17396
@@ -139174,6 +156570,7 @@
false
false
false
+ 0
17397
@@ -139182,6 +156579,7 @@
false
false
false
+ 0
17398
@@ -139190,6 +156588,7 @@
false
false
false
+ 0
17399
@@ -139198,6 +156597,7 @@
false
false
false
+ 0
17400
@@ -139206,6 +156606,7 @@
false
false
false
+ 0
17401
@@ -139214,6 +156615,7 @@
false
false
false
+ 0
17402
@@ -139222,6 +156624,7 @@
false
false
false
+ 0
17403
@@ -139230,6 +156633,7 @@
false
false
false
+ 0
17404
@@ -139238,6 +156642,7 @@
false
false
false
+ 0
17405
@@ -139246,6 +156651,7 @@
false
false
false
+ 0
17406
@@ -139254,6 +156660,7 @@
false
false
false
+ 0
17407
@@ -139262,6 +156669,7 @@
false
false
false
+ 0
17408
@@ -139270,6 +156678,7 @@
false
false
false
+ 0
17409
@@ -139278,6 +156687,7 @@
false
false
false
+ 0
17410
@@ -139286,6 +156696,7 @@
false
false
false
+ 0
17411
@@ -139294,6 +156705,7 @@
false
false
false
+ 0
17412
@@ -139302,6 +156714,7 @@
false
false
false
+ 0
17413
@@ -139310,6 +156723,7 @@
false
false
false
+ 0
17414
@@ -139318,6 +156732,7 @@
false
false
false
+ 0
17415
@@ -139326,6 +156741,7 @@
false
false
false
+ 0
17416
@@ -139334,6 +156750,7 @@
false
false
false
+ 0
17417
@@ -139342,6 +156759,7 @@
false
false
false
+ 0
17418
@@ -139350,6 +156768,7 @@
false
false
false
+ 0
17419
@@ -139358,6 +156777,7 @@
false
false
false
+ 0
17420
@@ -139366,6 +156786,7 @@
false
false
false
+ 0
17421
@@ -139374,6 +156795,7 @@
false
false
false
+ 0
17422
@@ -139382,6 +156804,7 @@
false
false
false
+ 0
17423
@@ -139390,6 +156813,7 @@
false
false
false
+ 0
17424
@@ -139398,6 +156822,7 @@
false
false
false
+ 0
17425
@@ -139406,6 +156831,7 @@
false
false
false
+ 0
17426
@@ -139414,6 +156840,7 @@
false
false
false
+ 0
17427
@@ -139422,6 +156849,7 @@
false
false
false
+ 0
17428
@@ -139430,6 +156858,7 @@
false
false
false
+ 0
17429
@@ -139438,6 +156867,7 @@
false
false
false
+ 0
17430
@@ -139446,6 +156876,7 @@
false
false
false
+ 0
17431
@@ -139454,6 +156885,7 @@
false
false
false
+ 0
17432
@@ -139462,6 +156894,7 @@
false
false
false
+ 0
17433
@@ -139470,6 +156903,7 @@
false
false
false
+ 0
17434
@@ -139478,6 +156912,7 @@
false
false
false
+ 0
17435
@@ -139486,6 +156921,7 @@
false
false
false
+ 0
17436
@@ -139494,6 +156930,7 @@
false
false
false
+ 0
17437
@@ -139502,6 +156939,7 @@
false
false
false
+ 0
17438
@@ -139510,6 +156948,7 @@
false
false
false
+ 0
17439
@@ -139518,6 +156957,7 @@
false
false
false
+ 0
17440
@@ -139526,6 +156966,7 @@
false
false
false
+ 0
17441
@@ -139534,6 +156975,7 @@
false
false
false
+ 0
17442
@@ -139542,6 +156984,7 @@
false
false
false
+ 0
17443
@@ -139550,6 +156993,7 @@
false
false
false
+ 0
17444
@@ -139558,6 +157002,7 @@
false
false
false
+ 0
17445
@@ -139566,6 +157011,7 @@
false
false
false
+ 0
17446
@@ -139574,6 +157020,7 @@
false
false
false
+ 0
17447
@@ -139582,6 +157029,7 @@
false
false
false
+ 0
17448
@@ -139590,6 +157038,7 @@
false
false
false
+ 0
17449
@@ -139598,6 +157047,7 @@
false
false
false
+ 0
17450
@@ -139606,6 +157056,7 @@
false
false
false
+ 0
17451
@@ -139614,6 +157065,7 @@
false
false
false
+ 0
17452
@@ -139622,6 +157074,7 @@
false
false
false
+ 0
17453
@@ -139630,6 +157083,7 @@
false
false
false
+ 0
17454
@@ -139638,6 +157092,7 @@
false
false
false
+ 0
17455
@@ -139646,6 +157101,7 @@
false
false
false
+ 0
17456
@@ -139654,6 +157110,7 @@
false
false
false
+ 0
17457
@@ -139662,6 +157119,7 @@
false
false
false
+ 0
17458
@@ -139670,6 +157128,7 @@
false
false
false
+ 0
17459
@@ -139678,6 +157137,7 @@
false
false
false
+ 0
17460
@@ -139686,6 +157146,7 @@
false
false
false
+ 0
17461
@@ -139694,6 +157155,7 @@
false
false
false
+ 0
17462
@@ -139702,6 +157164,7 @@
false
false
false
+ 0
17463
@@ -139710,6 +157173,7 @@
false
false
false
+ 0
17464
@@ -139718,6 +157182,7 @@
false
false
false
+ 0
17465
@@ -139726,6 +157191,7 @@
false
false
false
+ 0
17466
@@ -139734,6 +157200,7 @@
false
false
false
+ 0
17467
@@ -139742,6 +157209,7 @@
false
false
false
+ 0
17468
@@ -139750,6 +157218,7 @@
false
false
false
+ 0
17469
@@ -139758,6 +157227,7 @@
false
false
false
+ 0
17470
@@ -139766,6 +157236,7 @@
false
false
false
+ 0
17471
@@ -139774,6 +157245,7 @@
false
false
false
+ 0
17472
@@ -139782,6 +157254,7 @@
false
false
false
+ 0
17473
@@ -139790,6 +157263,7 @@
false
false
false
+ 0
17474
@@ -139798,6 +157272,7 @@
false
false
false
+ 0
17475
@@ -139806,6 +157281,7 @@
false
false
false
+ 0
17476
@@ -139814,6 +157290,7 @@
false
false
false
+ 0
17477
@@ -139822,6 +157299,7 @@
false
false
false
+ 0
17478
@@ -139830,6 +157308,7 @@
false
false
false
+ 0
17479
@@ -139838,6 +157317,7 @@
false
false
false
+ 0
17480
@@ -139846,6 +157326,7 @@
false
false
false
+ 0
17481
@@ -139854,6 +157335,7 @@
false
false
false
+ 0
17482
@@ -139862,6 +157344,7 @@
false
false
false
+ 0
17483
@@ -139870,6 +157353,7 @@
false
false
false
+ 0
17484
@@ -139878,6 +157362,7 @@
false
false
false
+ 0
17485
@@ -139886,6 +157371,7 @@
false
false
false
+ 0
17486
@@ -139894,6 +157380,7 @@
false
false
false
+ 0
17487
@@ -139902,6 +157389,7 @@
false
false
false
+ 0
17488
@@ -139910,6 +157398,7 @@
false
false
false
+ 0
17489
@@ -139918,6 +157407,7 @@
false
false
false
+ 0
17490
@@ -139926,6 +157416,7 @@
false
false
false
+ 0
17491
@@ -139934,6 +157425,7 @@
false
false
false
+ 0
17492
@@ -139942,6 +157434,7 @@
false
false
false
+ 0
17493
@@ -139950,6 +157443,7 @@
false
false
false
+ 0
17494
@@ -139958,6 +157452,7 @@
false
false
false
+ 0
17495
@@ -139966,6 +157461,7 @@
false
false
false
+ 0
17496
@@ -139974,6 +157470,7 @@
false
false
false
+ 0
17497
@@ -139982,6 +157479,7 @@
false
false
false
+ 0
17498
@@ -139990,6 +157488,7 @@
false
false
false
+ 0
17499
@@ -139998,6 +157497,7 @@
false
false
false
+ 0
17500
@@ -140006,6 +157506,7 @@
false
false
false
+ 0
17501
@@ -140014,6 +157515,7 @@
false
false
false
+ 0
17502
@@ -140022,6 +157524,7 @@
false
false
false
+ 0
17503
@@ -140030,6 +157533,7 @@
false
false
false
+ 0
17504
@@ -140038,6 +157542,7 @@
false
false
false
+ 0
17505
@@ -140046,6 +157551,7 @@
false
false
false
+ 0
17506
@@ -140054,6 +157560,7 @@
false
false
false
+ 0
17507
@@ -140062,6 +157569,7 @@
false
false
false
+ 0
17508
@@ -140070,6 +157578,7 @@
false
false
false
+ 0
17509
@@ -140078,6 +157587,7 @@
false
false
false
+ 0
17510
@@ -140086,6 +157596,7 @@
false
false
false
+ 0
17511
@@ -140094,6 +157605,7 @@
false
false
false
+ 0
17512
@@ -140102,6 +157614,7 @@
false
false
false
+ 0
17513
@@ -140110,6 +157623,7 @@
false
false
false
+ 0
17514
@@ -140118,6 +157632,7 @@
false
false
false
+ 0
17515
@@ -140126,6 +157641,7 @@
false
false
false
+ 0
17516
@@ -140134,6 +157650,7 @@
false
false
false
+ 0
17517
@@ -140142,6 +157659,7 @@
false
false
false
+ 0
17518
@@ -140150,6 +157668,7 @@
false
false
false
+ 0
17519
@@ -140158,6 +157677,7 @@
false
false
false
+ 0
17520
@@ -140166,6 +157686,7 @@
false
false
false
+ 0
17521
@@ -140174,6 +157695,7 @@
false
false
false
+ 0
17522
@@ -140182,6 +157704,7 @@
false
false
false
+ 0
17523
@@ -140190,6 +157713,7 @@
false
false
false
+ 0
17524
@@ -140198,6 +157722,7 @@
false
false
false
+ 0
17525
@@ -140206,6 +157731,7 @@
false
false
false
+ 0
17526
@@ -140214,6 +157740,7 @@
false
false
false
+ 0
17527
@@ -140222,6 +157749,7 @@
false
false
false
+ 0
17528
@@ -140230,6 +157758,7 @@
false
false
false
+ 0
17529
@@ -140238,6 +157767,7 @@
false
false
false
+ 0
17530
@@ -140246,6 +157776,7 @@
false
false
false
+ 0
17531
@@ -140254,6 +157785,7 @@
false
false
false
+ 0
17532
@@ -140262,6 +157794,7 @@
false
false
false
+ 0
17533
@@ -140270,6 +157803,7 @@
false
false
false
+ 0
17534
@@ -140278,6 +157812,7 @@
false
false
false
+ 0
17535
@@ -140286,6 +157821,7 @@
false
false
false
+ 0
17536
@@ -140294,6 +157830,7 @@
false
false
false
+ 0
17537
@@ -140302,6 +157839,7 @@
false
false
false
+ 0
17538
@@ -140310,6 +157848,7 @@
false
false
false
+ 0
17539
@@ -140318,6 +157857,7 @@
false
false
false
+ 0
17540
@@ -140326,6 +157866,7 @@
false
false
false
+ 0
17541
@@ -140334,6 +157875,7 @@
false
false
false
+ 0
17542
@@ -140342,6 +157884,7 @@
false
false
false
+ 0
17543
@@ -140350,6 +157893,7 @@
false
false
false
+ 0
17544
@@ -140358,6 +157902,7 @@
false
false
false
+ 0
17545
@@ -140366,6 +157911,7 @@
false
false
false
+ 0
17546
@@ -140374,6 +157920,7 @@
false
false
false
+ 0
17547
@@ -140382,6 +157929,7 @@
false
false
false
+ 0
17548
@@ -140390,6 +157938,7 @@
false
false
false
+ 0
17549
@@ -140398,6 +157947,7 @@
false
false
false
+ 0
17550
@@ -140406,6 +157956,7 @@
false
false
false
+ 0
17551
@@ -140414,6 +157965,7 @@
false
false
false
+ 0
17552
@@ -140422,6 +157974,7 @@
false
false
false
+ 0
17553
@@ -140430,6 +157983,7 @@
false
false
false
+ 0
17554
@@ -140438,6 +157992,7 @@
false
false
false
+ 0
17555
@@ -140446,6 +158001,7 @@
false
false
false
+ 0
17556
@@ -140454,6 +158010,7 @@
false
false
false
+ 0
17557
@@ -140462,6 +158019,7 @@
false
false
false
+ 0
17558
@@ -140470,6 +158028,7 @@
false
false
false
+ 0
17559
@@ -140478,6 +158037,7 @@
false
false
false
+ 0
17560
@@ -140486,6 +158046,7 @@
false
false
false
+ 0
17561
@@ -140494,6 +158055,7 @@
false
false
false
+ 0
17562
@@ -140502,6 +158064,7 @@
false
false
false
+ 0
17563
@@ -140510,6 +158073,7 @@
false
false
false
+ 0
17564
@@ -140518,6 +158082,7 @@
false
false
false
+ 0
17565
@@ -140526,6 +158091,7 @@
false
false
false
+ 0
17566
@@ -140534,6 +158100,7 @@
false
false
false
+ 0
17567
@@ -140542,6 +158109,7 @@
false
false
false
+ 0
17568
@@ -140550,6 +158118,7 @@
false
false
false
+ 0
17569
@@ -140558,6 +158127,7 @@
false
false
false
+ 0
17570
@@ -140566,6 +158136,7 @@
false
false
false
+ 0
17571
@@ -140574,6 +158145,7 @@
false
false
false
+ 0
17572
@@ -140582,6 +158154,7 @@
false
false
false
+ 0
17573
@@ -140590,6 +158163,7 @@
false
false
false
+ 0
17574
@@ -140598,6 +158172,7 @@
false
false
false
+ 0
17575
@@ -140606,6 +158181,7 @@
false
false
false
+ 0
17576
@@ -140614,6 +158190,7 @@
false
false
false
+ 0
17577
@@ -140622,6 +158199,7 @@
false
false
false
+ 0
17578
@@ -140630,6 +158208,7 @@
false
false
false
+ 0
17579
@@ -140638,6 +158217,7 @@
false
false
false
+ 0
17580
@@ -140646,6 +158226,7 @@
false
false
false
+ 0
17581
@@ -140654,6 +158235,7 @@
false
false
false
+ 0
17582
@@ -140662,6 +158244,7 @@
false
false
false
+ 0
17583
@@ -140670,6 +158253,7 @@
false
false
false
+ 0
17584
@@ -140678,6 +158262,7 @@
false
false
false
+ 0
17585
@@ -140686,6 +158271,7 @@
false
false
false
+ 0
17586
@@ -140694,6 +158280,7 @@
false
false
false
+ 0
17587
@@ -140702,6 +158289,7 @@
false
false
false
+ 0
17588
@@ -140710,6 +158298,7 @@
false
false
false
+ 0
17589
@@ -140718,6 +158307,7 @@
false
false
false
+ 0
17590
@@ -140726,6 +158316,7 @@
false
false
false
+ 0
17591
@@ -140734,6 +158325,7 @@
false
false
false
+ 0
17592
@@ -140742,6 +158334,7 @@
false
false
false
+ 0
17593
@@ -140750,6 +158343,7 @@
false
false
false
+ 0
17594
@@ -140758,6 +158352,7 @@
false
false
false
+ 0
17595
@@ -140766,6 +158361,7 @@
false
false
false
+ 0
17596
@@ -140774,6 +158370,7 @@
false
false
false
+ 0
17597
@@ -140782,6 +158379,7 @@
false
false
false
+ 0
17598
@@ -140790,6 +158388,7 @@
false
false
false
+ 0
17599
@@ -140798,6 +158397,7 @@
false
false
false
+ 0
17600
@@ -140806,6 +158406,7 @@
false
false
false
+ 0
17601
@@ -140814,6 +158415,7 @@
false
false
false
+ 0
17602
@@ -140822,6 +158424,7 @@
false
false
false
+ 0
17603
@@ -140830,6 +158433,7 @@
false
false
false
+ 0
17604
@@ -140838,6 +158442,7 @@
false
false
false
+ 0
17605
@@ -140846,6 +158451,7 @@
false
false
false
+ 0
17606
@@ -140854,6 +158460,7 @@
false
false
false
+ 0
17607
@@ -140862,6 +158469,7 @@
false
false
false
+ 0
17608
@@ -140870,6 +158478,7 @@
false
false
false
+ 0
17609
@@ -140878,6 +158487,7 @@
false
false
false
+ 0
17610
@@ -140886,6 +158496,7 @@
false
false
false
+ 0
17611
@@ -140894,6 +158505,7 @@
false
false
false
+ 0
17612
@@ -140902,6 +158514,7 @@
false
false
false
+ 0
17613
@@ -140910,6 +158523,7 @@
false
false
false
+ 0
17614
@@ -140918,6 +158532,7 @@
false
false
false
+ 0
17615
@@ -140926,6 +158541,7 @@
false
false
false
+ 0
17616
@@ -140934,6 +158550,7 @@
false
false
false
+ 0
17617
@@ -140942,6 +158559,7 @@
false
false
false
+ 0
17618
@@ -140950,6 +158568,7 @@
false
false
false
+ 0
17619
@@ -140958,6 +158577,7 @@
false
false
false
+ 0
17620
@@ -140966,6 +158586,7 @@
false
false
false
+ 0
17621
@@ -140974,6 +158595,7 @@
false
false
false
+ 0
17622
@@ -140982,6 +158604,7 @@
false
false
false
+ 0
17623
@@ -140990,6 +158613,7 @@
false
false
false
+ 0
17624
@@ -140998,6 +158622,7 @@
false
false
false
+ 0
17625
@@ -141006,6 +158631,7 @@
false
false
false
+ 0
17626
@@ -141014,6 +158640,7 @@
false
false
false
+ 0
17627
@@ -141022,6 +158649,7 @@
false
false
false
+ 0
17628
@@ -141030,6 +158658,7 @@
false
false
false
+ 0
17629
@@ -141038,6 +158667,7 @@
false
false
false
+ 0
17630
@@ -141046,6 +158676,7 @@
false
false
false
+ 0
17631
@@ -141054,6 +158685,7 @@
false
false
false
+ 0
17632
@@ -141062,6 +158694,7 @@
false
false
false
+ 0
17633
@@ -141070,6 +158703,7 @@
false
false
false
+ 0
17634
@@ -141078,6 +158712,7 @@
false
false
false
+ 0
17635
@@ -141086,6 +158721,7 @@
false
false
false
+ 0
17636
@@ -141094,6 +158730,7 @@
false
false
false
+ 0
17637
@@ -141102,6 +158739,7 @@
false
false
false
+ 0
17638
@@ -141110,6 +158748,7 @@
false
false
false
+ 0
17639
@@ -141118,6 +158757,7 @@
false
false
false
+ 0
17640
@@ -141126,6 +158766,7 @@
false
false
false
+ 0
17641
@@ -141134,6 +158775,7 @@
false
false
false
+ 0
17642
@@ -141142,6 +158784,7 @@
false
false
false
+ 0
17643
@@ -141150,6 +158793,7 @@
false
false
false
+ 0
17644
@@ -141158,6 +158802,7 @@
false
false
false
+ 0
17645
@@ -141166,6 +158811,7 @@
false
false
false
+ 0
17646
@@ -141174,6 +158820,7 @@
false
false
false
+ 0
17647
@@ -141182,6 +158829,7 @@
false
false
false
+ 0
17648
@@ -141190,6 +158838,7 @@
false
false
false
+ 0
17649
@@ -141198,6 +158847,7 @@
false
false
false
+ 0
17650
@@ -141206,6 +158856,7 @@
false
false
false
+ 0
17651
@@ -141214,6 +158865,7 @@
false
false
false
+ 0
17652
@@ -141222,6 +158874,7 @@
false
false
false
+ 0
17653
@@ -141230,6 +158883,7 @@
false
false
false
+ 0
17654
@@ -141238,6 +158892,7 @@
false
false
false
+ 0
17655
@@ -141246,6 +158901,7 @@
false
false
false
+ 0
17656
@@ -141254,6 +158910,7 @@
false
false
false
+ 0
17657
@@ -141262,6 +158919,7 @@
false
false
false
+ 0
17658
@@ -141270,6 +158928,7 @@
false
false
false
+ 0
17659
@@ -141278,6 +158937,7 @@
false
false
false
+ 0
17660
@@ -141286,6 +158946,7 @@
false
false
false
+ 0
17661
@@ -141294,6 +158955,7 @@
false
false
false
+ 0
17662
@@ -141302,6 +158964,7 @@
false
false
false
+ 0
17663
@@ -141310,6 +158973,7 @@
false
false
false
+ 0
17664
@@ -141318,6 +158982,7 @@
false
false
false
+ 0
17665
@@ -141326,6 +158991,7 @@
false
false
false
+ 0
17666
@@ -141334,6 +159000,7 @@
false
false
false
+ 0
17667
@@ -141342,6 +159009,7 @@
false
false
false
+ 0
17668
@@ -141350,6 +159018,7 @@
false
false
false
+ 0
17669
@@ -141358,6 +159027,7 @@
false
false
false
+ 0
17670
@@ -141366,6 +159036,7 @@
false
false
false
+ 0
17671
@@ -141374,6 +159045,7 @@
false
false
false
+ 0
17672
@@ -141382,6 +159054,7 @@
false
false
false
+ 0
17673
@@ -141390,6 +159063,7 @@
false
false
false
+ 0
17674
@@ -141398,6 +159072,7 @@
false
false
false
+ 0
17675
@@ -141406,6 +159081,7 @@
false
false
false
+ 0
17676
@@ -141414,6 +159090,7 @@
false
false
false
+ 0
17677
@@ -141422,6 +159099,7 @@
false
false
false
+ 0
17678
@@ -141430,6 +159108,7 @@
false
false
false
+ 0
17679
@@ -141438,6 +159117,7 @@
false
false
false
+ 0
17680
@@ -141446,6 +159126,7 @@
false
false
false
+ 0
17681
@@ -141454,6 +159135,7 @@
false
false
false
+ 0
17682
@@ -141462,6 +159144,7 @@
false
false
false
+ 0
17683
@@ -141470,6 +159153,7 @@
false
false
false
+ 0
17684
@@ -141478,6 +159162,7 @@
false
false
false
+ 0
17685
@@ -141486,6 +159171,7 @@
false
false
false
+ 0
17686
@@ -141494,6 +159180,7 @@
false
false
false
+ 0
17687
@@ -141502,6 +159189,7 @@
false
false
false
+ 0
17688
@@ -141510,6 +159198,7 @@
false
false
false
+ 0
17689
@@ -141518,6 +159207,7 @@
false
false
false
+ 0
17690
@@ -141526,6 +159216,7 @@
false
false
false
+ 0
17691
@@ -141534,6 +159225,7 @@
false
false
false
+ 0
17692
@@ -141542,6 +159234,7 @@
false
false
false
+ 0
17693
@@ -141550,6 +159243,7 @@
false
false
false
+ 0
17694
@@ -141558,6 +159252,7 @@
false
false
false
+ 0
17695
@@ -141566,6 +159261,7 @@
false
false
false
+ 0
17696
@@ -141574,6 +159270,7 @@
false
false
false
+ 0
17697
@@ -141582,6 +159279,7 @@
false
false
false
+ 0
17698
@@ -141590,6 +159288,7 @@
false
false
false
+ 0
17699
@@ -141598,6 +159297,7 @@
false
false
false
+ 0
17700
@@ -141606,6 +159306,7 @@
false
false
false
+ 0
17701
@@ -141614,6 +159315,7 @@
false
false
false
+ 0
17702
@@ -141622,6 +159324,7 @@
false
false
false
+ 0
17703
@@ -141630,6 +159333,7 @@
false
false
false
+ 0
17704
@@ -141638,6 +159342,7 @@
false
false
false
+ 0
17705
@@ -141646,6 +159351,7 @@
false
false
false
+ 0
17706
@@ -141654,6 +159360,7 @@
false
false
false
+ 0
17707
@@ -141662,6 +159369,7 @@
false
false
false
+ 0
17708
@@ -141670,6 +159378,7 @@
false
false
false
+ 0
17709
@@ -141678,6 +159387,7 @@
false
false
false
+ 0
17710
@@ -141686,6 +159396,7 @@
false
false
false
+ 0
17711
@@ -141694,6 +159405,7 @@
false
false
false
+ 0
17712
@@ -141702,6 +159414,7 @@
false
false
false
+ 0
17713
@@ -141710,6 +159423,7 @@
false
false
false
+ 0
17714
@@ -141718,6 +159432,7 @@
false
false
false
+ 0
17715
@@ -141726,6 +159441,7 @@
false
false
false
+ 0
17716
@@ -141734,6 +159450,7 @@
false
false
false
+ 0
17717
@@ -141742,6 +159459,7 @@
false
false
false
+ 0
17718
@@ -141750,6 +159468,7 @@
false
false
false
+ 0
17719
@@ -141758,6 +159477,7 @@
false
false
false
+ 0
17720
@@ -141766,6 +159486,7 @@
false
false
false
+ 0
17721
@@ -141774,6 +159495,7 @@
false
false
false
+ 0
17722
@@ -141782,6 +159504,7 @@
false
false
false
+ 0
17723
@@ -141790,6 +159513,7 @@
false
false
false
+ 0
17724
@@ -141798,6 +159522,7 @@
false
false
false
+ 0
17725
@@ -141806,6 +159531,7 @@
false
false
false
+ 0
17726
@@ -141814,6 +159540,7 @@
false
false
false
+ 0
17727
@@ -141822,6 +159549,7 @@
false
false
false
+ 0
17728
@@ -141830,6 +159558,7 @@
false
false
false
+ 0
17729
@@ -141838,6 +159567,7 @@
false
false
false
+ 0
17730
@@ -141846,6 +159576,7 @@
false
false
false
+ 0
17731
@@ -141854,6 +159585,7 @@
false
false
false
+ 0
17732
@@ -141862,6 +159594,7 @@
false
false
false
+ 0
17733
@@ -141870,6 +159603,7 @@
false
false
false
+ 0
17734
@@ -141878,6 +159612,7 @@
false
false
false
+ 0
17735
@@ -141886,6 +159621,7 @@
false
false
false
+ 0
17736
@@ -141894,6 +159630,7 @@
false
false
false
+ 0
17737
@@ -141902,6 +159639,7 @@
false
false
false
+ 0
17738
@@ -141910,6 +159648,7 @@
false
false
false
+ 0
17739
@@ -141918,6 +159657,7 @@
false
false
false
+ 0
17740
@@ -141926,6 +159666,7 @@
false
false
false
+ 0
17741
@@ -141934,6 +159675,7 @@
false
false
false
+ 0
17742
@@ -141942,6 +159684,7 @@
false
false
false
+ 0
17743
@@ -141950,6 +159693,7 @@
false
false
false
+ 0
17744
@@ -141958,6 +159702,7 @@
false
false
false
+ 0
17745
@@ -141966,6 +159711,7 @@
false
false
false
+ 0
17746
@@ -141974,6 +159720,7 @@
false
false
false
+ 0
17747
@@ -141982,6 +159729,7 @@
false
false
false
+ 0
17748
@@ -141990,6 +159738,7 @@
false
false
false
+ 0
17749
@@ -141998,6 +159747,7 @@
false
false
false
+ 0
17750
@@ -142006,6 +159756,7 @@
false
false
false
+ 0
17751
@@ -142014,6 +159765,7 @@
false
false
false
+ 0
17752
@@ -142022,6 +159774,7 @@
false
false
false
+ 0
17753
@@ -142030,6 +159783,7 @@
false
false
false
+ 0
17754
@@ -142038,6 +159792,7 @@
false
false
false
+ 0
17755
@@ -142046,6 +159801,7 @@
false
false
false
+ 0
17756
@@ -142054,6 +159810,7 @@
false
false
false
+ 0
17757
@@ -142062,6 +159819,7 @@
false
false
false
+ 0
17758
@@ -142070,6 +159828,7 @@
false
false
false
+ 0
17759
@@ -142078,6 +159837,7 @@
false
false
false
+ 0
17760
@@ -142086,6 +159846,7 @@
false
false
false
+ 0
17761
@@ -142094,6 +159855,7 @@
false
false
false
+ 0
17762
@@ -142102,6 +159864,7 @@
false
false
false
+ 0
17763
@@ -142110,6 +159873,7 @@
false
false
false
+ 0
17764
@@ -142118,6 +159882,7 @@
false
false
false
+ 0
17765
@@ -142126,6 +159891,7 @@
false
false
false
+ 0
17766
@@ -142134,6 +159900,7 @@
false
false
false
+ 0
17767
@@ -142142,6 +159909,7 @@
false
false
false
+ 0
17768
@@ -142150,6 +159918,7 @@
false
false
false
+ 0
17769
@@ -142158,6 +159927,7 @@
false
false
false
+ 0
17770
@@ -142166,6 +159936,7 @@
false
false
false
+ 0
17771
@@ -142174,6 +159945,7 @@
false
false
false
+ 0
17772
@@ -142182,6 +159954,7 @@
false
false
false
+ 0
17773
@@ -142190,6 +159963,7 @@
false
false
false
+ 0
17774
@@ -142198,6 +159972,7 @@
false
false
false
+ 0
17775
@@ -142206,6 +159981,7 @@
false
false
false
+ 0
17776
@@ -142214,6 +159990,7 @@
false
false
false
+ 0
17777
@@ -142222,6 +159999,7 @@
false
false
false
+ 0
17778
@@ -142230,6 +160008,7 @@
false
false
false
+ 0
17779
@@ -142238,6 +160017,7 @@
false
false
false
+ 0
17780
@@ -142246,6 +160026,7 @@
false
false
false
+ 0
17781
@@ -142254,6 +160035,7 @@
false
false
false
+ 0
17782
@@ -142262,6 +160044,7 @@
false
false
false
+ 0
17783
@@ -142270,6 +160053,7 @@
false
false
false
+ 0
17784
@@ -142278,6 +160062,7 @@
false
false
false
+ 0
17785
@@ -142286,6 +160071,7 @@
false
false
false
+ 0
17786
@@ -142294,6 +160080,7 @@
false
false
false
+ 0
17787
@@ -142302,6 +160089,7 @@
false
false
false
+ 0
17788
@@ -142310,6 +160098,7 @@
false
false
false
+ 0
17789
@@ -142318,6 +160107,7 @@
false
false
false
+ 0
17790
@@ -142326,6 +160116,7 @@
false
false
false
+ 0
17791
@@ -142334,6 +160125,7 @@
false
false
false
+ 0
17792
@@ -142342,6 +160134,7 @@
false
false
false
+ 0
17793
@@ -142350,6 +160143,7 @@
false
false
false
+ 0
17794
@@ -142358,6 +160152,7 @@
false
false
false
+ 0
17795
@@ -142366,6 +160161,7 @@
false
false
false
+ 0
17796
@@ -142374,6 +160170,7 @@
false
false
false
+ 0
17797
@@ -142382,6 +160179,7 @@
false
false
false
+ 0
17798
@@ -142390,6 +160188,7 @@
false
false
false
+ 0
17799
@@ -142398,6 +160197,7 @@
false
false
false
+ 0
17800
@@ -142406,6 +160206,7 @@
false
false
false
+ 0
17801
@@ -142414,6 +160215,7 @@
false
false
false
+ 0
17802
@@ -142422,6 +160224,7 @@
false
false
false
+ 0
17803
@@ -142430,6 +160233,7 @@
false
false
false
+ 0
17804
@@ -142438,6 +160242,7 @@
false
false
false
+ 0
17805
@@ -142446,6 +160251,7 @@
false
false
false
+ 0
17806
@@ -142454,6 +160260,7 @@
false
false
false
+ 0
17807
@@ -142462,6 +160269,7 @@
false
false
false
+ 0
17808
@@ -142470,6 +160278,7 @@
false
false
false
+ 0
17809
@@ -142478,6 +160287,7 @@
false
false
false
+ 0
17810
@@ -142486,6 +160296,7 @@
false
false
false
+ 0
17811
@@ -142494,6 +160305,7 @@
false
false
false
+ 0
17812
@@ -142502,6 +160314,7 @@
false
false
false
+ 0
17813
@@ -142510,6 +160323,7 @@
false
false
false
+ 0
17814
@@ -142518,6 +160332,7 @@
false
false
false
+ 0
17815
@@ -142526,6 +160341,7 @@
false
false
false
+ 0
17816
@@ -142534,6 +160350,7 @@
false
false
false
+ 0
17817
@@ -142542,6 +160359,7 @@
false
false
false
+ 0
17818
@@ -142550,6 +160368,7 @@
false
false
false
+ 0
17819
@@ -142558,6 +160377,7 @@
false
false
false
+ 0
17820
@@ -142566,6 +160386,7 @@
false
false
false
+ 0
17821
@@ -142574,6 +160395,7 @@
false
false
false
+ 0
17822
@@ -142582,6 +160404,7 @@
false
false
false
+ 0
17823
@@ -142590,6 +160413,7 @@
false
false
false
+ 0
17824
@@ -142598,6 +160422,7 @@
false
false
false
+ 0
17825
@@ -142606,6 +160431,7 @@
false
false
false
+ 0
17826
@@ -142614,6 +160440,7 @@
false
false
false
+ 0
17827
@@ -142622,6 +160449,7 @@
false
false
false
+ 0
17828
@@ -142630,6 +160458,7 @@
false
false
false
+ 0
17829
@@ -142638,6 +160467,7 @@
false
false
false
+ 0
17830
@@ -142646,6 +160476,7 @@
false
false
false
+ 0
17831
@@ -142654,6 +160485,7 @@
false
false
false
+ 0
17832
@@ -142662,6 +160494,7 @@
false
false
false
+ 0
17833
@@ -142670,6 +160503,7 @@
false
false
false
+ 0
17834
@@ -142678,6 +160512,7 @@
false
false
false
+ 0
17835
@@ -142686,6 +160521,7 @@
false
false
false
+ 0
17836
@@ -142694,6 +160530,7 @@
false
false
false
+ 0
17837
@@ -142702,6 +160539,7 @@
false
false
false
+ 0
17838
@@ -142710,6 +160548,7 @@
false
false
false
+ 0
17839
@@ -142718,6 +160557,7 @@
false
false
false
+ 0
17840
@@ -142726,6 +160566,7 @@
false
false
false
+ 0
17841
@@ -142734,6 +160575,7 @@
false
false
false
+ 0
17842
@@ -142742,6 +160584,7 @@
false
false
false
+ 0
17843
@@ -142750,6 +160593,7 @@
false
false
false
+ 0
17844
@@ -142758,6 +160602,7 @@
false
false
false
+ 0
17845
@@ -142766,6 +160611,7 @@
false
false
false
+ 0
17846
@@ -142774,6 +160620,7 @@
false
false
false
+ 0
17847
@@ -142782,6 +160629,7 @@
false
false
false
+ 0
17848
@@ -142790,6 +160638,7 @@
false
false
false
+ 0
17849
@@ -142798,6 +160647,7 @@
false
false
false
+ 0
17850
@@ -142806,6 +160656,7 @@
false
false
false
+ 0
17851
@@ -142814,6 +160665,7 @@
false
false
false
+ 0
17852
@@ -142822,6 +160674,7 @@
false
false
false
+ 0
17853
@@ -142830,6 +160683,7 @@
false
false
false
+ 0
17854
@@ -142838,6 +160692,7 @@
false
false
false
+ 0
17855
@@ -142846,6 +160701,7 @@
false
false
false
+ 0
17856
@@ -142854,6 +160710,7 @@
false
false
false
+ 0
17857
@@ -142862,6 +160719,7 @@
false
false
false
+ 0
17858
@@ -142870,6 +160728,7 @@
false
false
false
+ 0
17859
@@ -142878,6 +160737,7 @@
false
false
false
+ 0
17860
@@ -142886,6 +160746,7 @@
false
false
false
+ 0
17861
@@ -142894,6 +160755,7 @@
false
false
false
+ 0
17862
@@ -142902,6 +160764,7 @@
false
false
false
+ 0
17863
@@ -142910,6 +160773,7 @@
false
false
false
+ 0
17864
@@ -142918,6 +160782,7 @@
false
false
false
+ 0
17865
@@ -142926,6 +160791,7 @@
false
false
false
+ 0
17866
@@ -142934,6 +160800,7 @@
false
false
false
+ 0
17867
@@ -142942,6 +160809,7 @@
false
false
false
+ 0
17868
@@ -142950,6 +160818,7 @@
false
false
false
+ 0
17869
@@ -142958,6 +160827,7 @@
false
false
false
+ 0
17870
@@ -142966,6 +160836,7 @@
false
false
false
+ 0
17871
@@ -142974,6 +160845,7 @@
false
false
false
+ 0
17872
@@ -142982,6 +160854,7 @@
false
false
false
+ 0
17873
@@ -142990,6 +160863,7 @@
false
false
false
+ 0
17874
@@ -142998,6 +160872,7 @@
false
false
false
+ 0
17875
@@ -143006,6 +160881,7 @@
false
false
false
+ 0
17876
@@ -143014,6 +160890,7 @@
false
false
false
+ 0
17877
@@ -143022,6 +160899,7 @@
false
false
false
+ 0
17878
@@ -143030,6 +160908,7 @@
false
false
false
+ 0
17879
@@ -143038,6 +160917,7 @@
false
false
false
+ 0
17880
@@ -143046,6 +160926,7 @@
false
false
false
+ 0
17881
@@ -143054,6 +160935,7 @@
false
false
false
+ 0
17882
@@ -143062,6 +160944,7 @@
false
false
false
+ 0
17883
@@ -143070,6 +160953,7 @@
false
false
false
+ 0
17884
@@ -143078,6 +160962,7 @@
false
false
false
+ 0
17885
@@ -143086,6 +160971,7 @@
false
false
false
+ 0
17886
@@ -143094,6 +160980,7 @@
false
false
false
+ 0
17887
@@ -143102,6 +160989,7 @@
false
false
false
+ 0
17888
@@ -143110,6 +160998,7 @@
false
false
false
+ 0
17889
@@ -143118,6 +161007,7 @@
false
false
false
+ 0
17890
@@ -143126,6 +161016,7 @@
false
false
false
+ 0
17891
@@ -143134,6 +161025,7 @@
false
false
false
+ 0
17892
@@ -143142,6 +161034,7 @@
false
false
false
+ 0
17893
@@ -143150,6 +161043,7 @@
false
false
false
+ 0
17894
@@ -143158,6 +161052,7 @@
false
false
false
+ 0
17895
@@ -143166,6 +161061,7 @@
false
false
false
+ 0
17896
@@ -143174,6 +161070,7 @@
false
false
false
+ 0
17897
@@ -143182,6 +161079,7 @@
false
false
false
+ 0
17898
@@ -143190,6 +161088,7 @@
false
false
false
+ 0
17899
@@ -143198,6 +161097,7 @@
false
false
false
+ 0
17900
@@ -143206,6 +161106,7 @@
false
false
false
+ 0
17901
@@ -143214,6 +161115,7 @@
false
false
false
+ 0
17902
@@ -143222,6 +161124,7 @@
false
false
false
+ 0
17903
@@ -143230,6 +161133,7 @@
false
false
false
+ 0
17904
@@ -143238,6 +161142,7 @@
false
false
false
+ 0
17905
@@ -143246,6 +161151,7 @@
false
false
false
+ 0
17906
@@ -143254,6 +161160,7 @@
false
false
false
+ 0
17907
@@ -143262,6 +161169,7 @@
false
false
false
+ 0
17908
@@ -143270,6 +161178,7 @@
false
false
false
+ 0
17909
@@ -143278,6 +161187,7 @@
false
false
false
+ 0
17910
@@ -143286,6 +161196,7 @@
false
false
false
+ 0
17911
@@ -143294,6 +161205,7 @@
false
false
false
+ 0
17912
@@ -143302,6 +161214,7 @@
false
false
false
+ 0
17913
@@ -143310,6 +161223,7 @@
false
false
false
+ 0
17914
@@ -143318,6 +161232,7 @@
false
false
false
+ 0
17915
@@ -143326,6 +161241,7 @@
false
false
false
+ 0
17916
@@ -143334,6 +161250,7 @@
false
false
false
+ 0
17917
@@ -143342,6 +161259,7 @@
false
false
false
+ 0
17918
@@ -143350,6 +161268,7 @@
false
false
false
+ 0
17919
@@ -143358,6 +161277,7 @@
false
false
false
+ 0
17920
@@ -143366,6 +161286,7 @@
false
false
false
+ 0
17921
@@ -143374,6 +161295,7 @@
false
false
false
+ 0
17922
@@ -143382,6 +161304,7 @@
false
false
false
+ 0
17923
@@ -143390,6 +161313,7 @@
false
false
false
+ 0
17924
@@ -143398,6 +161322,7 @@
false
false
false
+ 0
17925
@@ -143406,6 +161331,7 @@
false
false
false
+ 0
17926
@@ -143414,6 +161340,7 @@
false
false
false
+ 0
17927
@@ -143422,6 +161349,7 @@
false
false
false
+ 0
17928
@@ -143430,6 +161358,7 @@
false
false
false
+ 0
17929
@@ -143438,6 +161367,7 @@
false
false
false
+ 0
17930
@@ -143446,6 +161376,7 @@
false
false
false
+ 0
17931
@@ -143454,6 +161385,7 @@
false
false
false
+ 0
17932
@@ -143462,6 +161394,7 @@
false
false
false
+ 0
17933
@@ -143470,6 +161403,7 @@
false
false
false
+ 0
17934
@@ -143478,6 +161412,7 @@
false
false
false
+ 0
17935
@@ -143486,6 +161421,7 @@
false
false
false
+ 0
17936
@@ -143494,6 +161430,7 @@
false
false
false
+ 0
17937
@@ -143502,6 +161439,7 @@
false
false
false
+ 0
17938
@@ -143510,6 +161448,7 @@
false
false
false
+ 0
17939
@@ -143518,6 +161457,7 @@
false
false
false
+ 0
17940
@@ -143526,6 +161466,7 @@
false
false
false
+ 0
17941
@@ -143534,6 +161475,7 @@
false
false
false
+ 0
17942
@@ -143542,6 +161484,7 @@
false
false
false
+ 0
17943
@@ -143550,6 +161493,7 @@
false
false
false
+ 0
17944
@@ -143558,6 +161502,7 @@
false
false
false
+ 0
17945
@@ -143566,6 +161511,7 @@
false
false
false
+ 0
17946
@@ -143574,6 +161520,7 @@
false
false
false
+ 0
17947
@@ -143582,6 +161529,7 @@
false
false
false
+ 0
17948
@@ -143590,6 +161538,7 @@
false
false
false
+ 0
17949
@@ -143598,6 +161547,7 @@
false
false
false
+ 0
17950
@@ -143606,6 +161556,7 @@
false
false
false
+ 0
17951
@@ -143614,6 +161565,7 @@
false
false
false
+ 0
17952
@@ -143622,6 +161574,7 @@
false
false
false
+ 0
17953
@@ -143630,6 +161583,7 @@
false
false
false
+ 0
17954
@@ -143638,6 +161592,7 @@
false
false
false
+ 0
17955
@@ -143646,6 +161601,7 @@
false
false
false
+ 0
17956
@@ -143654,6 +161610,7 @@
false
false
false
+ 0
17957
@@ -143662,6 +161619,7 @@
false
false
false
+ 0
17958
@@ -143670,6 +161628,7 @@
false
false
false
+ 0
17959
@@ -143678,6 +161637,7 @@
false
false
false
+ 0
17960
@@ -143686,6 +161646,7 @@
false
false
false
+ 0
17961
@@ -143694,6 +161655,7 @@
false
false
false
+ 0
17962
@@ -143702,6 +161664,7 @@
false
false
false
+ 0
17963
@@ -143710,6 +161673,7 @@
false
false
false
+ 0
17964
@@ -143718,6 +161682,7 @@
false
false
false
+ 0
17965
@@ -143726,6 +161691,7 @@
false
false
false
+ 0
17966
@@ -143734,6 +161700,7 @@
false
false
false
+ 0
17967
@@ -143742,6 +161709,7 @@
false
false
false
+ 0
17968
@@ -143750,6 +161718,7 @@
false
false
false
+ 0
17969
@@ -143758,6 +161727,7 @@
false
false
false
+ 0
17970
@@ -143766,6 +161736,7 @@
false
false
false
+ 0
17971
@@ -143774,6 +161745,7 @@
false
false
false
+ 0
17972
@@ -143782,6 +161754,7 @@
false
false
false
+ 0
17973
@@ -143790,6 +161763,7 @@
false
false
false
+ 0
17974
@@ -143798,6 +161772,7 @@
false
false
false
+ 0
17975
@@ -143806,6 +161781,7 @@
false
false
false
+ 0
17976
@@ -143814,6 +161790,7 @@
false
false
false
+ 0
17977
@@ -143822,6 +161799,7 @@
false
false
false
+ 0
17978
@@ -143830,6 +161808,7 @@
false
false
false
+ 0
17979
@@ -143838,6 +161817,7 @@
false
false
false
+ 0
17980
@@ -143846,6 +161826,7 @@
false
false
false
+ 0
17981
@@ -143854,6 +161835,7 @@
false
false
false
+ 0
17982
@@ -143862,6 +161844,7 @@
false
false
false
+ 0
17983
@@ -143870,6 +161853,7 @@
false
false
false
+ 0
17984
@@ -143878,6 +161862,7 @@
false
false
false
+ 0
17985
@@ -143886,6 +161871,7 @@
false
false
false
+ 0
17986
@@ -143894,6 +161880,7 @@
false
false
false
+ 0
17987
@@ -143902,6 +161889,7 @@
false
false
false
+ 0
17988
@@ -143910,6 +161898,7 @@
false
false
false
+ 0
17989
@@ -143918,6 +161907,7 @@
false
false
false
+ 0
17990
@@ -143926,6 +161916,7 @@
false
false
false
+ 0
17991
@@ -143934,6 +161925,7 @@
false
false
false
+ 0
17992
@@ -143942,6 +161934,7 @@
false
false
false
+ 0
17993
@@ -143950,6 +161943,7 @@
false
false
false
+ 0
17994
@@ -143958,6 +161952,7 @@
false
false
false
+ 0
17995
@@ -143966,6 +161961,7 @@
false
false
false
+ 0
17996
@@ -143974,6 +161970,7 @@
false
false
false
+ 0
17997
@@ -143982,6 +161979,7 @@
false
false
false
+ 0
17998
@@ -143990,6 +161988,7 @@
false
false
false
+ 0
17999
@@ -143998,6 +161997,7 @@
false
false
false
+ 0
18000
@@ -144006,6 +162006,7 @@
false
false
false
+ 0
18001
@@ -144014,6 +162015,7 @@
false
false
false
+ 0
18002
@@ -144022,6 +162024,7 @@
false
false
false
+ 0
18003
@@ -144030,6 +162033,7 @@
false
false
false
+ 0
18004
@@ -144038,6 +162042,7 @@
false
false
false
+ 0
18005
@@ -144046,6 +162051,7 @@
false
false
false
+ 0
18006
@@ -144054,6 +162060,7 @@
false
false
false
+ 0
18007
@@ -144062,6 +162069,7 @@
false
false
false
+ 0
18008
@@ -144070,6 +162078,7 @@
false
false
false
+ 0
18009
@@ -144078,6 +162087,7 @@
false
false
false
+ 0
18010
@@ -144086,6 +162096,7 @@
false
false
false
+ 0
18011
@@ -144094,6 +162105,7 @@
false
false
false
+ 0
18012
@@ -144102,6 +162114,7 @@
false
false
false
+ 0
18013
@@ -144110,6 +162123,7 @@
false
false
false
+ 0
18014
@@ -144118,6 +162132,7 @@
false
false
false
+ 0
18015
@@ -144126,6 +162141,7 @@
false
false
false
+ 0
18016
@@ -144134,6 +162150,7 @@
false
false
false
+ 0
18017
@@ -144142,6 +162159,7 @@
false
false
false
+ 0
18018
@@ -144150,6 +162168,7 @@
false
false
false
+ 0
18019
@@ -144158,6 +162177,7 @@
false
false
false
+ 0
18020
@@ -144166,6 +162186,7 @@
false
false
false
+ 0
18021
@@ -144174,6 +162195,7 @@
false
false
false
+ 0
18022
@@ -144182,6 +162204,7 @@
false
false
false
+ 0
18023
@@ -144190,6 +162213,7 @@
false
false
false
+ 0
18024
@@ -144198,6 +162222,7 @@
false
false
false
+ 0
18025
@@ -144206,6 +162231,7 @@
false
false
false
+ 0
18026
@@ -144214,6 +162240,7 @@
false
false
false
+ 0
18027
@@ -144222,6 +162249,7 @@
false
false
false
+ 0
18028
@@ -144230,6 +162258,7 @@
false
false
false
+ 0
18029
@@ -144238,6 +162267,7 @@
false
false
false
+ 0
18030
@@ -144246,6 +162276,7 @@
false
false
false
+ 0
18031
@@ -144254,6 +162285,7 @@
false
false
false
+ 0
18032
@@ -144262,6 +162294,7 @@
false
false
false
+ 0
18033
@@ -144270,6 +162303,7 @@
false
false
false
+ 0
18034
@@ -144278,6 +162312,7 @@
false
false
false
+ 0
18035
@@ -144286,6 +162321,7 @@
false
false
false
+ 0
18036
@@ -144294,6 +162330,7 @@
false
false
false
+ 0
18037
@@ -144302,6 +162339,7 @@
false
false
false
+ 0
18038
@@ -144310,6 +162348,7 @@
false
false
false
+ 0
18039
@@ -144318,6 +162357,7 @@
false
false
false
+ 0
18040
@@ -144326,6 +162366,7 @@
false
false
false
+ 0
18041
@@ -144334,6 +162375,7 @@
false
false
false
+ 0
18042
@@ -144342,6 +162384,7 @@
false
false
false
+ 0
18043
@@ -144350,6 +162393,7 @@
false
false
false
+ 0
18044
@@ -144358,6 +162402,7 @@
false
false
false
+ 0
18045
@@ -144366,6 +162411,7 @@
false
false
false
+ 0
18046
@@ -144374,6 +162420,7 @@
false
false
false
+ 0
18047
@@ -144382,6 +162429,7 @@
false
false
false
+ 0
18048
@@ -144390,6 +162438,7 @@
false
false
false
+ 0
18049
@@ -144398,6 +162447,7 @@
false
false
false
+ 0
18050
@@ -144406,6 +162456,7 @@
false
false
false
+ 0
18051
@@ -144414,6 +162465,7 @@
false
false
false
+ 0
18052
@@ -144422,6 +162474,7 @@
false
false
false
+ 0
18053
@@ -144430,6 +162483,7 @@
false
false
false
+ 0
18054
@@ -144438,6 +162492,7 @@
false
false
false
+ 0
18055
@@ -144446,6 +162501,7 @@
false
false
false
+ 0
18056
@@ -144454,6 +162510,7 @@
false
false
false
+ 0
18057
@@ -144462,6 +162519,7 @@
false
false
false
+ 0
18058
@@ -144470,6 +162528,7 @@
false
false
false
+ 0
18059
@@ -144478,6 +162537,7 @@
false
false
false
+ 0
18060
@@ -144486,6 +162546,7 @@
false
false
false
+ 0
18061
@@ -144494,6 +162555,7 @@
false
false
false
+ 0
18062
@@ -144502,6 +162564,7 @@
false
false
false
+ 0
18063
@@ -144510,6 +162573,7 @@
false
false
false
+ 0
18064
@@ -144518,6 +162582,7 @@
false
false
false
+ 0
18065
@@ -144526,6 +162591,7 @@
false
false
false
+ 0
18066
@@ -144534,6 +162600,7 @@
false
false
false
+ 0
18067
@@ -144542,6 +162609,7 @@
false
false
false
+ 0
18068
@@ -144550,6 +162618,7 @@
false
false
false
+ 0
18069
@@ -144558,6 +162627,7 @@
false
false
false
+ 0
18070
@@ -144566,6 +162636,7 @@
false
false
false
+ 0
18071
@@ -144574,6 +162645,7 @@
false
false
false
+ 0
18072
@@ -144582,6 +162654,7 @@
false
false
false
+ 0
18073
@@ -144590,6 +162663,7 @@
false
false
false
+ 0
18074
@@ -144598,6 +162672,7 @@
false
false
false
+ 0
18075
@@ -144606,6 +162681,7 @@
false
false
false
+ 0
18076
@@ -144614,6 +162690,7 @@
false
false
false
+ 0
18077
@@ -144622,6 +162699,7 @@
false
false
false
+ 0
18078
@@ -144630,6 +162708,7 @@
false
false
false
+ 0
18079
@@ -144638,6 +162717,7 @@
false
false
false
+ 0
18080
@@ -144646,6 +162726,7 @@
false
false
false
+ 0
18081
@@ -144654,6 +162735,7 @@
false
false
false
+ 0
18082
@@ -144662,6 +162744,7 @@
false
false
false
+ 0
18083
@@ -144670,6 +162753,7 @@
false
false
false
+ 0
18084
@@ -144678,6 +162762,7 @@
false
false
false
+ 0
18085
@@ -144686,6 +162771,7 @@
false
false
false
+ 0
18086
@@ -144694,6 +162780,7 @@
false
false
false
+ 0
18087
@@ -144702,6 +162789,7 @@
false
false
false
+ 0
18088
@@ -144710,6 +162798,7 @@
false
false
false
+ 0
18089
@@ -144718,6 +162807,7 @@
false
false
false
+ 0
18090
@@ -144726,6 +162816,7 @@
false
false
false
+ 0
18091
@@ -144734,6 +162825,7 @@
false
false
false
+ 0
18092
@@ -144742,6 +162834,7 @@
false
false
false
+ 0
18093
@@ -144750,6 +162843,7 @@
false
false
false
+ 0
18094
@@ -144758,6 +162852,7 @@
false
false
false
+ 0
18095
@@ -144766,6 +162861,7 @@
false
false
false
+ 0
18096
@@ -144774,6 +162870,7 @@
false
false
false
+ 0
18097
@@ -144782,6 +162879,7 @@
false
false
false
+ 0
18098
@@ -144790,6 +162888,7 @@
false
false
false
+ 0
18099
@@ -144798,6 +162897,7 @@
false
false
false
+ 0
18100
@@ -144806,6 +162906,7 @@
false
false
false
+ 0
18101
@@ -144814,6 +162915,7 @@
false
false
false
+ 0
18102
@@ -144822,6 +162924,7 @@
false
false
false
+ 0
18103
@@ -144830,6 +162933,7 @@
false
false
false
+ 0
18104
@@ -144838,6 +162942,7 @@
false
false
false
+ 0
18105
@@ -144846,6 +162951,7 @@
false
false
false
+ 0
18106
@@ -144854,6 +162960,7 @@
false
false
false
+ 0
18107
@@ -144862,6 +162969,7 @@
false
false
false
+ 0
18108
@@ -144870,6 +162978,7 @@
false
false
false
+ 0
18109
@@ -144878,6 +162987,7 @@
false
false
false
+ 0
18110
@@ -144886,6 +162996,7 @@
false
false
false
+ 0
18111
@@ -144894,6 +163005,7 @@
false
false
false
+ 0
18112
@@ -144902,6 +163014,7 @@
false
false
false
+ 0
18113
@@ -144910,6 +163023,7 @@
false
false
false
+ 0
18114
@@ -144918,6 +163032,7 @@
false
false
false
+ 0
18115
@@ -144926,6 +163041,7 @@
false
false
false
+ 0
18116
@@ -144934,6 +163050,7 @@
false
false
false
+ 0
18117
@@ -144942,6 +163059,7 @@
false
false
false
+ 0
18118
@@ -144950,6 +163068,7 @@
false
false
false
+ 0
18119
@@ -144958,6 +163077,7 @@
false
false
false
+ 0
18120
@@ -144966,6 +163086,7 @@
false
false
false
+ 0
18121
@@ -144974,6 +163095,7 @@
false
false
false
+ 0
18122
@@ -144982,6 +163104,7 @@
false
false
false
+ 0
18123
@@ -144990,6 +163113,7 @@
false
false
false
+ 0
18124
@@ -144998,6 +163122,7 @@
false
false
false
+ 0
18125
@@ -145006,6 +163131,7 @@
false
false
false
+ 0
18126
@@ -145014,6 +163140,7 @@
false
false
false
+ 0
18127
@@ -145022,6 +163149,7 @@
false
false
false
+ 0
18128
@@ -145030,6 +163158,7 @@
false
false
false
+ 0
18129
@@ -145038,6 +163167,7 @@
false
false
false
+ 0
18130
@@ -145046,6 +163176,7 @@
false
false
false
+ 0
18131
@@ -145054,6 +163185,7 @@
false
false
false
+ 0
18132
@@ -145062,6 +163194,7 @@
false
false
false
+ 0
18133
@@ -145070,6 +163203,7 @@
false
false
false
+ 0
18134
@@ -145078,6 +163212,7 @@
false
false
false
+ 0
18135
@@ -145086,6 +163221,7 @@
false
false
false
+ 0
18136
@@ -145094,6 +163230,7 @@
false
false
false
+ 0
18137
@@ -145102,6 +163239,7 @@
false
false
false
+ 0
18138
@@ -145110,6 +163248,7 @@
false
false
false
+ 0
18139
@@ -145118,6 +163257,7 @@
false
false
false
+ 0
18140
@@ -145126,6 +163266,7 @@
false
false
false
+ 0
18141
@@ -145134,6 +163275,7 @@
false
false
false
+ 0
18142
@@ -145142,6 +163284,7 @@
false
false
false
+ 0
18143
@@ -145150,6 +163293,7 @@
false
false
false
+ 0
18144
@@ -145158,6 +163302,7 @@
false
false
false
+ 0
18145
@@ -145166,6 +163311,7 @@
false
false
false
+ 0
18146
@@ -145174,6 +163320,7 @@
false
false
false
+ 0
18147
@@ -145182,6 +163329,7 @@
false
false
false
+ 0
18148
@@ -145190,6 +163338,7 @@
false
false
false
+ 0
18149
@@ -145198,6 +163347,7 @@
false
false
false
+ 0
18150
@@ -145206,6 +163356,7 @@
false
false
false
+ 0
18151
@@ -145214,6 +163365,7 @@
false
false
false
+ 0
18152
@@ -145222,6 +163374,7 @@
false
false
false
+ 0
18153
@@ -145230,6 +163383,7 @@
false
false
false
+ 0
18154
@@ -145238,6 +163392,7 @@
false
false
false
+ 0
18155
@@ -145246,6 +163401,7 @@
false
false
false
+ 0
18156
@@ -145254,6 +163410,7 @@
false
false
false
+ 0
18157
@@ -145262,6 +163419,7 @@
false
false
false
+ 0
18158
@@ -145270,6 +163428,7 @@
false
false
false
+ 0
18159
@@ -145278,6 +163437,7 @@
false
false
false
+ 0
18160
@@ -145286,6 +163446,7 @@
false
false
false
+ 0
18161
@@ -145294,6 +163455,7 @@
false
false
false
+ 0
18162
@@ -145302,6 +163464,7 @@
false
false
false
+ 0
18163
@@ -145310,6 +163473,7 @@
false
false
false
+ 0
18164
@@ -145318,6 +163482,7 @@
false
false
false
+ 0
18165
@@ -145326,6 +163491,7 @@
false
false
false
+ 0
18166
@@ -145334,6 +163500,7 @@
false
false
false
+ 0
18167
@@ -145342,6 +163509,7 @@
false
false
false
+ 0
18168
@@ -145350,6 +163518,7 @@
false
false
false
+ 0
18169
@@ -145358,6 +163527,7 @@
false
false
false
+ 0
18170
@@ -145366,6 +163536,7 @@
false
false
false
+ 0
18171
@@ -145374,6 +163545,7 @@
false
false
false
+ 0
18172
@@ -145382,6 +163554,7 @@
false
false
false
+ 0
18173
@@ -145390,6 +163563,7 @@
false
false
false
+ 0
18174
@@ -145398,6 +163572,7 @@
false
false
false
+ 0
18175
@@ -145406,6 +163581,7 @@
false
false
false
+ 0
18176
@@ -145414,6 +163590,7 @@
false
false
false
+ 0
18177
@@ -145422,6 +163599,7 @@
false
false
false
+ 0
18178
@@ -145430,6 +163608,7 @@
false
false
false
+ 0
18179
@@ -145438,6 +163617,7 @@
false
false
false
+ 0
18180
@@ -145446,6 +163626,7 @@
false
false
false
+ 0
18181
@@ -145454,6 +163635,7 @@
false
false
false
+ 0
18182
@@ -145462,6 +163644,7 @@
false
false
false
+ 0
18183
@@ -145470,6 +163653,7 @@
false
false
false
+ 0
18184
@@ -145478,6 +163662,7 @@
false
false
false
+ 0
18185
@@ -145486,6 +163671,7 @@
false
false
false
+ 0
18186
@@ -145494,6 +163680,7 @@
false
false
false
+ 0
18187
@@ -145502,6 +163689,7 @@
false
false
false
+ 0
18188
@@ -145510,6 +163698,7 @@
false
false
false
+ 0
18189
@@ -145518,6 +163707,7 @@
false
false
false
+ 0
18190
@@ -145526,6 +163716,7 @@
false
false
false
+ 0
18191
@@ -145534,6 +163725,7 @@
false
false
false
+ 0
18192
@@ -145542,6 +163734,7 @@
false
false
false
+ 0
18193
@@ -145550,6 +163743,7 @@
false
false
false
+ 0
18194
@@ -145558,6 +163752,7 @@
false
false
false
+ 0
18195
@@ -145566,6 +163761,7 @@
false
false
false
+ 0
18196
@@ -145574,6 +163770,7 @@
false
false
false
+ 0
18197
@@ -145582,6 +163779,7 @@
false
false
false
+ 0
18198
@@ -145590,6 +163788,7 @@
false
false
false
+ 0
18199
@@ -145598,6 +163797,7 @@
false
false
false
+ 0
18200
@@ -145606,6 +163806,7 @@
false
false
false
+ 0
18201
@@ -145614,6 +163815,7 @@
false
false
false
+ 0
18202
@@ -145622,6 +163824,7 @@
false
false
false
+ 0
18203
@@ -145630,6 +163833,7 @@
false
false
false
+ 0
18204
@@ -145638,6 +163842,7 @@
false
false
false
+ 0
18205
@@ -145646,6 +163851,7 @@
false
false
false
+ 0
18206
@@ -145654,6 +163860,7 @@
false
false
false
+ 0
18207
@@ -145662,6 +163869,7 @@
false
false
false
+ 0
18208
@@ -145670,6 +163878,7 @@
false
false
false
+ 0
18209
@@ -145678,6 +163887,7 @@
false
false
false
+ 0
18210
@@ -145686,6 +163896,7 @@
false
false
false
+ 0
18211
@@ -145694,6 +163905,7 @@
false
false
false
+ 0
18212
@@ -145702,6 +163914,7 @@
false
false
false
+ 0
18213
@@ -145710,6 +163923,7 @@
false
false
false
+ 0
18214
@@ -145718,6 +163932,7 @@
false
false
false
+ 0
18215
@@ -145726,6 +163941,7 @@
false
false
false
+ 0
18216
@@ -145734,6 +163950,7 @@
false
false
false
+ 0
18217
@@ -145742,6 +163959,7 @@
false
false
false
+ 0
18218
@@ -145750,6 +163968,7 @@
false
false
false
+ 0
18219
@@ -145758,6 +163977,7 @@
false
false
false
+ 0
18220
@@ -145766,6 +163986,7 @@
false
false
false
+ 0
18221
@@ -145774,6 +163995,7 @@
false
false
false
+ 0
18222
@@ -145782,6 +164004,7 @@
false
false
false
+ 0
18223
@@ -145790,6 +164013,7 @@
false
false
false
+ 0
18224
@@ -145798,6 +164022,7 @@
false
false
false
+ 0
18225
@@ -145806,6 +164031,7 @@
false
false
false
+ 0
18226
@@ -145814,6 +164040,7 @@
false
false
false
+ 0
18227
@@ -145822,6 +164049,7 @@
false
false
false
+ 0
18228
@@ -145830,6 +164058,7 @@
false
false
false
+ 0
18229
@@ -145838,6 +164067,7 @@
false
false
false
+ 0
18230
@@ -145846,6 +164076,7 @@
false
false
false
+ 0
18231
@@ -145854,6 +164085,7 @@
false
false
false
+ 0
18232
@@ -145862,6 +164094,7 @@
false
false
false
+ 0
18233
@@ -145870,6 +164103,7 @@
false
false
false
+ 0
18234
@@ -145878,6 +164112,7 @@
false
false
false
+ 0
18235
@@ -145886,6 +164121,7 @@
false
false
false
+ 0
18236
@@ -145894,6 +164130,7 @@
false
false
false
+ 0
18237
@@ -145902,6 +164139,7 @@
false
false
false
+ 0
18238
@@ -145910,6 +164148,7 @@
false
false
false
+ 0
18239
@@ -145918,6 +164157,7 @@
false
false
false
+ 0
18240
@@ -145926,6 +164166,7 @@
false
false
false
+ 0
18241
@@ -145934,6 +164175,7 @@
false
false
false
+ 0
18242
@@ -145942,6 +164184,7 @@
false
false
false
+ 0
18243
@@ -145950,6 +164193,7 @@
false
false
false
+ 0
18244
@@ -145958,6 +164202,7 @@
false
false
false
+ 0
18245
@@ -145966,6 +164211,7 @@
false
false
false
+ 0
18246
@@ -145974,6 +164220,7 @@
false
false
false
+ 0
18247
@@ -145982,6 +164229,7 @@
false
false
false
+ 0
18248
@@ -145990,6 +164238,7 @@
false
false
false
+ 0
18249
@@ -145998,6 +164247,7 @@
false
false
false
+ 0
18250
@@ -146006,6 +164256,7 @@
false
false
false
+ 0
18251
@@ -146014,6 +164265,7 @@
false
false
false
+ 0
18252
@@ -146022,6 +164274,7 @@
false
false
false
+ 0
18253
@@ -146030,6 +164283,7 @@
false
false
false
+ 0
18254
@@ -146038,6 +164292,7 @@
false
false
false
+ 0
18255
@@ -146046,6 +164301,7 @@
false
false
false
+ 0
18256
@@ -146054,6 +164310,7 @@
false
false
false
+ 0
18257
@@ -146062,6 +164319,7 @@
false
false
false
+ 0
18258
@@ -146070,6 +164328,7 @@
false
false
false
+ 0
18259
@@ -146078,6 +164337,7 @@
false
false
false
+ 0
18260
@@ -146086,6 +164346,7 @@
false
false
false
+ 0
18261
@@ -146094,6 +164355,7 @@
false
false
false
+ 0
18262
@@ -146102,6 +164364,7 @@
false
false
false
+ 0
18263
@@ -146110,6 +164373,7 @@
false
false
false
+ 0
18264
@@ -146118,6 +164382,7 @@
false
false
false
+ 0
18265
@@ -146126,6 +164391,7 @@
false
false
false
+ 0
18266
@@ -146134,6 +164400,7 @@
false
false
false
+ 0
18267
@@ -146142,6 +164409,7 @@
false
false
false
+ 0
18268
@@ -146150,6 +164418,7 @@
false
false
false
+ 0
18269
@@ -146158,6 +164427,7 @@
false
false
false
+ 0
18270
@@ -146166,6 +164436,7 @@
false
false
false
+ 0
18271
@@ -146174,6 +164445,7 @@
false
false
false
+ 0
18272
@@ -146182,6 +164454,7 @@
false
false
false
+ 0
18273
@@ -146190,6 +164463,7 @@
false
false
false
+ 0
18274
@@ -146198,6 +164472,7 @@
false
false
false
+ 0
18275
@@ -146206,6 +164481,7 @@
false
false
false
+ 0
18276
@@ -146214,6 +164490,7 @@
false
false
false
+ 0
18277
@@ -146222,6 +164499,7 @@
false
false
false
+ 0
18278
@@ -146230,6 +164508,7 @@
false
false
false
+ 0
18279
@@ -146238,6 +164517,7 @@
false
false
false
+ 0
18280
@@ -146246,6 +164526,7 @@
false
false
false
+ 0
18281
@@ -146254,6 +164535,7 @@
false
false
false
+ 0
18282
@@ -146262,6 +164544,7 @@
false
false
false
+ 0
18283
@@ -146270,6 +164553,7 @@
false
false
false
+ 0
18284
@@ -146278,6 +164562,7 @@
false
false
false
+ 0
18285
@@ -146286,6 +164571,7 @@
false
false
false
+ 0
18286
@@ -146294,6 +164580,7 @@
false
false
false
+ 0
18287
@@ -146302,6 +164589,7 @@
false
false
false
+ 0
18288
@@ -146310,6 +164598,7 @@
false
false
false
+ 0
18289
@@ -146318,6 +164607,7 @@
false
false
false
+ 0
18290
@@ -146326,6 +164616,7 @@
false
false
false
+ 0
18291
@@ -146334,6 +164625,7 @@
false
false
false
+ 0
18292
@@ -146342,6 +164634,7 @@
false
false
false
+ 0
18293
@@ -146350,6 +164643,7 @@
false
false
false
+ 0
18294
@@ -146358,6 +164652,7 @@
false
false
false
+ 0
18295
@@ -146366,6 +164661,7 @@
false
false
false
+ 0
18296
@@ -146374,6 +164670,7 @@
false
false
false
+ 0
18297
@@ -146382,6 +164679,7 @@
false
false
false
+ 0
18298
@@ -146390,6 +164688,7 @@
false
false
false
+ 0
18299
@@ -146398,6 +164697,7 @@
false
false
false
+ 0
18300
@@ -146406,6 +164706,7 @@
false
false
false
+ 0
18301
@@ -146414,6 +164715,7 @@
false
false
false
+ 0
18302
@@ -146422,6 +164724,7 @@
false
false
false
+ 0
18303
@@ -146430,6 +164733,7 @@
false
false
false
+ 0
18304
@@ -146438,6 +164742,7 @@
false
false
false
+ 0
18305
@@ -146446,6 +164751,7 @@
false
false
false
+ 0
18306
@@ -146454,6 +164760,7 @@
false
false
false
+ 0
18307
@@ -146462,6 +164769,7 @@
false
false
false
+ 0
18308
@@ -146470,6 +164778,7 @@
false
false
false
+ 0
18309
@@ -146478,6 +164787,7 @@
false
false
false
+ 0
18310
@@ -146486,6 +164796,7 @@
false
false
false
+ 0
18311
@@ -146494,6 +164805,7 @@
false
false
false
+ 0
18312
@@ -146502,6 +164814,7 @@
false
false
false
+ 0
18313
@@ -146510,6 +164823,7 @@
false
false
false
+ 0
18314
@@ -146518,6 +164832,7 @@
false
false
false
+ 0
18315
@@ -146526,6 +164841,7 @@
false
false
false
+ 0
18316
@@ -146534,6 +164850,7 @@
false
false
false
+ 0
18317
@@ -146542,6 +164859,7 @@
false
false
false
+ 0
18318
@@ -146550,6 +164868,7 @@
false
false
false
+ 0
18319
@@ -146558,6 +164877,7 @@
false
false
false
+ 0
18320
@@ -146566,6 +164886,7 @@
false
false
false
+ 0
18321
@@ -146574,6 +164895,7 @@
false
false
false
+ 0
18322
@@ -146582,6 +164904,7 @@
false
false
false
+ 0
18323
@@ -146590,6 +164913,7 @@
false
false
false
+ 0
18324
@@ -146598,6 +164922,7 @@
false
false
false
+ 0
18325
@@ -146606,6 +164931,7 @@
false
false
false
+ 0
18326
@@ -146614,6 +164940,7 @@
false
false
false
+ 0
18327
@@ -146622,6 +164949,7 @@
false
false
false
+ 0
18328
@@ -146630,6 +164958,7 @@
false
false
false
+ 0
18329
@@ -146638,6 +164967,7 @@
false
false
false
+ 0
18330
@@ -146646,6 +164976,7 @@
false
false
false
+ 0
18331
@@ -146654,6 +164985,7 @@
false
false
false
+ 0
18332
@@ -146662,6 +164994,7 @@
false
false
false
+ 0
18333
@@ -146670,6 +165003,7 @@
false
false
false
+ 0
18334
@@ -146678,6 +165012,7 @@
false
false
false
+ 0
18335
@@ -146686,6 +165021,7 @@
false
false
false
+ 0
18336
@@ -146694,6 +165030,7 @@
false
false
false
+ 0
18337
@@ -146702,6 +165039,7 @@
false
false
false
+ 0
18338
@@ -146710,6 +165048,7 @@
false
false
false
+ 0
18339
@@ -146718,6 +165057,7 @@
false
false
false
+ 0
18340
@@ -146726,6 +165066,7 @@
false
false
false
+ 0
18341
@@ -146734,6 +165075,7 @@
false
false
false
+ 0
18342
@@ -146742,6 +165084,7 @@
false
false
false
+ 0
18343
@@ -146750,6 +165093,7 @@
false
false
false
+ 0
18344
@@ -146758,6 +165102,7 @@
false
false
false
+ 0
18345
@@ -146766,6 +165111,7 @@
false
false
false
+ 0
18346
@@ -146774,6 +165120,7 @@
false
false
false
+ 0
18347
@@ -146782,6 +165129,7 @@
false
false
false
+ 0
18348
@@ -146790,6 +165138,7 @@
false
false
false
+ 0
18349
@@ -146798,6 +165147,7 @@
false
false
false
+ 0
18350
@@ -146806,6 +165156,7 @@
false
false
false
+ 0
18351
@@ -146814,6 +165165,7 @@
false
false
false
+ 0
18352
@@ -146822,6 +165174,7 @@
false
false
false
+ 0
18353
@@ -146830,6 +165183,7 @@
false
false
false
+ 0
18354
@@ -146838,6 +165192,7 @@
false
false
false
+ 0
18355
@@ -146846,6 +165201,7 @@
false
false
false
+ 0
18356
@@ -146854,6 +165210,7 @@
false
false
false
+ 0
18357
@@ -146862,6 +165219,7 @@
false
false
false
+ 0
18358
@@ -146870,6 +165228,7 @@
false
false
false
+ 0
18359
@@ -146878,6 +165237,7 @@
false
false
false
+ 0
18360
@@ -146886,6 +165246,7 @@
false
false
false
+ 0
18361
@@ -146894,6 +165255,7 @@
false
false
false
+ 0
18362
@@ -146902,6 +165264,7 @@
false
false
false
+ 0
18363
@@ -146910,6 +165273,7 @@
false
false
false
+ 0
18364
@@ -146918,6 +165282,7 @@
false
false
false
+ 0
18365
@@ -146926,6 +165291,7 @@
false
false
false
+ 0
18366
@@ -146934,6 +165300,7 @@
false
false
false
+ 0
18367
@@ -146942,6 +165309,7 @@
false
false
false
+ 0
18368
@@ -146950,6 +165318,7 @@
false
false
false
+ 0
18369
@@ -146958,6 +165327,7 @@
false
false
false
+ 0
18370
@@ -146966,6 +165336,7 @@
false
false
false
+ 0
18371
@@ -146974,6 +165345,7 @@
false
false
false
+ 0
18372
@@ -146982,6 +165354,7 @@
false
false
false
+ 0
18373
@@ -146990,6 +165363,7 @@
false
false
false
+ 0
18374
@@ -146998,6 +165372,7 @@
false
false
false
+ 0
18375
@@ -147006,6 +165381,7 @@
false
false
false
+ 0
18376
@@ -147014,6 +165390,7 @@
false
false
false
+ 0
18377
@@ -147022,6 +165399,7 @@
false
false
false
+ 0
18378
@@ -147030,6 +165408,7 @@
false
false
false
+ 0
18379
@@ -147038,6 +165417,7 @@
false
false
false
+ 0
18380
@@ -147046,6 +165426,7 @@
false
false
false
+ 0
18381
@@ -147054,6 +165435,7 @@
false
false
false
+ 0
18382
@@ -147062,6 +165444,7 @@
false
false
false
+ 0
18383
@@ -147070,6 +165453,7 @@
false
false
false
+ 0
18384
@@ -147078,6 +165462,7 @@
false
false
false
+ 0
18385
@@ -147086,6 +165471,7 @@
false
false
false
+ 0
18386
@@ -147094,6 +165480,7 @@
false
false
false
+ 0
18387
@@ -147102,6 +165489,7 @@
false
false
false
+ 0
18388
@@ -147110,6 +165498,7 @@
false
false
false
+ 0
18389
@@ -147118,6 +165507,7 @@
false
false
false
+ 0
18390
@@ -147126,6 +165516,7 @@
false
false
false
+ 0
18391
@@ -147134,6 +165525,7 @@
false
false
false
+ 0
18392
@@ -147142,6 +165534,7 @@
false
false
false
+ 0
18393
@@ -147150,6 +165543,7 @@
false
false
false
+ 0
18394
@@ -147158,6 +165552,7 @@
false
false
false
+ 0
18395
@@ -147166,6 +165561,7 @@
false
false
false
+ 0
18396
@@ -147174,6 +165570,7 @@
false
false
false
+ 0
18397
@@ -147182,6 +165579,7 @@
false
false
false
+ 0
18398
@@ -147190,6 +165588,7 @@
false
false
false
+ 0
18399
@@ -147198,6 +165597,7 @@
false
false
false
+ 0
18400
@@ -147206,6 +165606,7 @@
false
false
false
+ 0
18401
@@ -147214,6 +165615,7 @@
false
false
false
+ 0
18402
@@ -147222,6 +165624,7 @@
false
false
false
+ 0
18403
@@ -147230,6 +165633,7 @@
false
false
false
+ 0
18404
@@ -147238,6 +165642,7 @@
false
false
false
+ 0
18405
@@ -147246,6 +165651,7 @@
false
false
false
+ 0
18406
@@ -147254,6 +165660,7 @@
false
false
false
+ 0
18407
@@ -147262,6 +165669,7 @@
false
false
false
+ 0
18408
@@ -147270,6 +165678,7 @@
false
false
false
+ 0
18409
@@ -147278,6 +165687,7 @@
false
false
false
+ 0
18410
@@ -147286,6 +165696,7 @@
false
false
false
+ 0
18411
@@ -147294,6 +165705,7 @@
false
false
false
+ 0
18412
@@ -147302,6 +165714,7 @@
false
false
false
+ 0
18413
@@ -147310,6 +165723,7 @@
false
false
false
+ 0
18414
@@ -147318,6 +165732,7 @@
false
false
false
+ 0
18415
@@ -147326,6 +165741,7 @@
false
false
false
+ 0
18416
@@ -147334,6 +165750,7 @@
false
false
false
+ 0
18417
@@ -147342,6 +165759,7 @@
false
false
false
+ 0
18418
@@ -147350,6 +165768,7 @@
false
false
false
+ 0
18419
@@ -147358,6 +165777,7 @@
false
false
false
+ 0
18420
@@ -147366,6 +165786,7 @@
false
false
false
+ 0
18421
@@ -147374,6 +165795,7 @@
false
false
false
+ 0
18422
@@ -147382,6 +165804,7 @@
false
false
false
+ 0
18423
@@ -147390,6 +165813,7 @@
false
false
false
+ 0
18424
@@ -147398,6 +165822,7 @@
false
false
false
+ 0
18425
@@ -147406,6 +165831,7 @@
false
false
false
+ 0
18426
@@ -147414,6 +165840,7 @@
false
false
false
+ 0
18427
@@ -147422,6 +165849,7 @@
false
false
false
+ 0
18428
@@ -147430,6 +165858,7 @@
false
false
false
+ 0
18429
@@ -147438,6 +165867,7 @@
false
false
false
+ 0
18430
@@ -147446,6 +165876,7 @@
false
false
false
+ 0
18431
@@ -147454,6 +165885,7 @@
false
false
false
+ 0
18432
@@ -147462,6 +165894,7 @@
false
false
false
+ 0
18433
@@ -147470,6 +165903,7 @@
false
false
false
+ 0
18434
@@ -147478,6 +165912,7 @@
false
false
false
+ 0
18435
@@ -147486,6 +165921,7 @@
false
false
false
+ 0
18436
@@ -147494,6 +165930,7 @@
false
false
false
+ 0
18437
@@ -147502,6 +165939,7 @@
false
false
false
+ 0
18438
@@ -147510,6 +165948,7 @@
false
false
false
+ 0
18439
@@ -147518,6 +165957,7 @@
false
false
false
+ 0
18440
@@ -147526,6 +165966,7 @@
false
false
false
+ 0
18441
@@ -147534,6 +165975,7 @@
false
false
false
+ 0
18442
@@ -147542,6 +165984,7 @@
false
false
false
+ 0
18443
@@ -147550,6 +165993,7 @@
false
false
false
+ 0
18444
@@ -147558,6 +166002,7 @@
false
false
false
+ 0
18445
@@ -147566,6 +166011,7 @@
false
false
false
+ 0
18446
@@ -147574,6 +166020,7 @@
false
false
false
+ 0
18447
@@ -147582,6 +166029,7 @@
false
false
false
+ 0
18448
@@ -147590,6 +166038,7 @@
false
false
false
+ 0
18449
@@ -147598,6 +166047,7 @@
false
false
false
+ 0
18450
@@ -147606,6 +166056,7 @@
false
false
false
+ 0
18451
@@ -147614,6 +166065,7 @@
false
false
false
+ 0
18452
@@ -147622,6 +166074,7 @@
false
false
false
+ 0
18453
@@ -147630,6 +166083,7 @@
false
false
false
+ 0
18454
@@ -147638,6 +166092,7 @@
false
false
false
+ 0
18455
@@ -147646,6 +166101,7 @@
false
false
false
+ 0
18456
@@ -147654,6 +166110,7 @@
false
false
false
+ 0
18457
@@ -147662,6 +166119,7 @@
false
false
false
+ 0
18458
@@ -147670,6 +166128,7 @@
false
false
false
+ 0
18459
@@ -147678,6 +166137,7 @@
false
false
false
+ 0
18460
@@ -147686,6 +166146,7 @@
false
false
false
+ 0
18461
@@ -147694,6 +166155,7 @@
false
false
false
+ 0
18462
@@ -147702,6 +166164,7 @@
false
false
false
+ 0
18463
@@ -147710,6 +166173,7 @@
false
false
false
+ 0
18464
@@ -147718,6 +166182,7 @@
false
false
false
+ 0
18465
@@ -147726,6 +166191,7 @@
false
false
false
+ 0
18466
@@ -147734,6 +166200,7 @@
false
false
false
+ 0
18467
@@ -147742,6 +166209,7 @@
false
false
false
+ 0
18468
@@ -147750,6 +166218,7 @@
false
false
false
+ 0
18469
@@ -147758,6 +166227,7 @@
false
false
false
+ 0
18470
@@ -147766,6 +166236,7 @@
false
false
false
+ 0
18471
@@ -147774,6 +166245,7 @@
false
false
false
+ 0
18472
@@ -147782,6 +166254,7 @@
false
false
false
+ 0
18473
@@ -147790,6 +166263,7 @@
false
false
false
+ 0
18474
@@ -147798,6 +166272,7 @@
false
false
false
+ 0
18475
@@ -147806,6 +166281,7 @@
false
false
false
+ 0
18476
@@ -147814,6 +166290,7 @@
false
false
false
+ 0
18477
@@ -147822,6 +166299,7 @@
false
false
false
+ 0
18478
@@ -147830,6 +166308,7 @@
false
false
false
+ 0
18479
@@ -147838,6 +166317,7 @@
false
false
false
+ 0
18480
@@ -147846,6 +166326,7 @@
false
false
false
+ 0
18481
@@ -147854,6 +166335,7 @@
false
false
false
+ 0
18482
@@ -147862,6 +166344,7 @@
false
false
false
+ 0
18483
@@ -147870,6 +166353,7 @@
false
false
false
+ 0
18484
@@ -147878,6 +166362,7 @@
false
false
false
+ 0
18485
@@ -147886,6 +166371,7 @@
false
false
false
+ 0
18486
@@ -147894,6 +166380,7 @@
false
false
false
+ 0
18487
@@ -147902,6 +166389,7 @@
false
false
false
+ 0
18488
@@ -147910,6 +166398,7 @@
false
false
false
+ 0
18489
@@ -147918,6 +166407,7 @@
false
false
false
+ 0
18490
@@ -147926,6 +166416,7 @@
false
false
false
+ 0
18491
@@ -147934,6 +166425,7 @@
false
false
false
+ 0
18492
@@ -147942,6 +166434,7 @@
false
false
false
+ 0
18493
@@ -147950,6 +166443,7 @@
false
false
false
+ 0
18494
@@ -147958,6 +166452,7 @@
false
false
false
+ 0
18495
@@ -147966,6 +166461,7 @@
false
false
false
+ 0
18496
@@ -147974,6 +166470,7 @@
false
false
false
+ 0
18497
@@ -147982,6 +166479,7 @@
false
false
false
+ 0
18498
@@ -147990,6 +166488,7 @@
false
false
false
+ 0
18499
@@ -147998,6 +166497,7 @@
false
false
false
+ 0
18500
@@ -148006,6 +166506,7 @@
false
false
false
+ 0
18501
@@ -148014,6 +166515,7 @@
false
false
false
+ 0
18502
@@ -148022,6 +166524,7 @@
false
false
false
+ 0
18503
@@ -148030,6 +166533,7 @@
false
false
false
+ 0
18504
@@ -148038,6 +166542,7 @@
false
false
false
+ 0
18505
@@ -148046,6 +166551,7 @@
false
false
false
+ 0
18506
@@ -148054,6 +166560,7 @@
false
false
false
+ 0
18507
@@ -148062,6 +166569,7 @@
false
false
false
+ 0
18508
@@ -148070,6 +166578,7 @@
false
false
false
+ 0
18509
@@ -148078,6 +166587,7 @@
false
false
false
+ 0
18510
@@ -148086,6 +166596,7 @@
false
false
false
+ 0
18511
@@ -148094,6 +166605,7 @@
false
false
false
+ 0
18512
@@ -148102,6 +166614,7 @@
false
false
false
+ 0
18513
@@ -148110,6 +166623,7 @@
false
false
false
+ 0
18514
@@ -148118,6 +166632,7 @@
false
false
false
+ 0
18515
@@ -148126,6 +166641,7 @@
false
false
false
+ 0
18516
@@ -148134,6 +166650,7 @@
false
false
false
+ 0
18517
@@ -148142,6 +166659,7 @@
false
false
false
+ 0
18518
@@ -148150,6 +166668,7 @@
false
false
false
+ 0
18519
@@ -148158,6 +166677,7 @@
false
false
false
+ 0
18520
@@ -148166,6 +166686,7 @@
false
false
false
+ 0
18521
@@ -148174,6 +166695,7 @@
false
false
false
+ 0
18522
@@ -148182,6 +166704,7 @@
false
false
false
+ 0
18523
@@ -148190,6 +166713,7 @@
false
false
false
+ 0
18524
@@ -148198,6 +166722,7 @@
false
false
false
+ 0
18525
@@ -148206,6 +166731,7 @@
false
false
false
+ 0
18526
@@ -148214,6 +166740,7 @@
false
false
false
+ 0
18527
@@ -148222,6 +166749,7 @@
false
false
false
+ 0
18528
@@ -148230,6 +166758,7 @@
false
false
false
+ 0
18529
@@ -148238,6 +166767,7 @@
false
false
false
+ 0
18530
@@ -148246,6 +166776,7 @@
false
false
false
+ 0
18531
@@ -148254,6 +166785,7 @@
false
false
false
+ 0
18532
@@ -148262,6 +166794,7 @@
false
false
false
+ 0
18533
@@ -148270,6 +166803,7 @@
false
false
false
+ 0
18534
@@ -148278,6 +166812,7 @@
false
false
false
+ 0
18535
@@ -148286,6 +166821,7 @@
false
false
false
+ 0
18536
@@ -148294,6 +166830,7 @@
false
false
false
+ 0
18537
@@ -148302,6 +166839,7 @@
false
false
false
+ 0
18538
@@ -148310,6 +166848,7 @@
false
false
false
+ 0
18539
@@ -148318,6 +166857,7 @@
false
false
false
+ 0
18540
@@ -148326,6 +166866,7 @@
false
false
false
+ 0
18541
@@ -148334,6 +166875,7 @@
false
false
false
+ 0
18542
@@ -148342,6 +166884,7 @@
false
false
false
+ 0
18543
@@ -148350,6 +166893,7 @@
false
false
false
+ 0
18544
@@ -148358,6 +166902,7 @@
false
false
false
+ 0
18545
@@ -148366,6 +166911,7 @@
false
false
false
+ 0
18546
@@ -148374,6 +166920,7 @@
false
false
false
+ 0
18547
@@ -148382,6 +166929,7 @@
false
false
false
+ 0
18548
@@ -148390,6 +166938,7 @@
false
false
false
+ 0
18549
@@ -148398,6 +166947,7 @@
false
false
false
+ 0
18550
@@ -148406,6 +166956,7 @@
false
false
false
+ 0
18551
@@ -148414,6 +166965,7 @@
false
false
false
+ 0
18552
@@ -148422,6 +166974,7 @@
false
false
false
+ 0
18553
@@ -148430,6 +166983,7 @@
false
false
false
+ 0
18554
@@ -148438,6 +166992,7 @@
false
false
false
+ 0
18555
@@ -148446,6 +167001,7 @@
false
false
false
+ 0
18556
@@ -148454,6 +167010,7 @@
false
false
false
+ 0
18557
@@ -148462,6 +167019,7 @@
false
false
false
+ 0
18558
@@ -148470,6 +167028,7 @@
false
false
false
+ 0
18559
@@ -148478,6 +167037,7 @@
false
false
false
+ 0
18560
@@ -148486,6 +167046,7 @@
false
false
false
+ 0
18561
@@ -148494,6 +167055,7 @@
false
false
false
+ 0
18562
@@ -148502,6 +167064,7 @@
false
false
false
+ 0
18563
@@ -148510,6 +167073,7 @@
false
false
false
+ 0
18564
@@ -148518,6 +167082,7 @@
false
false
false
+ 0
18565
@@ -148526,6 +167091,7 @@
false
false
false
+ 0
18566
@@ -148534,6 +167100,7 @@
false
false
false
+ 0
18567
@@ -148542,6 +167109,7 @@
false
false
false
+ 0
18568
@@ -148550,6 +167118,7 @@
false
false
false
+ 0
18569
@@ -148558,6 +167127,7 @@
false
false
false
+ 0
18570
@@ -148566,6 +167136,7 @@
false
false
false
+ 0
18571
@@ -148574,6 +167145,7 @@
false
false
false
+ 0
18572
@@ -148582,6 +167154,7 @@
false
false
false
+ 0
18573
@@ -148590,6 +167163,7 @@
false
false
false
+ 0
18574
@@ -148598,6 +167172,7 @@
false
false
false
+ 0
18575
@@ -148606,6 +167181,7 @@
false
false
false
+ 0
18576
@@ -148614,6 +167190,7 @@
false
false
false
+ 0
18577
@@ -148622,6 +167199,7 @@
false
false
false
+ 0
18578
@@ -148630,6 +167208,7 @@
false
false
false
+ 0
18579
@@ -148638,6 +167217,7 @@
false
false
false
+ 0
18580
@@ -148646,6 +167226,7 @@
false
false
false
+ 0
18581
@@ -148654,6 +167235,7 @@
false
false
false
+ 0
18582
@@ -148662,6 +167244,7 @@
false
false
false
+ 0
18583
@@ -148670,6 +167253,7 @@
false
false
false
+ 0
18584
@@ -148678,6 +167262,7 @@
false
false
false
+ 0
18585
@@ -148686,6 +167271,7 @@
false
false
false
+ 0
18586
@@ -148694,6 +167280,7 @@
false
false
false
+ 0
18587
@@ -148702,6 +167289,7 @@
false
false
false
+ 0
18588
@@ -148710,6 +167298,7 @@
false
false
false
+ 0
18589
@@ -148718,6 +167307,7 @@
false
false
false
+ 0
18590
@@ -148726,6 +167316,7 @@
false
false
false
+ 0
18591
@@ -148734,6 +167325,7 @@
false
false
false
+ 0
18592
@@ -148742,6 +167334,7 @@
false
false
false
+ 0
18593
@@ -148750,6 +167343,7 @@
false
false
false
+ 0
18594
@@ -148758,6 +167352,7 @@
false
false
false
+ 0
18595
@@ -148766,6 +167361,7 @@
false
false
false
+ 0
18596
@@ -148774,6 +167370,7 @@
false
false
false
+ 0
18597
@@ -148782,6 +167379,7 @@
false
false
false
+ 0
18598
@@ -148790,6 +167388,7 @@
false
false
false
+ 0
18599
@@ -148798,6 +167397,7 @@
false
false
false
+ 0
18600
@@ -148806,6 +167406,7 @@
false
false
false
+ 0
18601
@@ -148814,6 +167415,7 @@
false
false
false
+ 0
18602
@@ -148822,6 +167424,7 @@
false
false
false
+ 0
18603
@@ -148830,6 +167433,7 @@
false
false
false
+ 0
18604
@@ -148838,6 +167442,7 @@
false
false
false
+ 0
18605
@@ -148846,6 +167451,7 @@
false
false
false
+ 0
18606
@@ -148854,6 +167460,7 @@
false
false
false
+ 0
18607
@@ -148862,6 +167469,7 @@
false
false
false
+ 0
18608
@@ -148870,6 +167478,7 @@
false
false
false
+ 0
18609
@@ -148878,6 +167487,7 @@
false
false
false
+ 0
18610
@@ -148886,6 +167496,7 @@
false
false
false
+ 0
18611
@@ -148894,6 +167505,7 @@
false
false
false
+ 0
18612
@@ -148902,6 +167514,7 @@
false
false
false
+ 0
18613
@@ -148910,6 +167523,7 @@
false
false
false
+ 0
18614
@@ -148918,6 +167532,7 @@
false
false
false
+ 0
18615
@@ -148926,6 +167541,7 @@
false
false
false
+ 0
18616
@@ -148934,6 +167550,7 @@
false
false
false
+ 0
18617
@@ -148942,6 +167559,7 @@
false
false
false
+ 0
18618
@@ -148950,6 +167568,7 @@
false
false
false
+ 0
18619
@@ -148958,6 +167577,7 @@
false
false
false
+ 0
18620
@@ -148966,6 +167586,7 @@
false
false
false
+ 0
18621
@@ -148974,6 +167595,7 @@
false
false
false
+ 0
18622
@@ -148982,6 +167604,7 @@
false
false
false
+ 0
18623
@@ -148990,6 +167613,7 @@
false
false
false
+ 0
18624
@@ -148998,6 +167622,7 @@
false
false
false
+ 0
18625
@@ -149006,6 +167631,7 @@
false
false
false
+ 0
18626
@@ -149014,6 +167640,7 @@
false
false
false
+ 0
18627
@@ -149022,6 +167649,7 @@
false
false
false
+ 0
18628
@@ -149030,6 +167658,7 @@
false
false
false
+ 0
18629
@@ -149038,6 +167667,7 @@
false
false
false
+ 0
18630
@@ -149046,6 +167676,7 @@
false
false
false
+ 0
18631
@@ -149054,6 +167685,7 @@
false
false
false
+ 0
18632
@@ -149062,6 +167694,7 @@
false
false
false
+ 0
18633
@@ -149070,6 +167703,7 @@
false
false
false
+ 0
18634
@@ -149078,6 +167712,7 @@
false
false
false
+ 0
18635
@@ -149086,6 +167721,7 @@
false
false
false
+ 0
18636
@@ -149094,6 +167730,7 @@
false
false
false
+ 0
18637
@@ -149102,6 +167739,7 @@
false
false
false
+ 0
18638
@@ -149110,6 +167748,7 @@
false
false
false
+ 0
18639
@@ -149118,6 +167757,7 @@
false
false
false
+ 0
18640
@@ -149126,6 +167766,7 @@
false
false
false
+ 0
18641
@@ -149134,6 +167775,7 @@
false
false
false
+ 0
18642
@@ -149142,6 +167784,7 @@
false
false
false
+ 0
18643
@@ -149150,6 +167793,7 @@
false
false
false
+ 0
18644
@@ -149158,6 +167802,7 @@
false
false
false
+ 0
18645
@@ -149166,6 +167811,7 @@
false
false
false
+ 0
18646
@@ -149174,6 +167820,7 @@
false
false
false
+ 0
18647
@@ -149182,6 +167829,7 @@
false
false
false
+ 0
18648
@@ -149190,6 +167838,7 @@
false
false
false
+ 0
18649
@@ -149198,6 +167847,7 @@
false
false
false
+ 0
18650
@@ -149206,6 +167856,7 @@
false
false
false
+ 0
18651
@@ -149214,6 +167865,7 @@
false
false
false
+ 0
18652
@@ -149222,6 +167874,7 @@
false
false
false
+ 0
18653
@@ -149230,6 +167883,7 @@
false
false
false
+ 0
18654
@@ -149238,6 +167892,7 @@
false
false
false
+ 0
18655
@@ -149246,6 +167901,7 @@
false
false
false
+ 0
18656
@@ -149254,6 +167910,7 @@
false
false
false
+ 0
18657
@@ -149262,6 +167919,7 @@
false
false
false
+ 0
18658
@@ -149270,6 +167928,7 @@
false
false
false
+ 0
18659
@@ -149278,6 +167937,7 @@
false
false
false
+ 0
18660
@@ -149286,6 +167946,7 @@
false
false
false
+ 0
18661
@@ -149294,6 +167955,7 @@
false
false
false
+ 0
18662
@@ -149302,6 +167964,7 @@
false
false
false
+ 0
18663
@@ -149310,6 +167973,7 @@
false
false
false
+ 0
18664
@@ -149318,6 +167982,7 @@
false
false
false
+ 0
18665
@@ -149326,6 +167991,7 @@
false
false
false
+ 0
18666
@@ -149334,6 +168000,7 @@
false
false
false
+ 0
18667
@@ -149342,6 +168009,7 @@
false
false
false
+ 0
18668
@@ -149350,6 +168018,7 @@
false
false
false
+ 0
18669
@@ -149358,6 +168027,7 @@
false
false
false
+ 0
18670
@@ -149366,6 +168036,7 @@
false
false
false
+ 0
18671
@@ -149374,6 +168045,7 @@
false
false
false
+ 0
18672
@@ -149382,6 +168054,7 @@
false
false
false
+ 0
18673
@@ -149390,6 +168063,7 @@
false
false
false
+ 0
18674
@@ -149398,6 +168072,7 @@
false
false
false
+ 0
18675
@@ -149406,6 +168081,7 @@
false
false
false
+ 0
18676
@@ -149414,6 +168090,7 @@
false
false
false
+ 0
18677
@@ -149422,6 +168099,7 @@
false
false
false
+ 0
18678
@@ -149430,6 +168108,7 @@
false
false
false
+ 0
18679
@@ -149438,6 +168117,7 @@
false
false
false
+ 0
18680
@@ -149446,6 +168126,7 @@
false
false
false
+ 0
18681
@@ -149454,6 +168135,7 @@
false
false
false
+ 0
18682
@@ -149462,6 +168144,7 @@
false
false
false
+ 0
18683
@@ -149470,6 +168153,7 @@
false
false
false
+ 0
18684
@@ -149478,6 +168162,7 @@
false
false
false
+ 0
18685
@@ -149486,6 +168171,7 @@
false
false
false
+ 0
18686
@@ -149494,6 +168180,7 @@
false
false
false
+ 0
18687
@@ -149502,6 +168189,7 @@
false
false
false
+ 0
18688
@@ -149510,6 +168198,7 @@
false
false
false
+ 0
18689
@@ -149518,6 +168207,7 @@
false
false
false
+ 0
18690
@@ -149526,6 +168216,7 @@
false
false
false
+ 0
18691
@@ -149534,6 +168225,7 @@
false
false
false
+ 0
18692
@@ -149542,6 +168234,7 @@
false
false
false
+ 0
18693
@@ -149550,6 +168243,7 @@
false
false
false
+ 0
18694
@@ -149558,6 +168252,7 @@
false
false
false
+ 0
18695
@@ -149566,6 +168261,7 @@
false
false
false
+ 0
18696
@@ -149574,6 +168270,7 @@
false
false
false
+ 0
18697
@@ -149582,6 +168279,7 @@
false
false
false
+ 0
18698
@@ -149590,6 +168288,7 @@
false
false
false
+ 0
18699
@@ -149598,6 +168297,7 @@
false
false
false
+ 0
18700
@@ -149606,6 +168306,7 @@
false
false
false
+ 0
18701
@@ -149614,6 +168315,7 @@
false
false
false
+ 0
18702
@@ -149622,6 +168324,7 @@
false
false
false
+ 0
18703
@@ -149630,6 +168333,7 @@
false
false
false
+ 0
18704
@@ -149638,6 +168342,7 @@
false
false
false
+ 0
18705
@@ -149646,6 +168351,7 @@
false
false
false
+ 0
18706
@@ -149654,6 +168360,7 @@
false
false
false
+ 0
18707
@@ -149662,6 +168369,7 @@
false
false
false
+ 0
18708
@@ -149670,6 +168378,7 @@
false
false
false
+ 0
18709
@@ -149678,6 +168387,7 @@
false
false
false
+ 0
18710
@@ -149686,6 +168396,7 @@
false
false
false
+ 0
18711
@@ -149694,6 +168405,7 @@
false
false
false
+ 0
18712
@@ -149702,6 +168414,7 @@
false
false
false
+ 0
18713
@@ -149710,6 +168423,7 @@
false
false
false
+ 0
18714
@@ -149718,6 +168432,7 @@
false
false
false
+ 0
18715
@@ -149726,6 +168441,7 @@
false
false
false
+ 0
18716
@@ -149734,6 +168450,7 @@
false
false
false
+ 0
18717
@@ -149742,6 +168459,7 @@
false
false
false
+ 0
18718
@@ -149750,6 +168468,7 @@
false
false
false
+ 0
18719
@@ -149758,6 +168477,7 @@
false
false
false
+ 0
18720
@@ -149766,6 +168486,7 @@
false
false
false
+ 0
18721
@@ -149774,6 +168495,7 @@
false
false
false
+ 0
18722
@@ -149782,6 +168504,7 @@
false
false
false
+ 0
18723
@@ -149790,6 +168513,7 @@
false
false
false
+ 0
18724
@@ -149798,6 +168522,7 @@
false
false
false
+ 0
18725
@@ -149806,6 +168531,7 @@
false
false
false
+ 0
18726
@@ -149814,6 +168540,7 @@
false
false
false
+ 0
18727
@@ -149822,6 +168549,7 @@
false
false
false
+ 0
18728
@@ -149830,6 +168558,7 @@
false
false
false
+ 0
18729
@@ -149838,6 +168567,7 @@
false
false
false
+ 0
18730
@@ -149846,6 +168576,7 @@
false
false
false
+ 0
18731
@@ -149854,6 +168585,7 @@
false
false
false
+ 0
18732
@@ -149862,6 +168594,7 @@
false
false
false
+ 0
18733
@@ -149870,6 +168603,7 @@
false
false
false
+ 0
18734
@@ -149878,6 +168612,7 @@
false
false
false
+ 0
18735
@@ -149886,6 +168621,7 @@
false
false
false
+ 0
18736
@@ -149894,6 +168630,7 @@
false
false
false
+ 0
18737
@@ -149902,6 +168639,7 @@
false
false
false
+ 0
18738
@@ -149910,6 +168648,7 @@
false
false
false
+ 0
18739
@@ -149918,6 +168657,7 @@
false
false
false
+ 0
18740
@@ -149926,6 +168666,7 @@
false
false
false
+ 0
18741
@@ -149934,6 +168675,7 @@
false
false
false
+ 0
18742
@@ -149942,6 +168684,7 @@
false
false
false
+ 0
18743
@@ -149950,6 +168693,7 @@
false
false
false
+ 0
18744
@@ -149958,6 +168702,7 @@
false
false
false
+ 0
18745
@@ -149966,6 +168711,7 @@
false
false
false
+ 0
18746
@@ -149974,6 +168720,7 @@
false
false
false
+ 0
18747
@@ -149982,6 +168729,7 @@
false
false
false
+ 0
18748
@@ -149990,6 +168738,7 @@
false
false
false
+ 0
18749
@@ -149998,6 +168747,7 @@
false
false
false
+ 0
18750
@@ -150006,6 +168756,7 @@
false
false
false
+ 0
18751
@@ -150014,6 +168765,7 @@
false
false
false
+ 0
18752
@@ -150022,6 +168774,7 @@
false
false
false
+ 0
18753
@@ -150030,6 +168783,7 @@
false
false
false
+ 0
18754
@@ -150038,6 +168792,7 @@
false
false
false
+ 0
18755
@@ -150046,6 +168801,7 @@
false
false
false
+ 0
18756
@@ -150054,6 +168810,7 @@
false
false
false
+ 0
18757
@@ -150062,6 +168819,7 @@
false
false
false
+ 0
18758
@@ -150070,6 +168828,7 @@
false
false
false
+ 0
18759
@@ -150078,6 +168837,7 @@
false
false
false
+ 0
18760
@@ -150086,6 +168846,7 @@
false
false
false
+ 0
18761
@@ -150094,6 +168855,7 @@
false
false
false
+ 0
18762
@@ -150102,6 +168864,7 @@
false
false
false
+ 0
18763
@@ -150110,6 +168873,7 @@
false
false
false
+ 0
18764
@@ -150118,6 +168882,7 @@
false
false
false
+ 0
18765
@@ -150126,6 +168891,7 @@
false
false
false
+ 0
18766
@@ -150134,6 +168900,7 @@
false
false
false
+ 0
18767
@@ -150142,6 +168909,7 @@
false
false
false
+ 0
18768
@@ -150150,6 +168918,7 @@
false
false
false
+ 0
18769
@@ -150158,6 +168927,7 @@
false
false
false
+ 0
18770
@@ -150166,6 +168936,7 @@
false
false
false
+ 0
18771
@@ -150174,6 +168945,7 @@
false
false
false
+ 0
18772
@@ -150182,6 +168954,7 @@
false
false
false
+ 0
18773
@@ -150190,6 +168963,7 @@
false
false
false
+ 0
18774
@@ -150198,6 +168972,7 @@
false
false
false
+ 0
18775
@@ -150206,6 +168981,7 @@
false
false
false
+ 0
18776
@@ -150214,6 +168990,7 @@
false
false
false
+ 0
18777
@@ -150222,6 +168999,7 @@
false
false
false
+ 0
18778
@@ -150230,6 +169008,7 @@
false
false
false
+ 0
18779
@@ -150238,6 +169017,7 @@
false
false
false
+ 0
18780
@@ -150246,6 +169026,7 @@
false
false
false
+ 0
18781
@@ -150254,6 +169035,7 @@
false
false
false
+ 0
18782
@@ -150262,6 +169044,7 @@
false
false
false
+ 0
18783
@@ -150270,6 +169053,7 @@
false
false
false
+ 0
18784
@@ -150278,6 +169062,7 @@
false
false
false
+ 0
18785
@@ -150286,6 +169071,7 @@
false
false
false
+ 0
18786
@@ -150294,6 +169080,7 @@
false
false
false
+ 0
18787
@@ -150302,6 +169089,7 @@
false
false
false
+ 0
18788
@@ -150310,6 +169098,7 @@
false
false
false
+ 0
18789
@@ -150318,6 +169107,7 @@
false
false
false
+ 0
18790
@@ -150326,6 +169116,7 @@
false
false
false
+ 0
18791
@@ -150334,6 +169125,7 @@
false
false
false
+ 0
18792
@@ -150342,6 +169134,7 @@
false
false
false
+ 0
18793
@@ -150350,6 +169143,7 @@
false
false
false
+ 0
18794
@@ -150358,6 +169152,7 @@
false
false
false
+ 0
18795
@@ -150366,6 +169161,7 @@
false
false
false
+ 0
18796
@@ -150374,6 +169170,7 @@
false
false
false
+ 0
18797
@@ -150382,6 +169179,7 @@
false
false
false
+ 0
18798
@@ -150390,6 +169188,7 @@
false
false
false
+ 0
18799
@@ -150398,6 +169197,7 @@
false
false
false
+ 0
18800
@@ -150406,6 +169206,7 @@
false
false
false
+ 0
18801
@@ -150414,6 +169215,7 @@
false
false
false
+ 0
18802
@@ -150422,6 +169224,7 @@
false
false
false
+ 0
18803
@@ -150430,6 +169233,7 @@
false
false
false
+ 0
18804
@@ -150438,6 +169242,7 @@
false
false
false
+ 0
18805
@@ -150446,6 +169251,7 @@
false
false
false
+ 0
18806
@@ -150454,6 +169260,7 @@
false
false
false
+ 0
18807
@@ -150462,6 +169269,7 @@
false
false
false
+ 0
18808
@@ -150470,6 +169278,7 @@
false
false
false
+ 0
18809
@@ -150478,6 +169287,7 @@
false
false
false
+ 0
18810
@@ -150486,6 +169296,7 @@
false
false
false
+ 0
18811
@@ -150494,6 +169305,7 @@
false
false
false
+ 0
18812
@@ -150502,6 +169314,7 @@
false
false
false
+ 0
18813
@@ -150510,6 +169323,7 @@
false
false
false
+ 0
18814
@@ -150518,6 +169332,7 @@
false
false
false
+ 0
18815
@@ -150526,6 +169341,7 @@
false
false
false
+ 0
18816
@@ -150534,6 +169350,7 @@
false
false
false
+ 0
18817
@@ -150542,6 +169359,7 @@
false
false
false
+ 0
18818
@@ -150550,6 +169368,7 @@
false
false
false
+ 0
18819
@@ -150558,6 +169377,7 @@
false
false
false
+ 0
18820
@@ -150566,6 +169386,7 @@
false
false
false
+ 0
18821
@@ -150574,6 +169395,7 @@
false
false
false
+ 0
18822
@@ -150582,6 +169404,7 @@
false
false
false
+ 0
18823
@@ -150590,6 +169413,7 @@
false
false
false
+ 0
18824
@@ -150598,6 +169422,7 @@
false
false
false
+ 0
18825
@@ -150606,6 +169431,7 @@
false
false
false
+ 0
18826
@@ -150614,6 +169440,7 @@
false
false
false
+ 0
18827
@@ -150622,6 +169449,7 @@
false
false
false
+ 0
18828
@@ -150630,6 +169458,7 @@
false
false
false
+ 0
18829
@@ -150638,6 +169467,7 @@
false
false
false
+ 0
18830
@@ -150646,6 +169476,7 @@
false
false
false
+ 0
18831
@@ -150654,6 +169485,7 @@
false
false
false
+ 0
18832
@@ -150662,6 +169494,7 @@
false
false
false
+ 0
18833
@@ -150670,6 +169503,7 @@
false
false
false
+ 0
18834
@@ -150678,6 +169512,7 @@
false
false
false
+ 0
18835
@@ -150686,6 +169521,7 @@
false
false
false
+ 0
18836
@@ -150694,6 +169530,7 @@
false
false
false
+ 0
18837
@@ -150702,6 +169539,7 @@
false
false
false
+ 0
18838
@@ -150710,6 +169548,7 @@
false
false
false
+ 0
18839
@@ -150718,6 +169557,7 @@
false
false
false
+ 0
18840
@@ -150726,6 +169566,7 @@
false
false
false
+ 0
18841
@@ -150734,6 +169575,7 @@
false
false
false
+ 0
18842
@@ -150742,6 +169584,7 @@
false
false
false
+ 0
18843
@@ -150750,6 +169593,7 @@
false
false
false
+ 0
18844
@@ -150758,6 +169602,7 @@
false
false
false
+ 0
18845
@@ -150766,6 +169611,7 @@
false
false
false
+ 0
18846
@@ -150774,6 +169620,7 @@
false
false
false
+ 0
18847
@@ -150782,6 +169629,7 @@
false
false
false
+ 0
18848
@@ -150790,6 +169638,7 @@
false
false
false
+ 0
18849
@@ -150798,6 +169647,7 @@
false
false
false
+ 0
18850
@@ -150806,6 +169656,7 @@
false
false
false
+ 0
18851
@@ -150814,6 +169665,7 @@
false
false
false
+ 0
18852
@@ -150822,6 +169674,7 @@
false
false
false
+ 0
18853
@@ -150830,6 +169683,7 @@
false
false
false
+ 0
18854
@@ -150838,6 +169692,7 @@
false
false
false
+ 0
18855
@@ -150846,6 +169701,7 @@
false
false
false
+ 0
18856
@@ -150854,6 +169710,7 @@
false
false
false
+ 0
18857
@@ -150862,6 +169719,7 @@
false
false
false
+ 0
18858
@@ -150870,6 +169728,7 @@
false
false
false
+ 0
18859
@@ -150878,6 +169737,7 @@
false
false
false
+ 0
18860
@@ -150886,6 +169746,7 @@
false
false
false
+ 0
18861
@@ -150894,6 +169755,7 @@
false
false
false
+ 0
18862
@@ -150902,6 +169764,7 @@
false
false
false
+ 0
18863
@@ -150910,6 +169773,7 @@
false
false
false
+ 0
18864
@@ -150918,6 +169782,7 @@
false
false
false
+ 0
18865
@@ -150926,6 +169791,7 @@
false
false
false
+ 0
18866
@@ -150934,6 +169800,7 @@
false
false
false
+ 0
18867
@@ -150942,6 +169809,7 @@
false
false
false
+ 0
18868
@@ -150950,6 +169818,7 @@
false
false
false
+ 0
18869
@@ -150958,6 +169827,7 @@
false
false
false
+ 0
18870
@@ -150966,6 +169836,7 @@
false
false
false
+ 0
18871
@@ -150974,6 +169845,7 @@
false
false
false
+ 0
18872
@@ -150982,6 +169854,7 @@
false
false
false
+ 0
18873
@@ -150990,6 +169863,7 @@
false
false
false
+ 0
18874
@@ -150998,6 +169872,7 @@
false
false
false
+ 0
18875
@@ -151006,6 +169881,7 @@
false
false
false
+ 0
18876
@@ -151014,6 +169890,7 @@
false
false
false
+ 0
18877
@@ -151022,6 +169899,7 @@
false
false
false
+ 0
18878
@@ -151030,6 +169908,7 @@
false
false
false
+ 0
18879
@@ -151038,6 +169917,7 @@
false
false
false
+ 0
18880
@@ -151046,6 +169926,7 @@
false
false
false
+ 0
18881
@@ -151054,6 +169935,7 @@
false
false
false
+ 0
18882
@@ -151062,6 +169944,7 @@
false
false
false
+ 0
18883
@@ -151070,6 +169953,7 @@
false
false
false
+ 0
18884
@@ -151078,6 +169962,7 @@
false
false
false
+ 0
18885
@@ -151086,6 +169971,7 @@
false
false
false
+ 0
18886
@@ -151094,6 +169980,7 @@
false
false
false
+ 0
18887
@@ -151102,6 +169989,7 @@
false
false
false
+ 0
18888
@@ -151110,6 +169998,7 @@
false
false
false
+ 0
18889
@@ -151118,6 +170007,7 @@
false
false
false
+ 0
18890
@@ -151126,6 +170016,7 @@
false
false
false
+ 0
18891
@@ -151134,6 +170025,7 @@
false
false
false
+ 0
18892
@@ -151142,6 +170034,7 @@
false
false
false
+ 0
18893
@@ -151150,6 +170043,7 @@
false
false
false
+ 0
18894
@@ -151158,6 +170052,7 @@
false
false
false
+ 0
18895
@@ -151166,6 +170061,7 @@
false
false
false
+ 0
18896
@@ -151174,6 +170070,7 @@
false
false
false
+ 0
18897
@@ -151182,6 +170079,7 @@
false
false
false
+ 0
18898
@@ -151190,6 +170088,7 @@
false
false
false
+ 0
18899
@@ -151198,6 +170097,7 @@
false
false
false
+ 0
18900
@@ -151206,6 +170106,7 @@
false
false
false
+ 0
18901
@@ -151214,6 +170115,7 @@
false
false
false
+ 0
18902
@@ -151222,6 +170124,7 @@
false
false
false
+ 0
18903
@@ -151230,6 +170133,7 @@
false
false
false
+ 0
18904
@@ -151238,6 +170142,7 @@
false
false
false
+ 0
18905
@@ -151246,6 +170151,7 @@
false
false
false
+ 0
18906
@@ -151254,6 +170160,7 @@
false
false
false
+ 0
18907
@@ -151262,6 +170169,7 @@
false
false
false
+ 0
18908
@@ -151270,6 +170178,7 @@
false
false
false
+ 0
18909
@@ -151278,6 +170187,7 @@
false
false
false
+ 0
18910
@@ -151286,6 +170196,7 @@
false
false
false
+ 0
18911
@@ -151294,6 +170205,7 @@
false
false
false
+ 0
18912
@@ -151302,6 +170214,7 @@
false
false
false
+ 0
18913
@@ -151310,6 +170223,7 @@
false
false
false
+ 0
18914
@@ -151318,6 +170232,7 @@
false
false
false
+ 0
18915
@@ -151326,6 +170241,7 @@
false
false
false
+ 0
18916
@@ -151334,6 +170250,7 @@
false
false
false
+ 0
18917
@@ -151342,6 +170259,7 @@
false
false
false
+ 0
18918
@@ -151350,6 +170268,7 @@
false
false
false
+ 0
18919
@@ -151358,6 +170277,7 @@
false
false
false
+ 0
18920
@@ -151366,6 +170286,7 @@
false
false
false
+ 0
18921
@@ -151374,6 +170295,7 @@
false
false
false
+ 0
18922
@@ -151382,6 +170304,7 @@
false
false
false
+ 0
18923
@@ -151390,6 +170313,7 @@
false
false
false
+ 0
18924
@@ -151398,6 +170322,7 @@
false
false
false
+ 0
18925
@@ -151406,6 +170331,7 @@
false
false
false
+ 0
18926
@@ -151414,6 +170340,7 @@
false
false
false
+ 0
18927
@@ -151422,6 +170349,7 @@
false
false
false
+ 0
18928
@@ -151430,6 +170358,7 @@
false
false
false
+ 0
18929
@@ -151438,6 +170367,7 @@
false
false
false
+ 0
18930
@@ -151446,6 +170376,7 @@
false
false
false
+ 0
18931
@@ -151454,6 +170385,7 @@
false
false
false
+ 0
18932
@@ -151462,6 +170394,7 @@
false
false
false
+ 0
18933
@@ -151470,6 +170403,7 @@
false
false
false
+ 0
18934
@@ -151478,6 +170412,7 @@
false
false
false
+ 0
18935
@@ -151486,6 +170421,7 @@
false
false
false
+ 0
18936
@@ -151494,6 +170430,7 @@
false
false
false
+ 0
18937
@@ -151502,6 +170439,7 @@
false
false
false
+ 0
18938
@@ -151510,6 +170448,7 @@
false
false
false
+ 0
18939
@@ -151518,6 +170457,7 @@
false
false
false
+ 0
18940
@@ -151526,6 +170466,7 @@
false
false
false
+ 0
18941
@@ -151534,6 +170475,7 @@
false
false
false
+ 0
18942
@@ -151542,6 +170484,7 @@
false
false
false
+ 0
18943
@@ -151550,6 +170493,7 @@
false
false
false
+ 0
18944
@@ -151558,6 +170502,7 @@
false
false
false
+ 0
18945
@@ -151566,6 +170511,7 @@
false
false
false
+ 0
18946
@@ -151574,6 +170520,7 @@
false
false
false
+ 0
18947
@@ -151582,6 +170529,7 @@
false
false
false
+ 0
18948
@@ -151590,6 +170538,7 @@
false
false
false
+ 0
18949
@@ -151598,6 +170547,7 @@
false
false
false
+ 0
18950
@@ -151606,6 +170556,7 @@
false
false
false
+ 0
18951
@@ -151614,6 +170565,7 @@
false
false
false
+ 0
18952
@@ -151622,6 +170574,7 @@
false
false
false
+ 0
18953
@@ -151630,6 +170583,7 @@
false
false
false
+ 0
18954
@@ -151638,6 +170592,7 @@
false
false
false
+ 0
18955
@@ -151646,6 +170601,7 @@
false
false
false
+ 0
18956
@@ -151654,6 +170610,7 @@
false
false
false
+ 0
18957
@@ -151662,6 +170619,7 @@
false
false
false
+ 0
18958
@@ -151670,6 +170628,7 @@
false
false
false
+ 0
18959
@@ -151678,6 +170637,7 @@
false
false
false
+ 0
18960
@@ -151686,6 +170646,7 @@
false
false
false
+ 0
18961
@@ -151694,6 +170655,7 @@
false
false
false
+ 0
18962
@@ -151702,6 +170664,7 @@
false
false
false
+ 0
18963
@@ -151710,6 +170673,7 @@
false
false
false
+ 0
18964
@@ -151718,6 +170682,7 @@
false
false
false
+ 0
18965
@@ -151726,6 +170691,7 @@
false
false
false
+ 0
18966
@@ -151734,6 +170700,7 @@
false
false
false
+ 0
18967
@@ -151742,6 +170709,7 @@
false
false
false
+ 0
18968
@@ -151750,6 +170718,7 @@
false
false
false
+ 0
18969
@@ -151758,6 +170727,7 @@
false
false
false
+ 0
18970
@@ -151766,6 +170736,7 @@
false
false
false
+ 0
18971
@@ -151774,6 +170745,7 @@
false
false
false
+ 0
18972
@@ -151782,6 +170754,7 @@
false
false
false
+ 0
18973
@@ -151790,6 +170763,7 @@
false
false
false
+ 0
18974
@@ -151798,6 +170772,7 @@
false
false
false
+ 0
18975
@@ -151806,6 +170781,7 @@
false
false
false
+ 0
18976
@@ -151814,6 +170790,7 @@
false
false
false
+ 0
18977
@@ -151822,6 +170799,7 @@
false
false
false
+ 0
18978
@@ -151830,6 +170808,7 @@
false
false
false
+ 0
18979
@@ -151838,6 +170817,7 @@
false
false
false
+ 0
18980
@@ -151846,6 +170826,7 @@
false
false
false
+ 0
18981
@@ -151854,6 +170835,7 @@
false
false
false
+ 0
18982
@@ -151862,6 +170844,7 @@
false
false
false
+ 0
18983
@@ -151870,6 +170853,7 @@
false
false
false
+ 0
18984
@@ -151878,6 +170862,7 @@
false
false
false
+ 0
18985
@@ -151886,6 +170871,7 @@
false
false
false
+ 0
18986
@@ -151894,6 +170880,7 @@
false
false
false
+ 0
18987
@@ -151902,6 +170889,7 @@
false
false
false
+ 0
18988
@@ -151910,6 +170898,7 @@
false
false
false
+ 0
18989
@@ -151918,6 +170907,7 @@
false
false
false
+ 0
18990
@@ -151926,6 +170916,7 @@
false
false
false
+ 0
18991
@@ -151934,6 +170925,7 @@
false
false
false
+ 0
18992
@@ -151942,6 +170934,7 @@
false
false
false
+ 0
18993
@@ -151950,6 +170943,7 @@
false
false
false
+ 0
18994
@@ -151958,6 +170952,7 @@
false
false
false
+ 0
18995
@@ -151966,6 +170961,7 @@
false
false
false
+ 0
18996
@@ -151974,6 +170970,7 @@
false
false
false
+ 0
18997
@@ -151982,6 +170979,7 @@
false
false
false
+ 0
18998
@@ -151990,6 +170988,7 @@
false
false
false
+ 0
18999
@@ -151998,6 +170997,7 @@
false
false
false
+ 0
19000
@@ -152006,6 +171006,7 @@
false
false
false
+ 0
19001
@@ -152014,6 +171015,7 @@
false
false
false
+ 0
19002
@@ -152022,6 +171024,7 @@
false
false
false
+ 0
19003
@@ -152030,6 +171033,7 @@
false
false
false
+ 0
19004
@@ -152038,6 +171042,7 @@
false
false
false
+ 0
19005
@@ -152046,6 +171051,7 @@
false
false
false
+ 0
19006
@@ -152054,6 +171060,7 @@
false
false
false
+ 0
19007
@@ -152062,6 +171069,7 @@
false
false
false
+ 0
19008
@@ -152070,6 +171078,7 @@
false
false
false
+ 0
19009
@@ -152078,6 +171087,7 @@
false
false
false
+ 0
19010
@@ -152086,6 +171096,7 @@
false
false
false
+ 0
19011
@@ -152094,6 +171105,7 @@
false
false
false
+ 0
19012
@@ -152102,6 +171114,7 @@
false
false
false
+ 0
19013
@@ -152110,6 +171123,7 @@
false
false
false
+ 0
19014
@@ -152118,6 +171132,7 @@
false
false
false
+ 0
19015
@@ -152126,6 +171141,7 @@
false
false
false
+ 0
19016
@@ -152134,6 +171150,7 @@
false
false
false
+ 0
19017
@@ -152142,6 +171159,7 @@
false
false
false
+ 0
19018
@@ -152150,6 +171168,7 @@
false
false
false
+ 0
19019
@@ -152158,6 +171177,7 @@
false
false
false
+ 0
19020
@@ -152166,6 +171186,7 @@
false
false
false
+ 0
19021
@@ -152174,6 +171195,7 @@
false
false
false
+ 0
19022
@@ -152182,6 +171204,7 @@
false
false
false
+ 0
19023
@@ -152190,6 +171213,7 @@
false
false
false
+ 0
19024
@@ -152198,6 +171222,7 @@
false
false
false
+ 0
19025
@@ -152206,6 +171231,7 @@
false
false
false
+ 0
19026
@@ -152214,6 +171240,7 @@
false
false
false
+ 0
19027
@@ -152222,6 +171249,7 @@
false
false
false
+ 0
19028
@@ -152230,6 +171258,7 @@
false
false
false
+ 0
19029
@@ -152238,6 +171267,7 @@
false
false
false
+ 0
19030
@@ -152246,6 +171276,7 @@
false
false
false
+ 0
19031
@@ -152254,6 +171285,7 @@
false
false
false
+ 0
19032
@@ -152262,6 +171294,7 @@
false
false
false
+ 0
19033
@@ -152270,6 +171303,7 @@
false
false
false
+ 0
19034
@@ -152278,6 +171312,7 @@
false
false
false
+ 0
19035
@@ -152286,6 +171321,7 @@
false
false
false
+ 0
19036
@@ -152294,6 +171330,7 @@
false
false
false
+ 0
19037
@@ -152302,6 +171339,7 @@
false
false
false
+ 0
19038
@@ -152310,6 +171348,7 @@
false
false
false
+ 0
19039
@@ -152318,6 +171357,7 @@
false
false
false
+ 0
19040
@@ -152326,6 +171366,7 @@
false
false
false
+ 0
19041
@@ -152334,6 +171375,7 @@
false
false
false
+ 0
19042
@@ -152342,6 +171384,7 @@
false
false
false
+ 0
19043
@@ -152350,6 +171393,7 @@
false
false
false
+ 0
19044
@@ -152358,6 +171402,7 @@
false
false
false
+ 0
19045
@@ -152366,6 +171411,7 @@
false
false
false
+ 0
19046
@@ -152374,6 +171420,7 @@
false
false
false
+ 0
19047
@@ -152382,6 +171429,7 @@
false
false
false
+ 0
19048
@@ -152390,6 +171438,7 @@
false
false
false
+ 0
19049
@@ -152398,6 +171447,7 @@
false
false
false
+ 0
19050
@@ -152406,6 +171456,7 @@
false
false
false
+ 0
19051
@@ -152414,6 +171465,7 @@
false
false
false
+ 0
19052
@@ -152422,6 +171474,7 @@
false
false
false
+ 0
19053
@@ -152430,6 +171483,7 @@
false
false
false
+ 0
19054
@@ -152438,6 +171492,7 @@
false
false
false
+ 0
19055
@@ -152446,6 +171501,7 @@
false
false
false
+ 0
19056
@@ -152454,6 +171510,7 @@
false
false
false
+ 0
19057
@@ -152462,6 +171519,7 @@
false
false
false
+ 0
19058
@@ -152470,6 +171528,7 @@
false
false
false
+ 0
19059
@@ -152478,6 +171537,7 @@
false
false
false
+ 0
19060
@@ -152486,6 +171546,7 @@
false
false
false
+ 0
19061
@@ -152494,6 +171555,7 @@
false
false
false
+ 0
19062
@@ -152502,6 +171564,7 @@
false
false
false
+ 0
19063
@@ -152510,6 +171573,7 @@
false
false
false
+ 0
19064
@@ -152518,6 +171582,7 @@
false
false
false
+ 0
19065
@@ -152526,6 +171591,7 @@
false
false
false
+ 0
19066
@@ -152534,6 +171600,7 @@
false
false
false
+ 0
19067
@@ -152542,6 +171609,7 @@
false
false
false
+ 0
19068
@@ -152550,6 +171618,7 @@
false
false
false
+ 0
19069
@@ -152558,6 +171627,7 @@
false
false
false
+ 0
19070
@@ -152566,6 +171636,7 @@
false
false
false
+ 0
19071
@@ -152574,6 +171645,7 @@
false
false
false
+ 0
19072
@@ -152582,6 +171654,7 @@
false
false
false
+ 0
19073
@@ -152590,6 +171663,7 @@
false
false
false
+ 0
19074
@@ -152598,6 +171672,7 @@
false
false
false
+ 0
19075
@@ -152606,6 +171681,7 @@
false
false
false
+ 0
19076
@@ -152614,6 +171690,7 @@
false
false
false
+ 0
19077
@@ -152622,6 +171699,7 @@
false
false
false
+ 0
19078
@@ -152630,6 +171708,7 @@
false
false
false
+ 0
19079
@@ -152638,6 +171717,7 @@
false
false
false
+ 0
19080
@@ -152646,6 +171726,7 @@
false
false
false
+ 0
19081
@@ -152654,6 +171735,7 @@
false
false
false
+ 0
19082
@@ -152662,6 +171744,7 @@
false
false
false
+ 0
19083
@@ -152670,6 +171753,7 @@
false
false
false
+ 0
19084
@@ -152678,6 +171762,7 @@
false
false
false
+ 0
19085
@@ -152686,6 +171771,7 @@
false
false
false
+ 0
19086
@@ -152694,6 +171780,7 @@
false
false
false
+ 0
19087
@@ -152702,6 +171789,7 @@
false
false
false
+ 0
19088
@@ -152710,6 +171798,7 @@
false
false
false
+ 0
19089
@@ -152718,6 +171807,7 @@
false
false
false
+ 0
19090
@@ -152726,6 +171816,7 @@
false
false
false
+ 0
19091
@@ -152734,6 +171825,7 @@
false
false
false
+ 0
19092
@@ -152742,6 +171834,7 @@
false
false
false
+ 0
19093
@@ -152750,6 +171843,7 @@
false
false
false
+ 0
19094
@@ -152758,6 +171852,7 @@
false
false
false
+ 0
19095
@@ -152766,6 +171861,7 @@
false
false
false
+ 0
19096
@@ -152774,6 +171870,7 @@
false
false
false
+ 0
19097
@@ -152782,6 +171879,7 @@
false
false
false
+ 0
19098
@@ -152790,6 +171888,7 @@
false
false
false
+ 0
19099
@@ -152798,6 +171897,7 @@
false
false
false
+ 0
19100
@@ -152806,6 +171906,7 @@
false
false
false
+ 0
19101
@@ -152814,6 +171915,7 @@
false
false
false
+ 0
19102
@@ -152822,6 +171924,7 @@
false
false
false
+ 0
19103
@@ -152830,6 +171933,7 @@
false
false
false
+ 0
19104
@@ -152838,6 +171942,7 @@
false
false
false
+ 0
19105
@@ -152846,6 +171951,7 @@
false
false
false
+ 0
19106
@@ -152854,6 +171960,7 @@
false
false
false
+ 0
19107
@@ -152862,6 +171969,7 @@
false
false
false
+ 0
19108
@@ -152870,6 +171978,7 @@
false
false
false
+ 0
19109
@@ -152878,6 +171987,7 @@
false
false
false
+ 0
19110
@@ -152886,6 +171996,7 @@
false
false
false
+ 0
19111
@@ -152894,6 +172005,7 @@
false
false
false
+ 0
19112
@@ -152902,6 +172014,7 @@
false
false
false
+ 0
19113
@@ -152910,6 +172023,7 @@
false
false
false
+ 0
19114
@@ -152918,6 +172032,7 @@
false
false
false
+ 0
19115
@@ -152926,6 +172041,7 @@
false
false
false
+ 0
19116
@@ -152934,6 +172050,7 @@
false
false
false
+ 0
19117
@@ -152942,6 +172059,7 @@
false
false
false
+ 0
19118
@@ -152950,6 +172068,7 @@
false
false
false
+ 0
19119
@@ -152958,6 +172077,7 @@
false
false
false
+ 0
19120
@@ -152966,6 +172086,7 @@
false
false
false
+ 0
19121
@@ -152974,6 +172095,7 @@
false
false
false
+ 0
19122
@@ -152982,6 +172104,7 @@
false
false
false
+ 0
19123
@@ -152990,6 +172113,7 @@
false
false
false
+ 0
19124
@@ -152998,6 +172122,7 @@
false
false
false
+ 0
19125
@@ -153006,6 +172131,7 @@
false
false
false
+ 0
19126
@@ -153014,6 +172140,7 @@
false
false
false
+ 0
19127
@@ -153022,6 +172149,7 @@
false
false
false
+ 0
19128
@@ -153030,6 +172158,7 @@
false
false
false
+ 0
19129
@@ -153038,6 +172167,7 @@
false
false
false
+ 0
19130
@@ -153046,6 +172176,7 @@
false
false
false
+ 0
19131
@@ -153054,6 +172185,7 @@
false
false
false
+ 0
19132
@@ -153062,6 +172194,7 @@
false
false
false
+ 0
19133
@@ -153070,6 +172203,7 @@
false
false
false
+ 0
19134
@@ -153078,6 +172212,7 @@
false
false
false
+ 0
19135
@@ -153086,6 +172221,7 @@
false
false
false
+ 0
19136
@@ -153094,6 +172230,7 @@
false
false
false
+ 0
19137
@@ -153102,6 +172239,7 @@
false
false
false
+ 0
19138
@@ -153110,6 +172248,7 @@
false
false
false
+ 0
19139
@@ -153118,6 +172257,7 @@
false
false
false
+ 0
19140
@@ -153126,6 +172266,7 @@
false
false
false
+ 0
19141
@@ -153134,6 +172275,7 @@
false
false
false
+ 0
19142
@@ -153142,6 +172284,7 @@
false
false
false
+ 0
19143
@@ -153150,6 +172293,7 @@
false
false
false
+ 0
19144
@@ -153158,6 +172302,7 @@
false
false
false
+ 0
19145
@@ -153166,6 +172311,7 @@
false
false
false
+ 0
19146
@@ -153174,6 +172320,7 @@
false
false
false
+ 0
19147
@@ -153182,6 +172329,7 @@
false
false
false
+ 0
19148
@@ -153190,6 +172338,7 @@
false
false
false
+ 0
19149
@@ -153198,6 +172347,7 @@
false
false
false
+ 0
19150
@@ -153206,6 +172356,7 @@
false
false
false
+ 0
19151
@@ -153214,6 +172365,7 @@
false
false
false
+ 0
19152
@@ -153222,6 +172374,7 @@
false
false
false
+ 0
19153
@@ -153230,6 +172383,7 @@
false
false
false
+ 0
19154
@@ -153238,6 +172392,7 @@
false
false
false
+ 0
19155
@@ -153246,6 +172401,7 @@
false
false
false
+ 0
19156
@@ -153254,6 +172410,7 @@
false
false
false
+ 0
19157
@@ -153262,6 +172419,7 @@
false
false
false
+ 0
19158
@@ -153270,6 +172428,7 @@
false
false
false
+ 0
19159
@@ -153278,6 +172437,7 @@
false
false
false
+ 0
19160
@@ -153286,6 +172446,7 @@
false
false
false
+ 0
19161
@@ -153294,6 +172455,7 @@
false
false
false
+ 0
19162
@@ -153302,6 +172464,7 @@
false
false
false
+ 0
19163
@@ -153310,6 +172473,7 @@
false
false
false
+ 0
19164
@@ -153318,6 +172482,7 @@
false
false
false
+ 0
19165
@@ -153326,6 +172491,7 @@
false
false
false
+ 0
19166
@@ -153334,6 +172500,7 @@
false
false
false
+ 0
19167
@@ -153342,6 +172509,7 @@
false
false
false
+ 0
19168
@@ -153350,6 +172518,7 @@
false
false
false
+ 0
19169
@@ -153358,6 +172527,7 @@
false
false
false
+ 0
19170
@@ -153366,6 +172536,7 @@
false
false
false
+ 0
19171
@@ -153374,6 +172545,7 @@
false
false
false
+ 0
19172
@@ -153382,6 +172554,7 @@
false
false
false
+ 0
19173
@@ -153390,6 +172563,7 @@
false
false
false
+ 0
19174
@@ -153398,6 +172572,7 @@
false
false
false
+ 0
19175
@@ -153406,6 +172581,7 @@
false
false
false
+ 0
19176
@@ -153414,6 +172590,7 @@
false
false
false
+ 0
19177
@@ -153422,6 +172599,7 @@
false
false
false
+ 0
19178
@@ -153430,6 +172608,7 @@
false
false
false
+ 0
19179
@@ -153438,6 +172617,7 @@
false
false
false
+ 0
19180
@@ -153446,6 +172626,7 @@
false
false
false
+ 0
19181
@@ -153454,6 +172635,7 @@
false
false
false
+ 0
19182
@@ -153462,6 +172644,7 @@
false
false
false
+ 0
19183
@@ -153470,6 +172653,7 @@
false
false
false
+ 0
19184
@@ -153478,6 +172662,7 @@
false
false
false
+ 0
19185
@@ -153486,6 +172671,7 @@
false
false
false
+ 0
19186
@@ -153494,6 +172680,7 @@
false
false
false
+ 0
19187
@@ -153502,6 +172689,7 @@
false
false
false
+ 0
19188
@@ -153510,6 +172698,7 @@
false
false
false
+ 0
19189
@@ -153518,6 +172707,7 @@
false
false
false
+ 0
19190
@@ -153526,6 +172716,7 @@
false
false
false
+ 0
19191
@@ -153534,6 +172725,7 @@
false
false
false
+ 0
19192
@@ -153542,6 +172734,7 @@
false
false
false
+ 0
19193
@@ -153550,6 +172743,7 @@
false
false
false
+ 0
19194
@@ -153558,6 +172752,7 @@
false
false
false
+ 0
19195
@@ -153566,6 +172761,7 @@
false
false
false
+ 0
19196
@@ -153574,6 +172770,7 @@
false
false
false
+ 0
19197
@@ -153582,6 +172779,7 @@
false
false
false
+ 0
19198
@@ -153590,6 +172788,7 @@
false
false
false
+ 0
19199
@@ -153598,6 +172797,7 @@
false
false
false
+ 0
19200
@@ -153606,6 +172806,7 @@
false
false
false
+ 0
19201
@@ -153614,6 +172815,7 @@
false
false
false
+ 0
19202
@@ -153622,6 +172824,7 @@
false
false
false
+ 0
19203
@@ -153630,6 +172833,7 @@
false
false
false
+ 0
19204
@@ -153638,6 +172842,7 @@
false
false
false
+ 0
19205
@@ -153646,6 +172851,7 @@
false
false
false
+ 0
19206
@@ -153654,6 +172860,7 @@
false
false
false
+ 0
19207
@@ -153662,6 +172869,7 @@
false
false
false
+ 0
19208
@@ -153670,6 +172878,7 @@
false
false
false
+ 0
19209
@@ -153678,6 +172887,7 @@
false
false
false
+ 0
19210
@@ -153686,6 +172896,7 @@
false
false
false
+ 0
19211
@@ -153694,6 +172905,7 @@
false
false
false
+ 0
19212
@@ -153702,6 +172914,7 @@
false
false
false
+ 0
19213
@@ -153710,6 +172923,7 @@
false
false
false
+ 0
19214
@@ -153718,6 +172932,7 @@
false
false
false
+ 0
19215
@@ -153726,6 +172941,7 @@
false
false
false
+ 0
19216
@@ -153734,6 +172950,7 @@
false
false
false
+ 0
19217
@@ -153742,6 +172959,7 @@
false
false
false
+ 0
19218
@@ -153750,6 +172968,7 @@
false
false
false
+ 0
19219
@@ -153758,6 +172977,7 @@
false
false
false
+ 0
19220
@@ -153766,6 +172986,7 @@
false
false
false
+ 0
19221
@@ -153774,6 +172995,7 @@
false
false
false
+ 0
19222
@@ -153782,6 +173004,7 @@
false
false
false
+ 0
19223
@@ -153790,6 +173013,7 @@
false
false
false
+ 0
19224
@@ -153798,6 +173022,7 @@
false
false
false
+ 0
19225
@@ -153806,6 +173031,7 @@
false
false
false
+ 0
19226
@@ -153814,6 +173040,7 @@
false
false
false
+ 0
19227
@@ -153822,6 +173049,7 @@
false
false
false
+ 0
19228
@@ -153830,6 +173058,7 @@
false
false
false
+ 0
19229
@@ -153838,6 +173067,7 @@
false
false
false
+ 0
19230
@@ -153846,6 +173076,7 @@
false
false
false
+ 0
19231
@@ -153854,6 +173085,7 @@
false
false
false
+ 0
19232
@@ -153862,6 +173094,7 @@
false
false
false
+ 0
19233
@@ -153870,6 +173103,7 @@
false
false
false
+ 0
19234
@@ -153878,6 +173112,7 @@
false
false
false
+ 0
19235
@@ -153886,6 +173121,7 @@
false
false
false
+ 0
19236
@@ -153894,6 +173130,7 @@
false
false
false
+ 0
19237
@@ -153902,6 +173139,7 @@
false
false
false
+ 0
19238
@@ -153910,6 +173148,7 @@
false
false
false
+ 0
19239
@@ -153918,6 +173157,7 @@
false
false
false
+ 0
19240
@@ -153926,6 +173166,7 @@
false
false
false
+ 0
19241
@@ -153934,6 +173175,7 @@
false
false
false
+ 0
19242
@@ -153942,6 +173184,7 @@
false
false
false
+ 0
19243
@@ -153950,6 +173193,7 @@
false
false
false
+ 0
19244
@@ -153958,6 +173202,7 @@
false
false
false
+ 0
19245
@@ -153966,6 +173211,7 @@
false
false
false
+ 0
19246
@@ -153974,6 +173220,7 @@
false
false
false
+ 0
19247
@@ -153982,6 +173229,7 @@
false
false
false
+ 0
19248
@@ -153990,6 +173238,7 @@
false
false
false
+ 0
19249
@@ -153998,6 +173247,7 @@
false
false
false
+ 0
19250
@@ -154006,6 +173256,7 @@
false
false
false
+ 0
19251
@@ -154014,6 +173265,7 @@
false
false
false
+ 0
19252
@@ -154022,6 +173274,7 @@
false
false
false
+ 0
19253
@@ -154030,6 +173283,7 @@
false
false
false
+ 0
19254
@@ -154038,6 +173292,7 @@
false
false
false
+ 0
19255
@@ -154046,6 +173301,7 @@
false
false
false
+ 0
19256
@@ -154054,6 +173310,7 @@
false
false
false
+ 0
19257
@@ -154062,6 +173319,7 @@
false
false
false
+ 0
19258
@@ -154070,6 +173328,7 @@
false
false
false
+ 0
19259
@@ -154078,6 +173337,7 @@
false
false
false
+ 0
19260
@@ -154086,6 +173346,7 @@
false
false
false
+ 0
19261
@@ -154094,6 +173355,7 @@
false
false
false
+ 0
19262
@@ -154102,6 +173364,7 @@
false
false
false
+ 0
19263
@@ -154110,6 +173373,7 @@
false
false
false
+ 0
19264
@@ -154118,6 +173382,7 @@
false
false
false
+ 0
19265
@@ -154126,6 +173391,7 @@
false
false
false
+ 0
19266
@@ -154134,6 +173400,7 @@
false
false
false
+ 0
19267
@@ -154142,6 +173409,7 @@
false
false
false
+ 0
19268
@@ -154150,6 +173418,7 @@
false
false
false
+ 0
19269
@@ -154158,6 +173427,7 @@
false
false
false
+ 0
19270
@@ -154166,6 +173436,7 @@
false
false
false
+ 0
19271
@@ -154174,6 +173445,7 @@
false
false
false
+ 0
19272
@@ -154182,6 +173454,7 @@
false
false
false
+ 0
19273
@@ -154190,6 +173463,7 @@
false
false
false
+ 0
19274
@@ -154198,6 +173472,7 @@
false
false
false
+ 0
19275
@@ -154206,6 +173481,7 @@
false
false
false
+ 0
19276
@@ -154214,6 +173490,7 @@
false
false
false
+ 0
19277
@@ -154222,6 +173499,7 @@
false
false
false
+ 0
19278
@@ -154230,6 +173508,7 @@
false
false
false
+ 0
19279
@@ -154238,6 +173517,7 @@
false
false
false
+ 0
19280
@@ -154246,6 +173526,7 @@
false
false
false
+ 0
19281
@@ -154254,6 +173535,7 @@
false
false
false
+ 0
19282
@@ -154262,6 +173544,7 @@
false
false
false
+ 0
19283
@@ -154270,6 +173553,7 @@
false
false
false
+ 0
19284
@@ -154278,6 +173562,7 @@
false
false
false
+ 0
19285
@@ -154286,6 +173571,7 @@
false
false
false
+ 0
19286
@@ -154294,6 +173580,7 @@
false
false
false
+ 0
19287
@@ -154302,6 +173589,7 @@
false
false
false
+ 0
19288
@@ -154310,6 +173598,7 @@
false
false
false
+ 0
19289
@@ -154318,6 +173607,7 @@
false
false
false
+ 0
19290
@@ -154326,6 +173616,7 @@
false
false
false
+ 0
19291
@@ -154334,6 +173625,7 @@
false
false
false
+ 0
19292
@@ -154342,6 +173634,7 @@
false
false
false
+ 0
19293
@@ -154350,6 +173643,7 @@
false
false
false
+ 0
19294
@@ -154358,6 +173652,7 @@
false
false
false
+ 0
19295
@@ -154366,6 +173661,7 @@
false
false
false
+ 0
19296
@@ -154374,6 +173670,7 @@
false
false
false
+ 0
19297
@@ -154382,6 +173679,7 @@
false
false
false
+ 0
19298
@@ -154390,6 +173688,7 @@
false
false
false
+ 0
19299
@@ -154398,6 +173697,7 @@
false
false
false
+ 0
19300
@@ -154406,6 +173706,7 @@
false
false
false
+ 0
19301
@@ -154414,6 +173715,7 @@
false
false
false
+ 0
19302
@@ -154422,6 +173724,7 @@
false
false
false
+ 0
19303
@@ -154430,6 +173733,7 @@
false
false
false
+ 0
19304
@@ -154438,6 +173742,7 @@
false
false
false
+ 0
19305
@@ -154446,6 +173751,7 @@
false
false
false
+ 0
19306
@@ -154454,6 +173760,7 @@
false
false
false
+ 0
19307
@@ -154462,6 +173769,7 @@
false
false
false
+ 0
19308
@@ -154470,6 +173778,7 @@
false
false
false
+ 0
19309
@@ -154478,6 +173787,7 @@
false
false
false
+ 0
19310
@@ -154486,6 +173796,7 @@
false
false
false
+ 0
19311
@@ -154494,6 +173805,7 @@
false
false
false
+ 0
19312
@@ -154502,6 +173814,7 @@
false
false
false
+ 0
19313
@@ -154510,6 +173823,7 @@
false
false
false
+ 0
19314
@@ -154518,6 +173832,7 @@
false
false
false
+ 0
19315
@@ -154526,6 +173841,7 @@
false
false
false
+ 0
19316
@@ -154534,6 +173850,7 @@
false
false
false
+ 0
19317
@@ -154542,6 +173859,7 @@
false
false
false
+ 0
19318
@@ -154550,6 +173868,7 @@
false
false
false
+ 0
19319
@@ -154558,6 +173877,7 @@
false
false
false
+ 0
19320
@@ -154566,6 +173886,7 @@
false
false
false
+ 0
19321
@@ -154574,6 +173895,7 @@
false
false
false
+ 0
19322
@@ -154582,6 +173904,7 @@
false
false
false
+ 0
19323
@@ -154590,6 +173913,7 @@
false
false
false
+ 0
19324
@@ -154598,6 +173922,7 @@
false
false
false
+ 0
19325
@@ -154606,6 +173931,7 @@
false
false
false
+ 0
19326
@@ -154614,6 +173940,7 @@
false
false
false
+ 0
19327
@@ -154622,6 +173949,7 @@
false
false
false
+ 0
19328
@@ -154630,6 +173958,7 @@
false
false
false
+ 0
19329
@@ -154638,6 +173967,7 @@
false
false
false
+ 0
19330
@@ -154646,6 +173976,7 @@
false
false
false
+ 0
19331
@@ -154654,6 +173985,7 @@
false
false
false
+ 0
19332
@@ -154662,6 +173994,7 @@
false
false
false
+ 0
19333
@@ -154670,6 +174003,7 @@
false
false
false
+ 0
19334
@@ -154678,6 +174012,7 @@
false
false
false
+ 0
19335
@@ -154686,6 +174021,7 @@
false
false
false
+ 0
19336
@@ -154694,6 +174030,7 @@
false
false
false
+ 0
19337
@@ -154702,6 +174039,7 @@
false
false
false
+ 0
19338
@@ -154710,6 +174048,7 @@
false
false
false
+ 0
19339
@@ -154718,6 +174057,7 @@
false
false
false
+ 0
19340
@@ -154726,6 +174066,7 @@
false
false
false
+ 0
19341
@@ -154734,6 +174075,7 @@
false
false
false
+ 0
19342
@@ -154742,6 +174084,7 @@
false
false
false
+ 0
19343
@@ -154750,6 +174093,7 @@
false
false
false
+ 0
19344
@@ -154758,6 +174102,7 @@
false
false
false
+ 0
19345
@@ -154766,6 +174111,7 @@
false
false
false
+ 0
19346
@@ -154774,6 +174120,7 @@
false
false
false
+ 0
19347
@@ -154782,6 +174129,7 @@
false
false
false
+ 0
19348
@@ -154790,6 +174138,7 @@
false
false
false
+ 0
19349
@@ -154798,6 +174147,7 @@
false
false
false
+ 0
19350
@@ -154806,6 +174156,7 @@
false
false
false
+ 0
19351
@@ -154814,6 +174165,7 @@
false
false
false
+ 0
19352
@@ -154822,6 +174174,7 @@
false
false
false
+ 0
19353
@@ -154830,6 +174183,7 @@
false
false
false
+ 0
19354
@@ -154838,6 +174192,7 @@
false
false
false
+ 0
19355
@@ -154846,6 +174201,7 @@
false
false
false
+ 0
19356
@@ -154854,6 +174210,7 @@
false
false
false
+ 0
19357
@@ -154862,6 +174219,7 @@
false
false
false
+ 0
19358
@@ -154870,6 +174228,7 @@
false
false
false
+ 0
19359
@@ -154878,6 +174237,7 @@
false
false
false
+ 0
19360
@@ -154886,6 +174246,7 @@
false
false
false
+ 0
19361
@@ -154894,6 +174255,7 @@
false
false
false
+ 0
19362
@@ -154902,6 +174264,7 @@
false
false
false
+ 0
19363
@@ -154910,6 +174273,7 @@
false
false
false
+ 0
19364
@@ -154918,6 +174282,7 @@
false
false
false
+ 0
19365
@@ -154926,6 +174291,7 @@
false
false
false
+ 0
19366
@@ -154934,6 +174300,7 @@
false
false
false
+ 0
19367
@@ -154942,6 +174309,7 @@
false
false
false
+ 0
19368
@@ -154950,6 +174318,7 @@
false
false
false
+ 0
19369
@@ -154958,6 +174327,7 @@
false
false
false
+ 0
19370
@@ -154966,6 +174336,7 @@
false
false
false
+ 0
19371
@@ -154974,6 +174345,7 @@
false
false
false
+ 0
19372
@@ -154982,6 +174354,7 @@
false
false
false
+ 0
19373
@@ -154990,6 +174363,7 @@
false
false
false
+ 0
19374
@@ -154998,6 +174372,7 @@
false
false
false
+ 0
19375
@@ -155006,6 +174381,7 @@
false
false
false
+ 0
19376
@@ -155014,6 +174390,7 @@
false
false
false
+ 0
19377
@@ -155022,6 +174399,7 @@
false
false
false
+ 0
19378
@@ -155030,6 +174408,7 @@
false
false
false
+ 0
19379
@@ -155038,6 +174417,7 @@
false
false
false
+ 0
19380
@@ -155046,6 +174426,7 @@
false
false
false
+ 0
19381
@@ -155054,6 +174435,7 @@
false
false
false
+ 0
19382
@@ -155062,6 +174444,7 @@
false
false
false
+ 0
19383
@@ -155070,6 +174453,7 @@
false
false
false
+ 0
19384
@@ -155078,6 +174462,7 @@
false
false
false
+ 0
19385
@@ -155086,6 +174471,7 @@
false
false
false
+ 0
19386
@@ -155094,6 +174480,7 @@
false
false
false
+ 0
19387
@@ -155102,6 +174489,7 @@
false
false
false
+ 0
19388
@@ -155110,6 +174498,7 @@
false
false
false
+ 0
19389
@@ -155118,6 +174507,7 @@
false
false
false
+ 0
19390
@@ -155126,6 +174516,7 @@
false
false
false
+ 0
19391
@@ -155134,6 +174525,7 @@
false
false
false
+ 0
19392
@@ -155142,6 +174534,7 @@
false
false
false
+ 0
19393
@@ -155150,6 +174543,7 @@
false
false
false
+ 0
19394
@@ -155158,6 +174552,7 @@
false
false
false
+ 0
19395
@@ -155166,6 +174561,7 @@
false
false
false
+ 0
19396
@@ -155174,6 +174570,7 @@
false
false
false
+ 0
19397
@@ -155182,6 +174579,7 @@
false
false
false
+ 0
19398
@@ -155190,6 +174588,7 @@
false
false
false
+ 0
19399
@@ -155198,6 +174597,7 @@
false
false
false
+ 0
19400
@@ -155206,6 +174606,7 @@
false
false
false
+ 0
19401
@@ -155214,6 +174615,7 @@
false
false
false
+ 0
19402
@@ -155222,6 +174624,7 @@
false
false
false
+ 0
19403
@@ -155230,6 +174633,7 @@
false
false
false
+ 0
19404
@@ -155238,6 +174642,7 @@
false
false
false
+ 0
19405
@@ -155246,6 +174651,7 @@
false
false
false
+ 0
19406
@@ -155254,6 +174660,7 @@
false
false
false
+ 0
19407
@@ -155262,6 +174669,7 @@
false
false
false
+ 0
19408
@@ -155270,6 +174678,7 @@
false
false
false
+ 0
19409
@@ -155278,6 +174687,7 @@
false
false
false
+ 0
19410
@@ -155286,6 +174696,7 @@
false
false
false
+ 0
19411
@@ -155294,6 +174705,7 @@
false
false
false
+ 0
19412
@@ -155302,6 +174714,7 @@
false
false
false
+ 0
19413
@@ -155310,6 +174723,7 @@
false
false
false
+ 0
19414
@@ -155318,6 +174732,7 @@
false
false
false
+ 0
19415
@@ -155326,6 +174741,7 @@
false
false
false
+ 0
19416
@@ -155334,6 +174750,7 @@
false
false
false
+ 0
19417
@@ -155342,6 +174759,7 @@
false
false
false
+ 0
19418
@@ -155350,6 +174768,7 @@
false
false
false
+ 0
19419
@@ -155358,6 +174777,7 @@
false
false
false
+ 0
19420
@@ -155366,6 +174786,7 @@
false
false
false
+ 0
19421
@@ -155374,6 +174795,7 @@
false
false
false
+ 0
19422
@@ -155382,6 +174804,7 @@
false
false
false
+ 0
19423
@@ -155390,6 +174813,7 @@
false
false
false
+ 0
19424
@@ -155398,6 +174822,7 @@
false
false
false
+ 0
19425
@@ -155406,6 +174831,7 @@
false
false
false
+ 0
19426
@@ -155414,6 +174840,7 @@
false
false
false
+ 0
19427
@@ -155422,6 +174849,7 @@
false
false
false
+ 0
19428
@@ -155430,6 +174858,7 @@
false
false
false
+ 0
19429
@@ -155438,6 +174867,7 @@
false
false
false
+ 0
19430
@@ -155446,6 +174876,7 @@
false
false
false
+ 0
19431
@@ -155454,6 +174885,7 @@
false
false
false
+ 0
19432
@@ -155462,6 +174894,7 @@
false
false
false
+ 0
19433
@@ -155470,6 +174903,7 @@
false
false
false
+ 0
19434
@@ -155478,6 +174912,7 @@
false
false
false
+ 0
19435
@@ -155486,6 +174921,7 @@
false
false
false
+ 0
19436
@@ -155494,6 +174930,7 @@
false
false
false
+ 0
19437
@@ -155502,6 +174939,7 @@
false
false
false
+ 0
19438
@@ -155510,6 +174948,7 @@
false
false
false
+ 0
19439
@@ -155518,6 +174957,7 @@
false
false
false
+ 0
19440
@@ -155526,6 +174966,7 @@
false
false
false
+ 0
19441
@@ -155534,6 +174975,7 @@
false
false
false
+ 0
19442
@@ -155542,6 +174984,7 @@
false
false
false
+ 0
19443
@@ -155550,6 +174993,7 @@
false
false
false
+ 0
19444
@@ -155558,6 +175002,7 @@
false
false
false
+ 0
19445
@@ -155566,6 +175011,7 @@
false
false
false
+ 0
19446
@@ -155574,6 +175020,7 @@
false
false
false
+ 0
19447
@@ -155582,6 +175029,7 @@
false
false
false
+ 0
19448
@@ -155590,6 +175038,7 @@
false
false
false
+ 0
19449
@@ -155598,6 +175047,7 @@
false
false
false
+ 0
19450
@@ -155606,6 +175056,7 @@
false
false
false
+ 0
19451
@@ -155614,6 +175065,7 @@
false
false
false
+ 0
19452
@@ -155622,6 +175074,7 @@
false
false
false
+ 0
19453
@@ -155630,6 +175083,7 @@
false
false
false
+ 0
19454
@@ -155638,6 +175092,7 @@
false
false
false
+ 0
19455
@@ -155646,6 +175101,7 @@
false
false
false
+ 0
19456
@@ -155654,6 +175110,7 @@
false
false
false
+ 0
19457
@@ -155662,6 +175119,7 @@
false
false
false
+ 0
19458
@@ -155670,6 +175128,7 @@
false
false
false
+ 0
19459
@@ -155678,6 +175137,7 @@
false
false
false
+ 0
19460
@@ -155686,6 +175146,7 @@
false
false
false
+ 0
19461
@@ -155694,6 +175155,7 @@
false
false
false
+ 0
19462
@@ -155702,6 +175164,7 @@
false
false
false
+ 0
19463
@@ -155710,6 +175173,7 @@
false
false
false
+ 0
19464
@@ -155718,6 +175182,7 @@
false
false
false
+ 0
19465
@@ -155726,6 +175191,7 @@
false
false
false
+ 0
19466
@@ -155734,6 +175200,7 @@
false
false
false
+ 0
19467
@@ -155742,6 +175209,7 @@
false
false
false
+ 0
19468
@@ -155750,6 +175218,7 @@
false
false
false
+ 0
19469
@@ -155758,6 +175227,7 @@
false
false
false
+ 0
19470
@@ -155766,6 +175236,7 @@
false
false
false
+ 0
19471
@@ -155774,6 +175245,7 @@
false
false
false
+ 0
19472
@@ -155782,6 +175254,7 @@
false
false
false
+ 0
19473
@@ -155790,6 +175263,7 @@
false
false
false
+ 0
19474
@@ -155798,6 +175272,7 @@
false
false
false
+ 0
19475
@@ -155806,6 +175281,7 @@
false
false
false
+ 0
19476
@@ -155814,6 +175290,7 @@
false
false
false
+ 0
19477
@@ -155822,6 +175299,7 @@
false
false
false
+ 0
19478
@@ -155830,6 +175308,7 @@
false
false
false
+ 0
19479
@@ -155838,6 +175317,7 @@
false
false
false
+ 0
19480
@@ -155846,6 +175326,7 @@
false
false
false
+ 0
19481
@@ -155854,6 +175335,7 @@
false
false
false
+ 0
19482
@@ -155862,6 +175344,7 @@
false
false
false
+ 0
19483
@@ -155870,6 +175353,7 @@
false
false
false
+ 0
19484
@@ -155878,6 +175362,7 @@
false
false
false
+ 0
19485
@@ -155886,6 +175371,7 @@
false
false
false
+ 0
19486
@@ -155894,6 +175380,7 @@
false
false
false
+ 0
19487
@@ -155902,6 +175389,7 @@
false
false
false
+ 0
19488
@@ -155910,6 +175398,7 @@
false
false
false
+ 0
19489
@@ -155918,6 +175407,7 @@
false
false
false
+ 0
19490
@@ -155926,6 +175416,7 @@
false
false
false
+ 0
19491
@@ -155934,6 +175425,7 @@
false
false
false
+ 0
19492
@@ -155942,6 +175434,7 @@
false
false
false
+ 0
19493
@@ -155950,6 +175443,7 @@
false
false
false
+ 0
19494
@@ -155958,6 +175452,7 @@
false
false
false
+ 0
19495
@@ -155966,6 +175461,7 @@
false
false
false
+ 0
19496
@@ -155974,6 +175470,7 @@
false
false
false
+ 0
19497
@@ -155982,6 +175479,7 @@
false
false
false
+ 0
19498
@@ -155990,6 +175488,7 @@
false
false
false
+ 0
19499
@@ -155998,6 +175497,7 @@
false
false
false
+ 0
19500
@@ -156006,6 +175506,7 @@
false
false
false
+ 0
19501
@@ -156014,6 +175515,7 @@
false
false
false
+ 0
19502
@@ -156022,6 +175524,7 @@
false
false
false
+ 0
19503
@@ -156030,6 +175533,7 @@
false
false
false
+ 0
19504
@@ -156038,6 +175542,7 @@
false
false
false
+ 0
19505
@@ -156046,6 +175551,7 @@
false
false
false
+ 0
19506
@@ -156054,6 +175560,7 @@
false
false
false
+ 0
19507
@@ -156062,6 +175569,7 @@
false
false
false
+ 0
19508
@@ -156070,6 +175578,7 @@
false
false
false
+ 0
19509
@@ -156078,6 +175587,7 @@
false
false
false
+ 0
19510
@@ -156086,6 +175596,7 @@
false
false
false
+ 0
19511
@@ -156094,6 +175605,7 @@
false
false
false
+ 0
19512
@@ -156102,6 +175614,7 @@
false
false
false
+ 0
19513
@@ -156110,6 +175623,7 @@
false
false
false
+ 0
19514
@@ -156118,6 +175632,7 @@
false
false
false
+ 0
19515
@@ -156126,6 +175641,7 @@
false
false
false
+ 0
19516
@@ -156134,6 +175650,7 @@
false
false
false
+ 0
19517
@@ -156142,6 +175659,7 @@
false
false
false
+ 0
19518
@@ -156150,6 +175668,7 @@
false
false
false
+ 0
19519
@@ -156158,6 +175677,7 @@
false
false
false
+ 0
19520
@@ -156166,6 +175686,7 @@
false
false
false
+ 0
19521
@@ -156174,6 +175695,7 @@
false
false
false
+ 0
19522
@@ -156182,6 +175704,7 @@
false
false
false
+ 0
19523
@@ -156190,6 +175713,7 @@
false
false
false
+ 0
19524
@@ -156198,6 +175722,7 @@
false
false
false
+ 0
19525
@@ -156206,6 +175731,7 @@
false
false
false
+ 0
19526
@@ -156214,6 +175740,7 @@
false
false
false
+ 0
19527
@@ -156222,6 +175749,7 @@
false
false
false
+ 0
19528
@@ -156230,6 +175758,7 @@
false
false
false
+ 0
19529
@@ -156238,6 +175767,7 @@
false
false
false
+ 0
19530
@@ -156246,6 +175776,7 @@
false
false
false
+ 0
19531
@@ -156254,6 +175785,7 @@
false
false
false
+ 0
19532
@@ -156262,6 +175794,7 @@
false
false
false
+ 0
19533
@@ -156270,6 +175803,7 @@
false
false
false
+ 0
19534
@@ -156278,6 +175812,7 @@
false
false
false
+ 0
19535
@@ -156286,6 +175821,7 @@
false
false
false
+ 0
19536
@@ -156294,6 +175830,7 @@
false
false
false
+ 0
19537
@@ -156302,6 +175839,7 @@
false
false
false
+ 0
19538
@@ -156310,6 +175848,7 @@
false
false
false
+ 0
19539
@@ -156318,6 +175857,7 @@
false
false
false
+ 0
19540
@@ -156326,6 +175866,7 @@
false
false
false
+ 0
19541
@@ -156334,6 +175875,7 @@
false
false
false
+ 0
19542
@@ -156342,6 +175884,7 @@
false
false
false
+ 0
19543
@@ -156350,6 +175893,7 @@
false
false
false
+ 0
19544
@@ -156358,6 +175902,7 @@
false
false
false
+ 0
19545
@@ -156366,6 +175911,7 @@
false
false
false
+ 0
19546
@@ -156374,6 +175920,7 @@
false
false
false
+ 0
19547
@@ -156382,6 +175929,7 @@
false
false
false
+ 0
19548
@@ -156390,6 +175938,7 @@
false
false
false
+ 0
19549
@@ -156398,6 +175947,7 @@
false
false
false
+ 0
19550
@@ -156406,6 +175956,7 @@
false
false
false
+ 0
19551
@@ -156414,6 +175965,7 @@
false
false
false
+ 0
19552
@@ -156422,6 +175974,7 @@
false
false
false
+ 0
19553
@@ -156430,6 +175983,7 @@
false
false
false
+ 0
19554
@@ -156438,6 +175992,7 @@
false
false
false
+ 0
19555
@@ -156446,6 +176001,7 @@
false
false
false
+ 0
19556
@@ -156454,6 +176010,7 @@
false
false
false
+ 0
19557
@@ -156462,6 +176019,7 @@
false
false
false
+ 0
19558
@@ -156470,6 +176028,7 @@
false
false
false
+ 0
19559
@@ -156478,6 +176037,7 @@
false
false
false
+ 0
19560
@@ -156486,6 +176046,7 @@
false
false
false
+ 0
19561
@@ -156494,6 +176055,7 @@
false
false
false
+ 0
19562
@@ -156502,6 +176064,7 @@
false
false
false
+ 0
19563
@@ -156510,6 +176073,7 @@
false
false
false
+ 0
19564
@@ -156518,6 +176082,7 @@
false
false
false
+ 0
19565
@@ -156526,6 +176091,7 @@
false
false
false
+ 0
19566
@@ -156534,6 +176100,7 @@
false
false
false
+ 0
19567
@@ -156542,6 +176109,7 @@
false
false
false
+ 0
19568
@@ -156550,6 +176118,7 @@
false
false
false
+ 0
19569
@@ -156558,6 +176127,7 @@
false
false
false
+ 0
19570
@@ -156566,6 +176136,7 @@
false
false
false
+ 0
19571
@@ -156574,6 +176145,7 @@
false
false
false
+ 0
19572
@@ -156582,6 +176154,7 @@
false
false
false
+ 0
19573
@@ -156590,6 +176163,7 @@
false
false
false
+ 0
19574
@@ -156598,6 +176172,7 @@
false
false
false
+ 0
19575
@@ -156606,6 +176181,7 @@
false
false
false
+ 0
19576
@@ -156614,6 +176190,7 @@
false
false
false
+ 0
19577
@@ -156622,6 +176199,7 @@
false
false
false
+ 0
19578
@@ -156630,6 +176208,7 @@
false
false
false
+ 0
19579
@@ -156638,6 +176217,7 @@
false
false
false
+ 0
19580
@@ -156646,6 +176226,7 @@
false
false
false
+ 0
19581
@@ -156654,6 +176235,7 @@
false
false
false
+ 0
19582
@@ -156662,6 +176244,7 @@
false
false
false
+ 0
19583
@@ -156670,6 +176253,7 @@
false
false
false
+ 0
19584
@@ -156678,6 +176262,7 @@
false
false
false
+ 0
19585
@@ -156686,6 +176271,7 @@
false
false
false
+ 0
19586
@@ -156694,6 +176280,7 @@
false
false
false
+ 0
19587
@@ -156702,6 +176289,7 @@
false
false
false
+ 0
19588
@@ -156710,6 +176298,7 @@
false
false
false
+ 0
19589
@@ -156718,6 +176307,7 @@
false
false
false
+ 0
19590
@@ -156726,6 +176316,7 @@
false
false
false
+ 0
19591
@@ -156734,6 +176325,7 @@
false
false
false
+ 0
19592
@@ -156742,6 +176334,7 @@
false
false
false
+ 0
19593
@@ -156750,6 +176343,7 @@
false
false
false
+ 0
19594
@@ -156758,6 +176352,7 @@
false
false
false
+ 0
19595
@@ -156766,6 +176361,7 @@
false
false
false
+ 0
19596
@@ -156774,6 +176370,7 @@
false
false
false
+ 0
19597
@@ -156782,6 +176379,7 @@
false
false
false
+ 0
19598
@@ -156790,6 +176388,7 @@
false
false
false
+ 0
19599
@@ -156798,6 +176397,7 @@
false
false
false
+ 0
19600
@@ -156806,6 +176406,7 @@
false
false
false
+ 0
19601
@@ -156814,6 +176415,7 @@
false
false
false
+ 0
19602
@@ -156822,6 +176424,7 @@
false
false
false
+ 0
19603
@@ -156830,6 +176433,7 @@
false
false
false
+ 0
19604
@@ -156838,6 +176442,7 @@
false
false
false
+ 0
19605
@@ -156846,6 +176451,7 @@
false
false
false
+ 0
19606
@@ -156854,6 +176460,7 @@
false
false
false
+ 0
19607
@@ -156862,6 +176469,7 @@
false
false
false
+ 0
19608
@@ -156870,6 +176478,7 @@
false
false
false
+ 0
19609
@@ -156878,6 +176487,7 @@
false
false
false
+ 0
19610
@@ -156886,6 +176496,7 @@
false
false
false
+ 0
19611
@@ -156894,6 +176505,7 @@
false
false
false
+ 0
19612
@@ -156902,6 +176514,7 @@
false
false
false
+ 0
19613
@@ -156910,6 +176523,7 @@
false
false
false
+ 0
19614
@@ -156918,6 +176532,7 @@
false
false
false
+ 0
19615
@@ -156926,6 +176541,7 @@
false
false
false
+ 0
19616
@@ -156934,6 +176550,7 @@
false
false
false
+ 0
19617
@@ -156942,6 +176559,7 @@
false
false
false
+ 0
19618
@@ -156950,6 +176568,7 @@
false
false
false
+ 0
19619
@@ -156958,6 +176577,7 @@
false
false
false
+ 0
19620
@@ -156966,6 +176586,7 @@
false
false
false
+ 0
19621
@@ -156974,6 +176595,7 @@
false
false
false
+ 0
19622
@@ -156982,6 +176604,7 @@
false
false
false
+ 0
19623
@@ -156990,6 +176613,7 @@
false
false
false
+ 0
19624
@@ -156998,6 +176622,7 @@
false
false
false
+ 0
19625
@@ -157006,6 +176631,7 @@
false
false
false
+ 0
19626
@@ -157014,6 +176640,7 @@
false
false
false
+ 0
19627
@@ -157022,6 +176649,7 @@
false
false
false
+ 0
19628
@@ -157030,6 +176658,7 @@
false
false
false
+ 0
19629
@@ -157038,6 +176667,7 @@
false
false
false
+ 0
19630
@@ -157046,6 +176676,7 @@
false
false
false
+ 0
19631
@@ -157054,6 +176685,7 @@
false
false
false
+ 0
19632
@@ -157062,6 +176694,7 @@
false
false
false
+ 0
19633
@@ -157070,6 +176703,7 @@
false
false
false
+ 0
19634
@@ -157078,6 +176712,7 @@
false
false
false
+ 0
19635
@@ -157086,6 +176721,7 @@
false
false
false
+ 0
19636
@@ -157094,6 +176730,7 @@
false
false
false
+ 0
19637
@@ -157102,6 +176739,7 @@
false
false
false
+ 0
19638
@@ -157110,6 +176748,7 @@
false
false
false
+ 0
19639
@@ -157118,6 +176757,7 @@
false
false
false
+ 0
19640
@@ -157126,6 +176766,7 @@
false
false
false
+ 0
19641
@@ -157134,6 +176775,7 @@
false
false
false
+ 0
19642
@@ -157142,6 +176784,7 @@
false
false
false
+ 0
19643
@@ -157150,6 +176793,7 @@
false
false
false
+ 0
19644
@@ -157158,6 +176802,7 @@
false
false
false
+ 0
19645
@@ -157166,6 +176811,7 @@
false
false
false
+ 0
19646
@@ -157174,6 +176820,7 @@
false
false
false
+ 0
19647
@@ -157182,6 +176829,7 @@
false
false
false
+ 0
19648
@@ -157190,6 +176838,7 @@
false
false
false
+ 0
19649
@@ -157198,6 +176847,7 @@
false
false
false
+ 0
19650
@@ -157206,6 +176856,7 @@
false
false
false
+ 0
19651
@@ -157214,6 +176865,7 @@
false
false
false
+ 0
19652
@@ -157222,6 +176874,7 @@
false
false
false
+ 0
19653
@@ -157230,6 +176883,7 @@
false
false
false
+ 0
19654
@@ -157238,6 +176892,7 @@
false
false
false
+ 0
19655
@@ -157246,6 +176901,7 @@
false
false
false
+ 0
19656
@@ -157254,6 +176910,7 @@
false
false
false
+ 0
19657
@@ -157262,6 +176919,7 @@
false
false
false
+ 0
19658
@@ -157270,6 +176928,7 @@
false
false
false
+ 0
19659
@@ -157278,6 +176937,7 @@
false
false
false
+ 0
19660
@@ -157286,6 +176946,7 @@
false
false
false
+ 0
19661
@@ -157294,6 +176955,7 @@
false
false
false
+ 0
19662
@@ -157302,6 +176964,7 @@
false
false
false
+ 0
19663
@@ -157310,6 +176973,7 @@
false
false
false
+ 0
19664
@@ -157318,6 +176982,7 @@
false
false
false
+ 0
19665
@@ -157326,6 +176991,7 @@
false
false
false
+ 0
19666
@@ -157334,6 +177000,7 @@
false
false
false
+ 0
19667
@@ -157342,6 +177009,7 @@
false
false
false
+ 0
19668
@@ -157350,6 +177018,7 @@
false
false
false
+ 0
19669
@@ -157358,6 +177027,7 @@
false
false
false
+ 0
19670
@@ -157366,6 +177036,7 @@
false
false
false
+ 0
19671
@@ -157374,6 +177045,7 @@
false
false
false
+ 0
19672
@@ -157382,6 +177054,7 @@
false
false
false
+ 0
19673
@@ -157390,6 +177063,7 @@
false
false
false
+ 0
19674
@@ -157398,6 +177072,7 @@
false
false
false
+ 0
19675
@@ -157406,6 +177081,7 @@
false
false
false
+ 0
19676
@@ -157414,6 +177090,7 @@
false
false
false
+ 0
19677
@@ -157422,6 +177099,7 @@
false
false
false
+ 0
19678
@@ -157430,6 +177108,7 @@
false
false
false
+ 0
19679
@@ -157438,6 +177117,7 @@
false
false
false
+ 0
19680
@@ -157446,6 +177126,7 @@
false
false
false
+ 0
19681
@@ -157454,6 +177135,7 @@
false
false
false
+ 0
19682
@@ -157462,6 +177144,7 @@
false
false
false
+ 0
19683
@@ -157470,6 +177153,7 @@
false
false
false
+ 0
19684
@@ -157478,6 +177162,7 @@
false
false
false
+ 0
19685
@@ -157486,6 +177171,7 @@
false
false
false
+ 0
19686
@@ -157494,6 +177180,7 @@
false
false
false
+ 0
19687
@@ -157502,6 +177189,7 @@
false
false
false
+ 0
19688
@@ -157510,6 +177198,7 @@
false
false
false
+ 0
19689
@@ -157518,6 +177207,7 @@
false
false
false
+ 0
19690
@@ -157526,6 +177216,7 @@
false
false
false
+ 0
19691
@@ -157534,6 +177225,7 @@
false
false
false
+ 0
19692
@@ -157542,6 +177234,7 @@
false
false
false
+ 0
19693
@@ -157550,6 +177243,7 @@
false
false
false
+ 0
19694
@@ -157558,6 +177252,7 @@
false
false
false
+ 0
19695
@@ -157566,6 +177261,7 @@
false
false
false
+ 0
19696
@@ -157574,6 +177270,7 @@
false
false
false
+ 0
19697
@@ -157582,6 +177279,7 @@
false
false
false
+ 0
19698
@@ -157590,6 +177288,7 @@
false
false
false
+ 0
19699
@@ -157598,6 +177297,7 @@
false
false
false
+ 0
19700
@@ -157606,6 +177306,7 @@
false
false
false
+ 0
19701
@@ -157614,6 +177315,7 @@
false
false
false
+ 0
19702
@@ -157622,6 +177324,7 @@
false
false
false
+ 0
19703
@@ -157630,6 +177333,7 @@
false
false
false
+ 0
19704
@@ -157638,6 +177342,7 @@
false
false
false
+ 0
19705
@@ -157646,6 +177351,7 @@
false
false
false
+ 0
19706
@@ -157654,6 +177360,7 @@
false
false
false
+ 0
19707
@@ -157662,6 +177369,7 @@
false
false
false
+ 0
19708
@@ -157670,6 +177378,7 @@
false
false
false
+ 0
19709
@@ -157678,6 +177387,7 @@
false
false
false
+ 0
19710
@@ -157686,6 +177396,7 @@
false
false
false
+ 0
19711
@@ -157694,6 +177405,7 @@
false
false
false
+ 0
19712
@@ -157702,6 +177414,7 @@
false
false
false
+ 0
19713
@@ -157710,6 +177423,7 @@
false
false
false
+ 0
19714
@@ -157718,6 +177432,7 @@
false
false
false
+ 0
19715
@@ -157726,6 +177441,7 @@
false
false
false
+ 0
19716
@@ -157734,6 +177450,7 @@
false
false
false
+ 0
19717
@@ -157742,6 +177459,7 @@
false
false
false
+ 0
19718
@@ -157750,6 +177468,7 @@
false
false
false
+ 0
19719
@@ -157758,6 +177477,7 @@
false
false
false
+ 0
19720
@@ -157766,6 +177486,7 @@
false
false
false
+ 0
19721
@@ -157774,6 +177495,7 @@
false
false
false
+ 0
19722
@@ -157782,6 +177504,7 @@
false
false
false
+ 0
19723
@@ -157790,6 +177513,7 @@
false
false
false
+ 0
19724
@@ -157798,6 +177522,7 @@
false
false
false
+ 0
19725
@@ -157806,6 +177531,7 @@
false
false
false
+ 0
19726
@@ -157814,6 +177540,7 @@
false
false
false
+ 0
19727
@@ -157822,6 +177549,7 @@
false
false
false
+ 0
19728
@@ -157830,6 +177558,7 @@
false
false
false
+ 0
19729
@@ -157838,6 +177567,7 @@
false
false
false
+ 0
19730
@@ -157846,6 +177576,7 @@
false
false
false
+ 0
19731
@@ -157854,6 +177585,7 @@
false
false
false
+ 0
19732
@@ -157862,6 +177594,7 @@
false
false
false
+ 0
19733
@@ -157870,6 +177603,7 @@
false
false
false
+ 0
19734
@@ -157878,6 +177612,7 @@
false
false
false
+ 0
19735
@@ -157886,6 +177621,7 @@
false
false
false
+ 0
19736
@@ -157894,6 +177630,7 @@
false
false
false
+ 0
19737
@@ -157902,6 +177639,7 @@
false
false
false
+ 0
19738
@@ -157910,6 +177648,7 @@
false
false
false
+ 0
19739
@@ -157918,6 +177657,7 @@
false
false
false
+ 0
19740
@@ -157926,6 +177666,7 @@
false
false
false
+ 0
19741
@@ -157934,6 +177675,7 @@
false
false
false
+ 0
19742
@@ -157942,6 +177684,7 @@
false
false
false
+ 0
19743
@@ -157950,6 +177693,7 @@
false
false
false
+ 0
19744
@@ -157958,6 +177702,7 @@
false
false
false
+ 0
19745
@@ -157966,6 +177711,7 @@
false
false
false
+ 0
19746
@@ -157974,6 +177720,7 @@
false
false
false
+ 0
19747
@@ -157982,6 +177729,7 @@
false
false
false
+ 0
19748
@@ -157990,6 +177738,7 @@
false
false
false
+ 0
19749
@@ -157998,6 +177747,7 @@
false
false
false
+ 0
19750
@@ -158006,6 +177756,7 @@
false
false
false
+ 0
19751
@@ -158014,6 +177765,7 @@
false
false
false
+ 0
19752
@@ -158022,6 +177774,7 @@
false
false
false
+ 0
19753
@@ -158030,6 +177783,7 @@
false
false
false
+ 0
19754
@@ -158038,6 +177792,7 @@
false
false
false
+ 0
19755
@@ -158046,6 +177801,7 @@
false
false
false
+ 0
19756
@@ -158054,6 +177810,7 @@
false
false
false
+ 0
19757
@@ -158062,6 +177819,7 @@
false
false
false
+ 0
19758
@@ -158070,6 +177828,7 @@
false
false
false
+ 0
19759
@@ -158078,6 +177837,7 @@
false
false
false
+ 0
19760
@@ -158086,6 +177846,7 @@
false
false
false
+ 0
19761
@@ -158094,6 +177855,7 @@
false
false
false
+ 0
19762
@@ -158102,6 +177864,7 @@
false
false
false
+ 0
19763
@@ -158110,6 +177873,7 @@
false
false
false
+ 0
19764
@@ -158118,6 +177882,7 @@
false
false
false
+ 0
19765
@@ -158126,6 +177891,7 @@
false
false
false
+ 0
19766
@@ -158134,6 +177900,7 @@
false
false
false
+ 0
19767
@@ -158142,6 +177909,7 @@
false
false
false
+ 0
19768
@@ -158150,6 +177918,7 @@
false
false
false
+ 0
19769
@@ -158158,6 +177927,7 @@
false
false
false
+ 0
19770
@@ -158166,6 +177936,7 @@
false
false
false
+ 0
19771
@@ -158174,6 +177945,7 @@
false
false
false
+ 0
19772
@@ -158182,6 +177954,7 @@
false
false
false
+ 0
19773
@@ -158190,6 +177963,7 @@
false
false
false
+ 0
19774
@@ -158198,6 +177972,7 @@
false
false
false
+ 0
19775
@@ -158206,6 +177981,7 @@
false
false
false
+ 0
19776
@@ -158214,6 +177990,7 @@
false
false
false
+ 0
19777
@@ -158222,6 +177999,7 @@
false
false
false
+ 0
19778
@@ -158230,6 +178008,7 @@
false
false
false
+ 0
19779
@@ -158238,6 +178017,7 @@
false
false
false
+ 0
19780
@@ -158246,6 +178026,7 @@
false
false
false
+ 0
19781
@@ -158254,6 +178035,7 @@
false
false
false
+ 0
19782
@@ -158262,6 +178044,7 @@
false
false
false
+ 0
19783
@@ -158270,6 +178053,7 @@
false
false
false
+ 0
19784
@@ -158278,6 +178062,7 @@
false
false
false
+ 0
19785
@@ -158286,6 +178071,7 @@
false
false
false
+ 0
19786
@@ -158294,6 +178080,7 @@
false
false
false
+ 0
19787
@@ -158302,6 +178089,7 @@
false
false
false
+ 0
19788
@@ -158310,6 +178098,7 @@
false
false
false
+ 0
19789
@@ -158318,6 +178107,7 @@
false
false
false
+ 0
19790
@@ -158326,6 +178116,7 @@
false
false
false
+ 0
19791
@@ -158334,6 +178125,7 @@
false
false
false
+ 0
19792
@@ -158342,6 +178134,7 @@
false
false
false
+ 0
19793
@@ -158350,6 +178143,7 @@
false
false
false
+ 0
19794
@@ -158358,6 +178152,7 @@
false
false
false
+ 0
19795
@@ -158366,6 +178161,7 @@
false
false
false
+ 0
19796
@@ -158374,6 +178170,7 @@
false
false
false
+ 0
19797
@@ -158382,6 +178179,7 @@
false
false
false
+ 0
19798
@@ -158390,6 +178188,7 @@
false
false
false
+ 0
19799
@@ -158398,6 +178197,7 @@
false
false
false
+ 0
19800
@@ -158406,6 +178206,7 @@
false
false
false
+ 0
19801
@@ -158414,6 +178215,7 @@
false
false
false
+ 0
19802
@@ -158422,6 +178224,7 @@
false
false
false
+ 0
19803
@@ -158430,6 +178233,7 @@
false
false
false
+ 0
19804
@@ -158438,6 +178242,7 @@
false
false
false
+ 0
19805
@@ -158446,6 +178251,7 @@
false
false
false
+ 0
19806
@@ -158454,6 +178260,7 @@
false
false
false
+ 0
19807
@@ -158462,6 +178269,7 @@
false
false
false
+ 0
19808
@@ -158470,6 +178278,7 @@
false
false
false
+ 0
19809
@@ -158478,6 +178287,7 @@
false
false
false
+ 0
19810
@@ -158486,6 +178296,7 @@
false
false
false
+ 0
19811
@@ -158494,6 +178305,7 @@
false
false
false
+ 0
19812
@@ -158502,6 +178314,7 @@
false
false
false
+ 0
19813
@@ -158510,6 +178323,7 @@
false
false
false
+ 0
19814
@@ -158518,6 +178332,7 @@
false
false
false
+ 0
19815
@@ -158526,6 +178341,7 @@
false
false
false
+ 0
19816
@@ -158534,6 +178350,7 @@
false
false
false
+ 0
19817
@@ -158542,6 +178359,7 @@
false
false
false
+ 0
19818
@@ -158550,6 +178368,7 @@
false
false
false
+ 0
19819
@@ -158558,6 +178377,7 @@
false
false
false
+ 0
19820
@@ -158566,6 +178386,7 @@
false
false
false
+ 0
19821
@@ -158574,6 +178395,7 @@
false
false
false
+ 0
19822
@@ -158582,6 +178404,7 @@
false
false
false
+ 0
19823
@@ -158590,6 +178413,7 @@
false
false
false
+ 0
19824
@@ -158598,6 +178422,7 @@
false
false
false
+ 0
19825
@@ -158606,6 +178431,7 @@
false
false
false
+ 0
19826
@@ -158614,6 +178440,7 @@
false
false
false
+ 0
19827
@@ -158622,6 +178449,7 @@
false
false
false
+ 0
19828
@@ -158630,6 +178458,7 @@
false
false
false
+ 0
19829
@@ -158638,6 +178467,7 @@
false
false
false
+ 0
19830
@@ -158646,6 +178476,7 @@
false
false
false
+ 0
19831
@@ -158654,6 +178485,7 @@
false
false
false
+ 0
19832
@@ -158662,6 +178494,7 @@
false
false
false
+ 0
19833
@@ -158670,6 +178503,7 @@
false
false
false
+ 0
19834
@@ -158678,6 +178512,7 @@
false
false
false
+ 0
19835
@@ -158686,6 +178521,7 @@
false
false
false
+ 0
19836
@@ -158694,6 +178530,7 @@
false
false
false
+ 0
19837
@@ -158702,6 +178539,7 @@
false
false
false
+ 0
19838
@@ -158710,6 +178548,7 @@
false
false
false
+ 0
19839
@@ -158718,6 +178557,7 @@
false
false
false
+ 0
19840
@@ -158726,6 +178566,7 @@
false
false
false
+ 0
19841
@@ -158734,6 +178575,7 @@
false
false
false
+ 0
19842
@@ -158742,6 +178584,7 @@
false
false
false
+ 0
19843
@@ -158750,6 +178593,7 @@
false
false
false
+ 0
19844
@@ -158758,6 +178602,7 @@
false
false
false
+ 0
19845
@@ -158766,6 +178611,7 @@
false
false
false
+ 0
19846
@@ -158774,6 +178620,7 @@
false
false
false
+ 0
19847
@@ -158782,6 +178629,7 @@
false
false
false
+ 0
19848
@@ -158790,6 +178638,7 @@
false
false
false
+ 0
19849
@@ -158798,6 +178647,7 @@
false
false
false
+ 0
19850
@@ -158806,6 +178656,7 @@
false
false
false
+ 0
19851
@@ -158814,6 +178665,7 @@
false
false
false
+ 0
19852
@@ -158822,6 +178674,7 @@
false
false
false
+ 0
19853
@@ -158830,6 +178683,7 @@
false
false
false
+ 0
19854
@@ -158838,6 +178692,7 @@
false
false
false
+ 0
19855
@@ -158846,6 +178701,7 @@
false
false
false
+ 0
19856
@@ -158854,6 +178710,7 @@
false
false
false
+ 0
19857
@@ -158862,6 +178719,7 @@
false
false
false
+ 0
19858
@@ -158870,6 +178728,7 @@
false
false
false
+ 0
19859
@@ -158878,6 +178737,7 @@
false
false
false
+ 0
19860
@@ -158886,6 +178746,7 @@
false
false
false
+ 0
19861
@@ -158894,6 +178755,7 @@
false
false
false
+ 0
19862
@@ -158902,6 +178764,7 @@
false
false
false
+ 0
19863
@@ -158910,6 +178773,7 @@
false
false
false
+ 0
19864
@@ -158918,6 +178782,7 @@
false
false
false
+ 0
19865
@@ -158926,6 +178791,7 @@
false
false
false
+ 0
19866
@@ -158934,6 +178800,7 @@
false
false
false
+ 0
19867
@@ -158942,6 +178809,7 @@
false
false
false
+ 0
19868
@@ -158950,6 +178818,7 @@
false
false
false
+ 0
19869
@@ -158958,6 +178827,7 @@
false
false
false
+ 0
19870
@@ -158966,6 +178836,7 @@
false
false
false
+ 0
19871
@@ -158974,6 +178845,7 @@
false
false
false
+ 0
19872
@@ -158982,6 +178854,7 @@
false
false
false
+ 0
19873
@@ -158990,6 +178863,7 @@
false
false
false
+ 0
19874
@@ -158998,6 +178872,7 @@
false
false
false
+ 0
19875
@@ -159006,6 +178881,7 @@
false
false
false
+ 0
19876
@@ -159014,6 +178890,7 @@
false
false
false
+ 0
19877
@@ -159022,6 +178899,7 @@
false
false
false
+ 0
19878
@@ -159030,6 +178908,7 @@
false
false
false
+ 0
19879
@@ -159038,6 +178917,7 @@
false
false
false
+ 0
19880
@@ -159046,6 +178926,7 @@
false
false
false
+ 0
19881
@@ -159054,6 +178935,7 @@
false
false
false
+ 0
19882
@@ -159062,6 +178944,7 @@
false
false
false
+ 0
19883
@@ -159070,6 +178953,7 @@
false
false
false
+ 0
19884
@@ -159078,6 +178962,7 @@
false
false
false
+ 0
19885
@@ -159086,6 +178971,7 @@
false
false
false
+ 0
19886
@@ -159094,6 +178980,7 @@
false
false
false
+ 0
19887
@@ -159102,6 +178989,7 @@
false
false
false
+ 0
19888
@@ -159110,6 +178998,7 @@
false
false
false
+ 0
19889
@@ -159118,6 +179007,7 @@
false
false
false
+ 0
19890
@@ -159126,6 +179016,7 @@
false
false
false
+ 0
19891
@@ -159134,6 +179025,7 @@
false
false
false
+ 0
19892
@@ -159142,6 +179034,7 @@
false
false
false
+ 0
19893
@@ -159150,6 +179043,7 @@
false
false
false
+ 0
19894
@@ -159158,6 +179052,7 @@
false
false
false
+ 0
19895
@@ -159166,6 +179061,7 @@
false
false
false
+ 0
19896
@@ -159174,6 +179070,7 @@
false
false
false
+ 0
19897
@@ -159182,6 +179079,7 @@
false
false
false
+ 0
19898
@@ -159190,6 +179088,7 @@
false
false
false
+ 0
19899
@@ -159198,6 +179097,7 @@
false
false
false
+ 0
19900
@@ -159206,6 +179106,7 @@
false
false
false
+ 0
19901
@@ -159214,6 +179115,7 @@
false
false
false
+ 0
19902
@@ -159222,6 +179124,7 @@
false
false
false
+ 0
19903
@@ -159230,6 +179133,7 @@
false
false
false
+ 0
19904
@@ -159238,6 +179142,7 @@
false
false
false
+ 0
19905
@@ -159246,6 +179151,7 @@
false
false
false
+ 0
19906
@@ -159254,6 +179160,7 @@
false
false
false
+ 0
19907
@@ -159262,6 +179169,7 @@
false
false
false
+ 0
19908
@@ -159270,6 +179178,7 @@
false
false
false
+ 0
19909
@@ -159278,6 +179187,7 @@
false
false
false
+ 0
19910
@@ -159286,6 +179196,7 @@
false
false
false
+ 0
19911
@@ -159294,6 +179205,7 @@
false
false
false
+ 0
19912
@@ -159302,6 +179214,7 @@
false
false
false
+ 0
19913
@@ -159310,6 +179223,7 @@
false
false
false
+ 0
19914
@@ -159318,6 +179232,7 @@
false
false
false
+ 0
19915
@@ -159326,6 +179241,7 @@
false
false
false
+ 0
19916
@@ -159334,6 +179250,7 @@
false
false
false
+ 0
19917
@@ -159342,6 +179259,7 @@
false
false
false
+ 0
19918
@@ -159350,6 +179268,7 @@
false
false
false
+ 0
19919
@@ -159358,6 +179277,7 @@
false
false
false
+ 0
19920
@@ -159366,6 +179286,7 @@
false
false
false
+ 0
19921
@@ -159374,6 +179295,7 @@
false
false
false
+ 0
19922
@@ -159382,6 +179304,7 @@
false
false
false
+ 0
19923
@@ -159390,6 +179313,7 @@
false
false
false
+ 0
19924
@@ -159398,6 +179322,7 @@
false
false
false
+ 0
19925
@@ -159406,6 +179331,7 @@
false
false
false
+ 0
19926
@@ -159414,6 +179340,7 @@
false
false
false
+ 0
19927
@@ -159422,6 +179349,7 @@
false
false
false
+ 0
19928
@@ -159430,6 +179358,7 @@
false
false
false
+ 0
19929
@@ -159438,6 +179367,7 @@
false
false
false
+ 0
19930
@@ -159446,6 +179376,7 @@
false
false
false
+ 0
19931
@@ -159454,6 +179385,7 @@
false
false
false
+ 0
19932
@@ -159462,6 +179394,7 @@
false
false
false
+ 0
19933
@@ -159470,6 +179403,7 @@
false
false
false
+ 0
19934
@@ -159478,6 +179412,7 @@
false
false
false
+ 0
19935
@@ -159486,6 +179421,7 @@
false
false
false
+ 0
19936
@@ -159494,6 +179430,7 @@
false
false
false
+ 0
19937
@@ -159502,6 +179439,7 @@
false
false
false
+ 0
19938
@@ -159510,6 +179448,7 @@
false
false
false
+ 0
19939
@@ -159518,6 +179457,7 @@
false
false
false
+ 0
19940
@@ -159526,6 +179466,7 @@
false
false
false
+ 0
19941
@@ -159534,6 +179475,7 @@
false
false
false
+ 0
19942
@@ -159542,6 +179484,7 @@
false
false
false
+ 0
19943
@@ -159550,6 +179493,7 @@
false
false
false
+ 0
19944
@@ -159558,6 +179502,7 @@
false
false
false
+ 0
19945
@@ -159566,6 +179511,7 @@
false
false
false
+ 0
19946
@@ -159574,6 +179520,7 @@
false
false
false
+ 0
19947
@@ -159582,6 +179529,7 @@
false
false
false
+ 0
19948
@@ -159590,6 +179538,7 @@
false
false
false
+ 0
19949
@@ -159598,6 +179547,7 @@
false
false
false
+ 0
19950
@@ -159606,6 +179556,7 @@
false
false
false
+ 0
19951
@@ -159614,6 +179565,7 @@
false
false
false
+ 0
19952
@@ -159622,6 +179574,7 @@
false
false
false
+ 0
19953
@@ -159630,6 +179583,7 @@
false
false
false
+ 0
19954
@@ -159638,6 +179592,7 @@
false
false
false
+ 0
19955
@@ -159646,6 +179601,7 @@
false
false
false
+ 0
19956
@@ -159654,6 +179610,7 @@
false
false
false
+ 0
19957
@@ -159662,6 +179619,7 @@
false
false
false
+ 0
19958
@@ -159670,6 +179628,7 @@
false
false
false
+ 0
19959
@@ -159678,6 +179637,7 @@
false
false
false
+ 0
19960
@@ -159686,6 +179646,7 @@
false
false
false
+ 0
19961
@@ -159694,6 +179655,7 @@
false
false
false
+ 0
19962
@@ -159702,6 +179664,7 @@
false
false
false
+ 0
19963
@@ -159710,6 +179673,7 @@
false
false
false
+ 0
19964
@@ -159718,6 +179682,7 @@
false
false
false
+ 0
19965
@@ -159726,6 +179691,7 @@
false
false
false
+ 0
19966
@@ -159734,6 +179700,7 @@
false
false
false
+ 0
19967
@@ -159742,6 +179709,7 @@
false
false
false
+ 0
19968
@@ -159750,6 +179718,7 @@
false
false
false
+ 0
19969
@@ -159758,6 +179727,7 @@
false
false
false
+ 0
19970
@@ -159766,6 +179736,7 @@
false
false
false
+ 0
19971
@@ -159774,6 +179745,7 @@
false
false
false
+ 0
19972
@@ -159782,6 +179754,7 @@
false
false
false
+ 0
19973
@@ -159790,6 +179763,7 @@
false
false
false
+ 0
19974
@@ -159798,6 +179772,7 @@
false
false
false
+ 0
19975
@@ -159806,6 +179781,7 @@
false
false
false
+ 0
19976
@@ -159814,6 +179790,7 @@
false
false
false
+ 0
19977
@@ -159822,6 +179799,7 @@
false
false
false
+ 0
19978
@@ -159830,6 +179808,7 @@
false
false
false
+ 0
19979
@@ -159838,6 +179817,7 @@
false
false
false
+ 0
19980
@@ -159846,6 +179826,7 @@
false
false
false
+ 0
19981
@@ -159854,6 +179835,7 @@
false
false
false
+ 0
19982
@@ -159862,6 +179844,7 @@
false
false
false
+ 0
19983
@@ -159870,6 +179853,7 @@
false
false
false
+ 0
19984
@@ -159878,6 +179862,7 @@
false
false
false
+ 0
19985
@@ -159886,6 +179871,7 @@
false
false
false
+ 0
19986
@@ -159894,6 +179880,7 @@
false
false
false
+ 0
19987
@@ -159902,6 +179889,7 @@
false
false
false
+ 0
19988
@@ -159910,6 +179898,7 @@
false
false
false
+ 0
19989
@@ -159918,6 +179907,7 @@
false
false
false
+ 0
19990
@@ -159926,6 +179916,7 @@
false
false
false
+ 0
19991
@@ -159934,6 +179925,7 @@
false
false
false
+ 0
19992
@@ -159942,6 +179934,7 @@
false
false
false
+ 0
19993
@@ -159950,6 +179943,7 @@
false
false
false
+ 0
19994
@@ -159958,6 +179952,7 @@
false
false
false
+ 0
19995
@@ -159966,6 +179961,7 @@
false
false
false
+ 0
19996
@@ -159974,6 +179970,7 @@
false
false
false
+ 0
19997
@@ -159982,6 +179979,7 @@
false
false
false
+ 0
19998
@@ -159990,6 +179988,7 @@
false
false
false
+ 0
19999
@@ -159998,13 +179997,17 @@
false
false
false
+ 0
20000
class
nominal
+ -1
+ 1
false
false
false
+ 0
-
\ No newline at end of file
+
diff --git a/tests/files/org/openml/test/datasets/-1/qualities.xml b/tests/files/org/openml/test/datasets/-1/qualities.xml
new file mode 100644
index 000000000..32a27a42c
--- /dev/null
+++ b/tests/files/org/openml/test/datasets/-1/qualities.xml
@@ -0,0 +1,80 @@
+
+
+
+ DefaultAccuracy
+ 0.5
+
+
+ Dimensionality
+ 33.335
+
+
+ MajorityClassPercentage
+ 50.0
+
+
+ MajorityClassSize
+ 300.0
+
+
+ MinorityClassPerentage
+ 50.0
+
+
+ MinorityClassSize
+ 300.0
+
+
+ NumberOfBinaryFeatures
+ 1.0
+
+
+ NumberOfClasses
+ 2.0
+
+
+ NumberOfFeatures
+ 20001.0
+
+
+ NumberOfInstances
+ 600.0
+
+
+ NumberOfInstancesWithMissingValues
+ 0.0
+
+
+ NumberOfMissingValues
+ 0.0
+
+
+ NumberOfNumericFeatures
+ 20000.0
+
+
+ NumberOfSymbolicFeatures
+ 1.0
+
+
+ PercentageOfBinaryFeatures
+ 0.004999750012499375
+
+
+ PercentageOfInstancesWithMissingValues
+ 0.0
+
+
+ PercentageOfMissingValues
+ 0.0
+
+
+ PercentageOfNumericFeatures
+ 99.9950002499875
+
+
+ PercentageOfSymbolicFeatures
+ 0.004999750012499375
+
+
+
diff --git a/tests/files/datasets/2/dataset.arff b/tests/files/org/openml/test/datasets/2/dataset.arff
similarity index 100%
rename from tests/files/datasets/2/dataset.arff
rename to tests/files/org/openml/test/datasets/2/dataset.arff
diff --git a/tests/files/datasets/2/description.xml b/tests/files/org/openml/test/datasets/2/description.xml
similarity index 100%
rename from tests/files/datasets/2/description.xml
rename to tests/files/org/openml/test/datasets/2/description.xml
diff --git a/tests/files/datasets/2/features.xml b/tests/files/org/openml/test/datasets/2/features.xml
similarity index 52%
rename from tests/files/datasets/2/features.xml
rename to tests/files/org/openml/test/datasets/2/features.xml
index b872ee711..8b994ccaa 100644
--- a/tests/files/datasets/2/features.xml
+++ b/tests/files/org/openml/test/datasets/2/features.xml
@@ -3,312 +3,431 @@
0
family
nominal
- false
+ GB
+ GK
+ GS
+ TN
+ ZA
+ ZF
+ ZH
+ ZM
+ ZS
+ false
false
false
+ 772
1
product-type
nominal
- false
+ C
+ H
+ G
+ false
false
false
+ 0
2
steel
nominal
- false
+ R
+ A
+ U
+ K
+ M
+ S
+ W
+ V
+ false
false
false
+ 86
3
carbon
numeric
- false
+ false
false
false
+ 0
4
hardness
numeric
- false
+ false
false
false
+ 0
5
temper_rolling
nominal
- false
+ T
+ false
false
false
+ 761
6
condition
nominal
- false
+ S
+ A
+ X
+ false
false
false
+ 303
7
formability
nominal
- false
+ 1
+ 2
+ 3
+ 4
+ 5
+ false
false
false
+ 318
8
strength
numeric
- false
+ false
false
false
+ 0
9
non-ageing
nominal
- false
+ N
+ false
false
false
+ 793
10
surface-finish
nominal
- false
+ P
+ M
+ false
false
false
+ 889
11
surface-quality
nominal
- false
+ D
+ E
+ F
+ G
+ false
false
false
+ 244
12
enamelability
nominal
- false
+ 1
+ 2
+ 3
+ 4
+ 5
+ false
false
false
+ 882
13
bc
nominal
- false
+ Y
+ false
false
false
+ 897
14
bf
nominal
- false
+ Y
+ false
false
false
+ 769
15
bt
nominal
- false
+ Y
+ false
false
false
+ 824
16
bw%2Fme
nominal
- false
+ B
+ M
+ false
false
false
+ 687
17
bl
nominal
- false
+ Y
+ false
false
false
+ 749
18
m
nominal
- false
+ Y
+ false
false
false
+ 898
19
chrom
nominal
- false
+ C
+ false
false
false
+ 872
20
phos
nominal
- false
+ P
+ false
false
false
+ 891
21
cbond
nominal
- false
+ Y
+ false
false
false
+ 824
22
marvi
nominal
- false
+ Y
+ false
false
false
+ 898
23
exptl
nominal
- false
+ Y
+ false
false
false
+ 896
24
ferro
nominal
- false
+ Y
+ false
false
false
+ 868
25
corr
nominal
- false
+ Y
+ false
false
false
+ 898
26
blue%2Fbright%2Fvarn%2Fclean
nominal
- false
+ B
+ R
+ V
+ C
+ false
false
false
+ 892
27
lustre
nominal
- false
+ Y
+ false
false
false
+ 847
28
jurofm
nominal
- false
+ Y
+ false
false
false
+ 898
29
s
nominal
- false
+ Y
+ false
false
false
+ 898
30
p
nominal
- false
+ Y
+ false
false
false
+ 898
31
shape
nominal
- false
+ COIL
+ SHEET
+ false
false
false
+ 0
32
thick
numeric
- false
+ false
false
false
+ 0
33
width
numeric
- false
+ false
false
false
+ 0
34
len
numeric
- false
+ false
false
false
+ 0
35
oil
nominal
- false
+ Y
+ N
+ false
false
false
+ 834
36
bore
nominal
- false
+ 0
+ 500
+ 600
+ 760
+ false
false
false
+ 0
37
packing
nominal
- false
+ 1
+ 2
+ 3
+ false
false
false
+ 889
38
class
nominal
- true
+ 1
+ 2
+ 3
+ 4
+ 5
+ U
+ true
false
false
+ 0
diff --git a/tests/files/datasets/2/qualities.xml b/tests/files/org/openml/test/datasets/2/qualities.xml
similarity index 100%
rename from tests/files/datasets/2/qualities.xml
rename to tests/files/org/openml/test/datasets/2/qualities.xml
diff --git a/tests/files/org/openml/test/datasets/30/dataset_30.pq b/tests/files/org/openml/test/datasets/30/dataset_30.pq
new file mode 100644
index 000000000..b35597281
Binary files /dev/null and b/tests/files/org/openml/test/datasets/30/dataset_30.pq differ
diff --git a/tests/files/org/openml/test/runs/1/description.xml b/tests/files/org/openml/test/runs/1/description.xml
new file mode 100644
index 000000000..92e9bcb98
--- /dev/null
+++ b/tests/files/org/openml/test/runs/1/description.xml
@@ -0,0 +1,645 @@
+
+ 100
+ 1
+ Jan van Rijn
+ 28
+ Supervised Classification
+ 67
+ weka.BayesNet_K2(1)
+ 12
+ weka.classifiers.bayes.BayesNet -- -D -Q weka.classifiers.bayes.net.search.local.K2 -- -P 1 -S BAYES -E weka.classifiers.bayes.net.estimate.SimpleEstimator -- -A 0.5
+
+ D
+ true
+
+
+ Q
+ weka.classifiers.bayes.net.search.local.K2
+
+
+ P
+ 1
+
+
+ S
+ BAYES
+
+
+
+ 28
+ optdigits
+ https://www.openml.org/data/download/28/dataset_28_optdigits.arff
+
+
+
+
+ -1
+ 261
+ description
+ https://www.openml.org/data/download/261/weka_generated_run935374685998857626.xml
+
+
+ -1
+ 262
+ predictions
+ https://www.openml.org/data/download/262/weka_generated_predictions576954524972002741.arff
+
+
+ area_under_roc_curve
+ 0.990288 [0.99724,0.989212,0.992776,0.994279,0.980578,0.98649,0.99422,0.99727,0.994858,0.976143]
+
+ average_cost
+ 0
+
+ f_measure
+ 0.922723 [0.989091,0.898857,0.935041,0.92431,0.927944,0.918156,0.980322,0.933219,0.895018,0.826531]
+
+ kappa
+ 0.913601
+
+ kb_relative_information_score
+ 5181.417432
+
+ mean_absolute_error
+ 0.016374
+
+ mean_prior_absolute_error
+ 0.179997
+
+ number_of_instances
+ 5620 [554,571,557,572,568,558,558,566,554,562]
+
+ os_information
+ [ Oracle Corporation, 1.7.0_51, amd64, Linux, 3.7.10-1.28-desktop ]
+
+ precision
+ 0.924345 [0.996337,0.902827,0.953358,0.941924,0.926316,0.966337,0.978571,0.905316,0.882456,0.791531]
+
+ predictive_accuracy
+ 0.922242
+
+ prior_entropy
+ 3.321833
+
+ recall
+ 0.922242 [0.981949,0.894921,0.917415,0.907343,0.929577,0.874552,0.982079,0.962898,0.907942,0.864769]
+
+ relative_absolute_error
+ 0.090968
+
+ root_mean_prior_squared_error
+ 0.299998
+
+ root_mean_squared_error
+ 0.117387
+
+ root_relative_squared_error
+ 0.391293
+
+ scimark_benchmark
+ 1969.9216824070186 [ 1241.8943613564243, 1575.5968355392279, 906.1111964820476, 1675.0415998938465, 4450.964418763549 ]
+
+ total_cost
+ 0
+
+ area_under_roc_curve
+ 0.993338 [1,0.987789,0.9958,0.998255,0.988484,0.997318,0.989483,0.999259,0.99749,0.979708]
+
+ area_under_roc_curve
+ 0.990543 [0.999928,0.977523,0.996012,0.995758,0.970974,0.978296,0.999929,0.998447,0.995804,0.993418]
+
+ area_under_roc_curve
+ 0.990071 [0.990873,0.996839,0.994512,0.994268,0.988327,0.989448,0.999965,0.997881,0.994549,0.95384]
+
+ area_under_roc_curve
+ 0.988339 [1,0.983081,0.979249,0.989456,0.989699,0.976937,1,0.999375,0.996199,0.969597]
+
+ area_under_roc_curve
+ 0.993133 [1,0.992201,0.988848,1,0.998749,0.996288,0.981719,0.998506,0.996378,0.978826]
+
+ area_under_roc_curve
+ 0.990209 [0.990837,0.997777,0.9982,0.997151,0.9786,0.985602,0.99086,0.990412,0.99125,0.981449]
+
+ area_under_roc_curve
+ 0.989645 [0.990613,0.998558,0.999014,0.985044,0.980059,0.986784,0.990335,0.996161,0.998094,0.971944]
+
+ area_under_roc_curve
+ 0.987188 [0.999929,0.974848,0.997418,0.995275,0.960292,0.969438,0.999928,0.997533,0.99213,0.985866]
+
+ area_under_roc_curve
+ 0.987588 [1,0.987189,0.989098,0.988067,0.96208,0.98696,1,0.996206,0.99213,0.974185]
+
+ area_under_roc_curve
+ 0.993119 [1,0.996648,0.989977,0.998089,0.990401,0.998412,0.989801,0.998765,0.994636,0.974308]
+
+ average_cost
+ 0
+
+ average_cost
+ 0
+
+ average_cost
+ 0
+
+ average_cost
+ 0
+
+ average_cost
+ 0
+
+ average_cost
+ 0
+
+ average_cost
+ 0
+
+ average_cost
+ 0
+
+ average_cost
+ 0
+
+ average_cost
+ 0
+
+ build_cpu_time
+ 0.058
+
+ build_cpu_time
+ 0.058
+
+ build_cpu_time
+ 0.055
+
+ build_cpu_time
+ 0.054
+
+ build_cpu_time
+ 0.052
+
+ build_cpu_time
+ 0.052
+
+ build_cpu_time
+ 0.051
+
+ build_cpu_time
+ 0.054
+
+ build_cpu_time
+ 0.052
+
+ build_cpu_time
+ 0.052
+
+ f_measure
+ 0.922001 [1,0.894737,0.954128,0.910714,0.902655,0.953271,0.947368,0.932203,0.902655,0.824561]
+
+ f_measure
+ 0.933066 [0.990826,0.93578,0.929825,0.929825,0.921739,0.914286,0.99115,0.954955,0.910714,0.852459]
+
+ f_measure
+ 0.921167 [0.990826,0.900901,0.915888,0.912281,0.932203,0.93578,0.990991,0.933333,0.923077,0.777778]
+
+ f_measure
+ 0.92075 [0.990826,0.854701,0.914286,0.944444,0.932203,0.924528,1,0.957265,0.87931,0.810345]
+
+ f_measure
+ 0.928674 [0.981481,0.882883,0.928571,0.974359,0.949153,0.915888,0.963636,0.966102,0.915888,0.810345]
+
+ f_measure
+ 0.913056 [0.981481,0.907563,0.934579,0.912281,0.928571,0.836735,0.973451,0.973913,0.839286,0.84127]
+
+ f_measure
+ 0.931169 [0.972973,0.93913,0.971963,0.914286,0.923077,0.910714,0.972477,0.905983,0.927273,0.876033]
+
+ f_measure
+ 0.909739 [0.99115,0.841121,0.936937,0.928571,0.899083,0.914286,0.982143,0.898305,0.892857,0.816]
+
+ f_measure
+ 0.906202 [0.990991,0.898305,0.915888,0.9,0.915888,0.903846,1,0.883333,0.864865,0.789474]
+
+ f_measure
+ 0.939667 [1,0.931034,0.947368,0.915888,0.972973,0.963636,0.982143,0.929825,0.894737,0.859649]
+
+ kappa
+ 0.91301
+
+ kappa
+ 0.924872
+
+ kappa
+ 0.913007
+
+ kappa
+ 0.911031
+
+ kappa
+ 0.92091
+
+ kappa
+ 0.903112
+
+ kappa
+ 0.922892
+
+ kappa
+ 0.899172
+
+ kappa
+ 0.895209
+
+ kappa
+ 0.932781
+
+ kb_relative_information_score
+ 520.530219
+
+ kb_relative_information_score
+ 522.44632
+
+ kb_relative_information_score
+ 516.098237
+
+ kb_relative_information_score
+ 516.223961
+
+ kb_relative_information_score
+ 520.828521
+
+ kb_relative_information_score
+ 512.802292
+
+ kb_relative_information_score
+ 522.721047
+
+ kb_relative_information_score
+ 510.349442
+
+ kb_relative_information_score
+ 511.383119
+
+ kb_relative_information_score
+ 528.034273
+
+ mean_absolute_error
+ 0.01561
+
+ mean_absolute_error
+ 0.014946
+
+ mean_absolute_error
+ 0.017004
+
+ mean_absolute_error
+ 0.01725
+
+ mean_absolute_error
+ 0.014984
+
+ mean_absolute_error
+ 0.018277
+
+ mean_absolute_error
+ 0.014778
+
+ mean_absolute_error
+ 0.019306
+
+ mean_absolute_error
+ 0.018655
+
+ mean_absolute_error
+ 0.01293
+
+ mean_prior_absolute_error
+ 0.179997
+
+ mean_prior_absolute_error
+ 0.179997
+
+ mean_prior_absolute_error
+ 0.179997
+
+ mean_prior_absolute_error
+ 0.179997
+
+ mean_prior_absolute_error
+ 0.179997
+
+ mean_prior_absolute_error
+ 0.179997
+
+ mean_prior_absolute_error
+ 0.179998
+
+ mean_prior_absolute_error
+ 0.179998
+
+ mean_prior_absolute_error
+ 0.179998
+
+ mean_prior_absolute_error
+ 0.179999
+
+ number_of_instances
+ 562 [55,57,56,58,57,56,56,56,55,56]
+
+ number_of_instances
+ 562 [55,57,56,58,57,56,56,56,55,56]
+
+ number_of_instances
+ 562 [55,57,56,57,57,56,56,57,55,56]
+
+ number_of_instances
+ 562 [55,57,56,57,57,56,56,57,55,56]
+
+ number_of_instances
+ 562 [55,57,56,57,57,55,56,57,55,57]
+
+ number_of_instances
+ 562 [55,57,56,57,57,55,56,57,55,57]
+
+ number_of_instances
+ 562 [56,57,55,57,57,56,55,57,56,56]
+
+ number_of_instances
+ 562 [56,57,55,57,57,56,55,57,56,56]
+
+ number_of_instances
+ 562 [56,58,55,57,56,56,56,56,56,56]
+
+ number_of_instances
+ 562 [56,57,56,57,56,56,56,56,56,56]
+
+ precision
+ 0.923823 [1,0.894737,0.981132,0.944444,0.910714,1,0.931034,0.887097,0.87931,0.810345]
+
+ precision
+ 0.936344 [1,0.980769,0.913793,0.946429,0.913793,0.979592,0.982456,0.963636,0.894737,0.787879]
+
+ precision
+ 0.922887 [1,0.925926,0.960784,0.912281,0.901639,0.962264,1,0.888889,0.870968,0.807692]
+
+ precision
+ 0.924699 [1,0.833333,0.979592,1,0.901639,0.98,1,0.933333,0.836066,0.783333]
+
+ precision
+ 0.92969 [1,0.907407,0.928571,0.95,0.918033,0.942308,0.981481,0.934426,0.942308,0.79661]
+
+ precision
+ 0.918297 [1,0.870968,0.980392,0.912281,0.945455,0.953488,0.964912,0.965517,0.824561,0.768116]
+
+ precision
+ 0.934578 [0.981818,0.931034,1,1,0.9,0.910714,0.981481,0.883333,0.944444,0.815385]
+
+ precision
+ 0.914296 [0.982456,0.9,0.928571,0.945455,0.942308,0.979592,0.964912,0.868852,0.892857,0.73913]
+
+ precision
+ 0.909699 [1,0.883333,0.942308,0.857143,0.960784,0.979167,1,0.828125,0.872727,0.775862]
+
+ precision
+ 0.94099 [1,0.915254,0.931034,0.98,0.981818,0.981481,0.982143,0.913793,0.87931,0.844828]
+
+ predictive_accuracy
+ 0.921708
+
+ predictive_accuracy
+ 0.932384
+
+ predictive_accuracy
+ 0.921708
+
+ predictive_accuracy
+ 0.919929
+
+ predictive_accuracy
+ 0.928826
+
+ predictive_accuracy
+ 0.912811
+
+ predictive_accuracy
+ 0.930605
+
+ predictive_accuracy
+ 0.909253
+
+ predictive_accuracy
+ 0.905694
+
+ predictive_accuracy
+ 0.939502
+
+ prior_entropy
+ 3.321833
+
+ prior_entropy
+ 3.321833
+
+ prior_entropy
+ 3.321833
+
+ prior_entropy
+ 3.321833
+
+ prior_entropy
+ 3.321833
+
+ prior_entropy
+ 3.321833
+
+ prior_entropy
+ 3.321833
+
+ prior_entropy
+ 3.321833
+
+ prior_entropy
+ 3.321833
+
+ prior_entropy
+ 3.321833
+
+ recall
+ 0.921708 [1,0.894737,0.928571,0.87931,0.894737,0.910714,0.964286,0.982143,0.927273,0.839286]
+
+ recall
+ 0.932384 [0.981818,0.894737,0.946429,0.913793,0.929825,0.857143,1,0.946429,0.927273,0.928571]
+
+ recall
+ 0.921708 [0.981818,0.877193,0.875,0.912281,0.964912,0.910714,0.982143,0.982456,0.981818,0.75]
+
+ recall
+ 0.919929 [0.981818,0.877193,0.857143,0.894737,0.964912,0.875,1,0.982456,0.927273,0.839286]
+
+ recall
+ 0.928826 [0.963636,0.859649,0.928571,1,0.982456,0.890909,0.946429,1,0.890909,0.824561]
+
+ recall
+ 0.912811 [0.963636,0.947368,0.892857,0.912281,0.912281,0.745455,0.982143,0.982456,0.854545,0.929825]
+
+ recall
+ 0.930605 [0.964286,0.947368,0.945455,0.842105,0.947368,0.910714,0.963636,0.929825,0.910714,0.946429]
+
+ recall
+ 0.909253 [1,0.789474,0.945455,0.912281,0.859649,0.857143,1,0.929825,0.892857,0.910714]
+
+ recall
+ 0.905694 [0.982143,0.913793,0.890909,0.947368,0.875,0.839286,1,0.946429,0.857143,0.803571]
+
+ recall
+ 0.939502 [1,0.947368,0.964286,0.859649,0.964286,0.946429,0.982143,0.946429,0.910714,0.875]
+
+ relative_absolute_error
+ 0.086724
+
+ relative_absolute_error
+ 0.083036
+
+ relative_absolute_error
+ 0.094469
+
+ relative_absolute_error
+ 0.095836
+
+ relative_absolute_error
+ 0.083244
+
+ relative_absolute_error
+ 0.101539
+
+ relative_absolute_error
+ 0.082103
+
+ relative_absolute_error
+ 0.107256
+
+ relative_absolute_error
+ 0.103639
+
+ relative_absolute_error
+ 0.071835
+
+ root_mean_prior_squared_error
+ 0.299997
+
+ root_mean_prior_squared_error
+ 0.299997
+
+ root_mean_prior_squared_error
+ 0.299997
+
+ root_mean_prior_squared_error
+ 0.299997
+
+ root_mean_prior_squared_error
+ 0.299997
+
+ root_mean_prior_squared_error
+ 0.299997
+
+ root_mean_prior_squared_error
+ 0.299998
+
+ root_mean_prior_squared_error
+ 0.299998
+
+ root_mean_prior_squared_error
+ 0.299999
+
+ root_mean_prior_squared_error
+ 0.3
+
+ root_mean_squared_error
+ 0.114072
+
+ root_mean_squared_error
+ 0.111221
+
+ root_mean_squared_error
+ 0.121483
+
+ root_mean_squared_error
+ 0.119208
+
+ root_mean_squared_error
+ 0.113177
+
+ root_mean_squared_error
+ 0.123617
+
+ root_mean_squared_error
+ 0.111738
+
+ root_mean_squared_error
+ 0.128468
+
+ root_mean_squared_error
+ 0.126509
+
+ root_mean_squared_error
+ 0.101792
+
+ root_relative_squared_error
+ 0.380244
+
+ root_relative_squared_error
+ 0.370741
+
+ root_relative_squared_error
+ 0.404947
+
+ root_relative_squared_error
+ 0.397364
+
+ root_relative_squared_error
+ 0.377262
+
+ root_relative_squared_error
+ 0.412061
+
+ root_relative_squared_error
+ 0.372463
+
+ root_relative_squared_error
+ 0.428228
+
+ root_relative_squared_error
+ 0.4217
+
+ root_relative_squared_error
+ 0.339306
+
+ total_cost
+ 0
+
+ total_cost
+ 0
+
+ total_cost
+ 0
+
+ total_cost
+ 0
+
+ total_cost
+ 0
+
+ total_cost
+ 0
+
+ total_cost
+ 0
+
+ total_cost
+ 0
+
+ total_cost
+ 0
+
+ total_cost
+ 0
+
+
diff --git a/tests/files/org/openml/test/setups/1/description.xml b/tests/files/org/openml/test/setups/1/description.xml
new file mode 100644
index 000000000..5717ad9f5
--- /dev/null
+++ b/tests/files/org/openml/test/setups/1/description.xml
@@ -0,0 +1,25 @@
+
+ 100
+ 60
+
+ 3432
+ 60
+ weka.J48
+ weka.J48(1)_C
+ C
+ option
+ 0.25
+ 0.9
+
+
+ 3435
+ 60
+ weka.J48
+ weka.J48(1)_M
+ M
+ option
+ 2
+ 2
+
+
+
diff --git a/tests/files/tasks/1/datasplits.arff b/tests/files/org/openml/test/tasks/1/datasplits.arff
similarity index 100%
rename from tests/files/tasks/1/datasplits.arff
rename to tests/files/org/openml/test/tasks/1/datasplits.arff
diff --git a/tests/files/tasks/1/task.xml b/tests/files/org/openml/test/tasks/1/task.xml
similarity index 98%
rename from tests/files/tasks/1/task.xml
rename to tests/files/org/openml/test/tasks/1/task.xml
index c70baaff3..38325bc24 100644
--- a/tests/files/tasks/1/task.xml
+++ b/tests/files/org/openml/test/tasks/1/task.xml
@@ -9,6 +9,7 @@
+ 1
crossvalidation
http://www.openml.org/api_splits/get/1/Task_1_splits.arff
1
diff --git a/tests/files/tasks/1882/datasplits.arff b/tests/files/org/openml/test/tasks/1882/datasplits.arff
similarity index 100%
rename from tests/files/tasks/1882/datasplits.arff
rename to tests/files/org/openml/test/tasks/1882/datasplits.arff
diff --git a/tests/files/tasks/1882/task.xml b/tests/files/org/openml/test/tasks/1882/task.xml
similarity index 98%
rename from tests/files/tasks/1882/task.xml
rename to tests/files/org/openml/test/tasks/1882/task.xml
index 4a744b397..07e63d969 100644
--- a/tests/files/tasks/1882/task.xml
+++ b/tests/files/org/openml/test/tasks/1882/task.xml
@@ -9,6 +9,7 @@
+ 3
crossvalidation
http://capa.win.tue.nl/api_splits/get/1882/Task_1882_splits.arff
10
diff --git a/tests/files/tasks/3/datasplits.arff b/tests/files/org/openml/test/tasks/3/datasplits.arff
similarity index 100%
rename from tests/files/tasks/3/datasplits.arff
rename to tests/files/org/openml/test/tasks/3/datasplits.arff
diff --git a/tests/files/tasks/3/task.xml b/tests/files/org/openml/test/tasks/3/task.xml
similarity index 98%
rename from tests/files/tasks/3/task.xml
rename to tests/files/org/openml/test/tasks/3/task.xml
index ef538330d..e73bbc75a 100644
--- a/tests/files/tasks/3/task.xml
+++ b/tests/files/org/openml/test/tasks/3/task.xml
@@ -9,6 +9,7 @@
+ 1
crossvalidation
http://www.openml.org/api_splits/get/3/Task_3_splits.arff
1
diff --git a/tests/flows/test_flow.py b/tests/flows/test_flow.py
deleted file mode 100644
index c1214cf1c..000000000
--- a/tests/flows/test_flow.py
+++ /dev/null
@@ -1,42 +0,0 @@
-import hashlib
-import sys
-import time
-import unittest
-
-from sklearn.dummy import DummyClassifier
-
-from openml.testing import TestBase
-import openml
-
-if sys.version_info[0] >= 3:
- from unittest import mock
-else:
- import mock
-
-
-class TestFlow(TestBase):
- @unittest.skip('The method which is tested by this function doesnt exist')
- def test_download_flow_list(self):
- def check_flow(flow):
- self.assertIsInstance(flow, dict)
- self.assertEqual(len(flow), 6)
-
- flows = openml.flows.get_flow_list()
- self.assertGreaterEqual(len(flows), 1448)
- for flow in flows:
- check_flow(flow)
-
- @mock.patch.object(openml.OpenMLFlow, '_get_name', autospec=True)
- def test_upload_flow(self, name_mock):
- flow = openml.OpenMLFlow(model=DummyClassifier(), description="test description")
-
- # Create a unique prefix for the flow. Necessary because the flow is
- # identified by its name and external version online. Having a unique
- # name allows us to publish the same flow in each test run
- md5 = hashlib.md5()
- md5.update(str(time.time()).encode('utf-8'))
- sentinel = md5.hexdigest()[:10]
- name_mock.return_value = '%s%s' % (sentinel, flow.name)
-
- flow.publish()
- self.assertIsInstance(flow.flow_id, int)
diff --git a/tests/runs/test_run_functions.py b/tests/runs/test_run_functions.py
deleted file mode 100644
index 1bac276ba..000000000
--- a/tests/runs/test_run_functions.py
+++ /dev/null
@@ -1,158 +0,0 @@
-from sklearn.linear_model import LogisticRegression, SGDClassifier
-import openml
-import openml.exceptions
-from openml.testing import TestBase
-
-
-class TestRun(TestBase):
- def test_run_iris(self):
- task = openml.tasks.get_task(10107)
- clf = LogisticRegression()
- run = openml.runs.run_task(task, clf)
- run_ = run.publish()
- self.assertEqual(run_, run)
- self.assertIsInstance(run.dataset_id, int)
-
- def test__run_task_get_arffcontent(self):
- task = openml.tasks.get_task(1939)
- class_labels = task.class_labels
-
- clf = SGDClassifier(loss='hinge', random_state=1)
- self.assertRaisesRegexp(AttributeError,
- "probability estimates are not available for loss='hinge'",
- openml.runs.functions._run_task_get_arffcontent,
- clf, task, class_labels)
-
- clf = SGDClassifier(loss='log', random_state=1)
- arff_datacontent = openml.runs.functions._run_task_get_arffcontent(
- clf, task, class_labels)
- self.assertIsInstance(arff_datacontent, list)
- # 10 times 10 fold CV of 150 samples
- self.assertEqual(len(arff_datacontent), 1500)
- for arff_line in arff_datacontent:
- self.assertEqual(len(arff_line), 8)
- self.assertGreaterEqual(arff_line[0], 0)
- self.assertLessEqual(arff_line[0], 9)
- self.assertGreaterEqual(arff_line[1], 0)
- self.assertLessEqual(arff_line[1], 9)
- self.assertGreaterEqual(arff_line[2], 0)
- self.assertLessEqual(arff_line[2], 149)
- self.assertAlmostEqual(sum(arff_line[3:6]), 1.0)
- self.assertIn(arff_line[6], ['Iris-setosa', 'Iris-versicolor',
- 'Iris-virginica'])
- self.assertIn(arff_line[7], ['Iris-setosa', 'Iris-versicolor',
- 'Iris-virginica'])
-
- def test_get_run(self):
- run = openml.runs.get_run(473350)
- self.assertEqual(run.dataset_id, 1167)
- self.assertEqual(run.evaluations['f_measure'], 0.624668)
- for i, value in [(0, 0.66233),
- (1, 0.639286),
- (2, 0.567143),
- (3, 0.745833),
- (4, 0.599638),
- (5, 0.588801),
- (6, 0.527976),
- (7, 0.666365),
- (8, 0.56759),
- (9, 0.64621)]:
- self.assertEqual(run.detailed_evaluations['f_measure'][0][i], value)
-
- def _check_run(self, run):
- self.assertIsInstance(run, dict)
- self.assertEqual(len(run), 5)
-
- def test_get_runs_list(self):
- runs = openml.runs.list_runs(id=[2])
- self.assertEqual(len(runs), 1)
- for rid in runs:
- self._check_run(runs[rid])
-
- def test_get_runs_list_by_task(self):
- task_ids = [20]
- runs = openml.runs.list_runs(task=task_ids)
- self.assertGreaterEqual(len(runs), 590)
- for rid in runs:
- self.assertIn(runs[rid]['task_id'], task_ids)
- self._check_run(runs[rid])
- num_runs = len(runs)
-
- task_ids.append(21)
- runs = openml.runs.list_runs(task=task_ids)
- self.assertGreaterEqual(len(runs), num_runs + 1)
- for rid in runs:
- self.assertIn(runs[rid]['task_id'], task_ids)
- self._check_run(runs[rid])
-
- def test_get_runs_list_by_uploader(self):
- # 29 is Dominik Kirchhoff - Joaquin and Jan have too many runs right now
- uploader_ids = [29]
-
- runs = openml.runs.list_runs(uploader=uploader_ids)
- self.assertGreaterEqual(len(runs), 3)
- for rid in runs:
- self.assertIn(runs[rid]['uploader'], uploader_ids)
- self._check_run(runs[rid])
- num_runs = len(runs)
-
- uploader_ids.append(274)
-
- runs = openml.runs.list_runs(uploader=uploader_ids)
- self.assertGreaterEqual(len(runs), num_runs + 1)
- for rid in runs:
- self.assertIn(runs[rid]['uploader'], uploader_ids)
- self._check_run(runs[rid])
-
- def test_get_runs_list_by_flow(self):
- flow_ids = [1154]
- runs = openml.runs.list_runs(flow=flow_ids)
- self.assertGreaterEqual(len(runs), 1)
- for rid in runs:
- self.assertIn(runs[rid]['flow_id'], flow_ids)
- self._check_run(runs[rid])
- num_runs = len(runs)
-
- flow_ids.append(1069)
- runs = openml.runs.list_runs(flow=flow_ids)
- self.assertGreaterEqual(len(runs), num_runs + 1)
- for rid in runs:
- self.assertIn(runs[rid]['flow_id'], flow_ids)
- self._check_run(runs[rid])
-
- def test_get_runs_pagination(self):
- uploader_ids = [1]
- size = 10
- max = 100
- for i in range(0, max, size):
- runs = openml.runs.list_runs(offset=i, size=size, uploader=uploader_ids)
- self.assertGreaterEqual(size, len(runs))
- for rid in runs:
- self.assertIn(runs[rid]["uploader"], uploader_ids)
-
- def test_get_runs_list_by_filters(self):
- ids = [505212, 6100]
- tasks = [2974, 339]
- uploaders_1 = [1, 17]
- uploaders_2 = [29, 274]
- flows = [74, 1718]
-
- self.assertRaises(openml.exceptions.OpenMLServerError, openml.runs.list_runs)
-
- runs = openml.runs.list_runs(id=ids)
- self.assertEqual(len(runs), 2)
-
- runs = openml.runs.list_runs(task=tasks)
- self.assertGreaterEqual(len(runs), 2)
-
- runs = openml.runs.list_runs(uploader=uploaders_2)
- self.assertGreaterEqual(len(runs), 10)
-
- runs = openml.runs.list_runs(flow=flows)
- self.assertGreaterEqual(len(runs), 100)
-
- runs = openml.runs.list_runs(id=ids, task=tasks, uploader=uploaders_1)
-
- def test_get_runs_list_by_tag(self):
- runs = openml.runs.list_runs(tag='02-11-16_21.46.39')
- self.assertEqual(len(runs), 1)
diff --git a/tests/tasks/test_task.py b/tests/tasks/test_task.py
deleted file mode 100644
index dbbc0666d..000000000
--- a/tests/tasks/test_task.py
+++ /dev/null
@@ -1,91 +0,0 @@
-import sys
-import types
-
-if sys.version_info[0] >= 3:
- from unittest import mock
-else:
- import mock
-
-import numpy as np
-
-import openml
-from openml.testing import TestBase
-
-
-class OpenMLTaskTest(TestBase):
-
- def test_get_clustering_task(self):
- self.assertRaisesRegexp(KeyError, 'oml:target_feature',
- openml.tasks.get_task, 10128)
-
- @mock.patch('openml.datasets.get_dataset', autospec=True)
- def test_get_dataset(self, patch):
- patch.return_value = mock.MagicMock()
- mm = mock.MagicMock()
- patch.return_value.retrieve_class_labels = mm
- patch.return_value.retrieve_class_labels.return_value = 'LA'
- retval = openml.tasks.get_task(1)
- self.assertEqual(patch.call_count, 1)
- self.assertIsInstance(retval, openml.OpenMLTask)
- self.assertEqual(retval.class_labels, 'LA')
-
- def test_get_X_and_Y(self):
- # Classification task
- task = openml.tasks.get_task(1)
- X, Y = task.get_X_and_y()
- self.assertEqual((898, 38), X.shape)
- self.assertIsInstance(X, np.ndarray)
- self.assertEqual((898, ), Y.shape)
- self.assertIsInstance(Y, np.ndarray)
- self.assertEqual(Y.dtype, int)
-
- # Regression task
- task = openml.tasks.get_task(2280)
- X, Y = task.get_X_and_y()
- self.assertEqual((8192, 8), X.shape)
- self.assertIsInstance(X, np.ndarray)
- self.assertEqual((8192,), Y.shape)
- self.assertIsInstance(Y, np.ndarray)
- self.assertEqual(Y.dtype, float)
-
- def test_get_train_and_test_split_indices(self):
- openml.config.set_cache_directory(self.static_cache_dir)
- task = openml.tasks.get_task(1882)
- train_indices, test_indices = task.get_train_test_split_indices(0, 0)
- self.assertEqual(16, train_indices[0])
- self.assertEqual(395, train_indices[-1])
- self.assertEqual(412, test_indices[0])
- self.assertEqual(364, test_indices[-1])
- train_indices, test_indices = task.get_train_test_split_indices(2, 2)
- self.assertEqual(237, train_indices[0])
- self.assertEqual(681, train_indices[-1])
- self.assertEqual(583, test_indices[0])
- self.assertEqual(24, test_indices[-1])
- self.assertRaisesRegexp(ValueError, "Fold 10 not known",
- task.get_train_test_split_indices, 10, 0)
- self.assertRaisesRegexp(ValueError, "Repeat 10 not known",
- task.get_train_test_split_indices, 0, 10)
-
- def test_iterate_repeats(self):
- openml.config.set_cache_directory(self.static_cache_dir)
- task = openml.tasks.get_task(1882)
-
- num_repeats = 0
- for rep in task.iterate_repeats():
- num_repeats += 1
- self.assertIsInstance(rep, types.GeneratorType)
- self.assertEqual(num_repeats, 10)
-
- def test_iterate_all_splits(self):
- openml.config.set_cache_directory(self.static_cache_dir)
- task = openml.tasks.get_task(1882)
-
- num_splits = 0
- for split in task.iterate_all_splits():
- num_splits += 1
- self.assertIsInstance(split[0], np.ndarray)
- self.assertIsInstance(split[1], np.ndarray)
- self.assertEqual(split[0].shape[0] + split[1].shape[0], 898)
- self.assertEqual(num_splits, 100)
-
-
diff --git a/tests/tasks/test_task_functions.py b/tests/tasks/test_task_functions.py
deleted file mode 100644
index f99ea9d8d..000000000
--- a/tests/tasks/test_task_functions.py
+++ /dev/null
@@ -1,119 +0,0 @@
-import os
-import sys
-
-if sys.version_info[0] >= 3:
- from unittest import mock
-else:
- import mock
-
-from openml.util import is_string
-from openml.testing import TestBase
-from openml import OpenMLSplit, OpenMLTask
-from openml.exceptions import OpenMLCacheException
-import openml
-
-
-class TestTask(TestBase):
- def test__get_cached_tasks(self):
- openml.config.set_cache_directory(self.static_cache_dir)
- tasks = openml.tasks.functions._get_cached_tasks()
- self.assertIsInstance(tasks, dict)
- self.assertEqual(len(tasks), 3)
- self.assertIsInstance(list(tasks.values())[0], OpenMLTask)
-
- def test__get_cached_task(self):
- openml.config.set_cache_directory(self.static_cache_dir)
- task = openml.tasks.functions._get_cached_task(1)
- self.assertIsInstance(task, OpenMLTask)
-
- def test__get_cached_task_not_cached(self):
- openml.config.set_cache_directory(self.static_cache_dir)
- self.assertRaisesRegexp(OpenMLCacheException,
- 'Task file for tid 2 not cached',
- openml.tasks.functions._get_cached_task, 2)
-
- def test__get_estimation_procedure_list(self):
- estimation_procedures = openml.tasks.functions.\
- _get_estimation_procedure_list()
- self.assertIsInstance(estimation_procedures, list)
- self.assertIsInstance(estimation_procedures[0], dict)
- self.assertEqual(estimation_procedures[0]['task_type_id'], 1)
-
- def _check_task(self, task):
- self.assertEqual(type(task), dict)
- self.assertGreaterEqual(len(task), 2)
- self.assertIn('did', task)
- self.assertIsInstance(task['did'], int)
- self.assertIn('status', task)
- self.assertTrue(is_string(task['status']))
- self.assertIn(task['status'],
- ['in_preparation', 'active', 'deactivated'])
-
- def test_list_tasks_by_type(self):
- num_curves_tasks = 200 # number is flexible, check server if fails
- ttid=3
- tasks = openml.tasks.list_tasks(task_type_id=ttid)
- self.assertGreaterEqual(len(tasks), num_curves_tasks)
- for tid in tasks:
- self.assertEquals(ttid, tasks[tid]["ttid"])
- self._check_task(tasks[tid])
-
- def test_list_tasks_by_tag(self):
- num_basic_tasks = 54 # number is flexible, check server if fails
- tasks = openml.tasks.list_tasks(tag='basic')
- self.assertGreaterEqual(len(tasks), num_basic_tasks)
- for tid in tasks:
- self._check_task(tasks[tid])
-
- def test_list_tasks(self):
- tasks = openml.tasks.list_tasks()
- self.assertGreaterEqual(len(tasks), 2000)
- for tid in tasks:
- self._check_task(tasks[tid])
-
- def test_list_tasks_paginate(self):
- size = 10
- max = 100
- for i in range(0, max, size):
- tasks = openml.tasks.list_tasks(offset=i, size=size)
- self.assertGreaterEqual(size, len(tasks))
- for tid in tasks:
- self._check_task(tasks[tid])
-
- def test_list_tasks_per_type_paginate(self):
- size = 10
- max = 100
- task_types = 5
- for j in range(1,task_types):
- for i in range(0, max, size):
- tasks = openml.tasks.list_tasks(task_type_id=j, offset=i, size=size)
- self.assertGreaterEqual(size, len(tasks))
- for tid in tasks:
- self.assertEquals(j, tasks[tid]["ttid"])
- self._check_task(tasks[tid])
-
- def test__get_task(self):
- openml.config.set_cache_directory(self.static_cache_dir)
- task = openml.tasks.get_task(1882)
-
- def test_get_task(self):
- task = openml.tasks.get_task(1)
- self.assertIsInstance(task, OpenMLTask)
- self.assertTrue(os.path.exists(
- os.path.join(os.getcwd(), "tasks", "1", "task.xml")))
- self.assertTrue(os.path.exists(
- os.path.join(os.getcwd(), "tasks", "1", "datasplits.arff")))
- self.assertTrue(os.path.exists(
- os.path.join(os.getcwd(), "datasets", "1", "dataset.arff")))
-
- def test_get_task_with_cache(self):
- openml.config.set_cache_directory(self.static_cache_dir)
- task = openml.tasks.get_task(1)
- self.assertIsInstance(task, OpenMLTask)
-
- def test_download_split(self):
- task = openml.tasks.get_task(1)
- split = task.download_split()
- self.assertEqual(type(split), OpenMLSplit)
- self.assertTrue(os.path.exists(
- os.path.join(os.getcwd(), "tasks", "1", "datasplits.arff")))
diff --git a/tests/datasets/__init__.py b/tests/test_datasets/__init__.py
similarity index 100%
rename from tests/datasets/__init__.py
rename to tests/test_datasets/__init__.py
diff --git a/tests/test_datasets/test_dataset.py b/tests/test_datasets/test_dataset.py
new file mode 100644
index 000000000..c651845fb
--- /dev/null
+++ b/tests/test_datasets/test_dataset.py
@@ -0,0 +1,484 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+import os
+import unittest.mock
+from time import time
+
+import numpy as np
+import pandas as pd
+import pytest
+from scipy import sparse
+
+import openml
+from openml.datasets import OpenMLDataFeature, OpenMLDataset
+from openml.exceptions import PyOpenMLError
+from openml.testing import TestBase
+
+import pytest
+
+
+@pytest.mark.production_server()
+class OpenMLDatasetTest(TestBase):
+ _multiprocess_can_split_ = True
+
+ def setUp(self):
+ super().setUp()
+ self.use_production_server()
+
+ # Load dataset id 2 - dataset 2 is interesting because it contains
+ # missing values, categorical features etc.
+ self._dataset = None
+ # titanic as missing values, categories, and string
+ self._titanic = None
+ # these datasets have some boolean features
+ self._pc4 = None
+ self._jm1 = None
+ self._iris = None
+
+ @property
+ def dataset(self):
+ if self._dataset is None:
+ self._dataset = openml.datasets.get_dataset(2, download_data=False)
+ return self._dataset
+
+ @property
+ def titanic(self):
+ if self._titanic is None:
+ self._titanic = openml.datasets.get_dataset(40945, download_data=False)
+ return self._titanic
+
+ @property
+ def pc4(self):
+ if self._pc4 is None:
+ self._pc4 = openml.datasets.get_dataset(1049, download_data=False)
+ return self._pc4
+
+ @property
+ def jm1(self):
+ if self._jm1 is None:
+ self._jm1 = openml.datasets.get_dataset(1053, download_data=False)
+ return self._jm1
+
+ @property
+ def iris(self):
+ if self._iris is None:
+ self._iris = openml.datasets.get_dataset(61, download_data=False)
+ return self._iris
+
+ def test_repr(self):
+ # create a bare-bones dataset as would be returned by
+ # create_dataset
+ data = openml.datasets.OpenMLDataset(name="somename", description="a description")
+ str(data)
+
+ def test_init_string_validation(self):
+ with pytest.raises(ValueError, match="Invalid symbols ' ' in name"):
+ openml.datasets.OpenMLDataset(name="some name", description="a description")
+
+ with pytest.raises(ValueError, match="Invalid symbols 'ï' in description"):
+ openml.datasets.OpenMLDataset(name="somename", description="a descriptïon")
+
+ with pytest.raises(ValueError, match="Invalid symbols 'ü' in citation"):
+ openml.datasets.OpenMLDataset(
+ name="somename",
+ description="a description",
+ citation="Something by Müller",
+ )
+
+ def test__unpack_categories_with_nan_likes(self):
+ # unpack_categories decodes numeric categorical values according to the header
+ # Containing a 'non' category in the header shouldn't lead to failure.
+ categories = ["a", "b", None, float("nan"), np.nan]
+ series = pd.Series([0, 1, None, float("nan"), np.nan, 1, 0])
+ clean_series = OpenMLDataset._unpack_categories(series, categories)
+
+ expected_values = ["a", "b", np.nan, np.nan, np.nan, "b", "a"]
+ self.assertListEqual(list(clean_series.values), expected_values)
+ self.assertListEqual(list(clean_series.cat.categories.values), list("ab"))
+
+ def test_get_data_pandas(self):
+ data, _, _, _ = self.titanic.get_data()
+ assert isinstance(data, pd.DataFrame)
+ assert data.shape[1] == len(self.titanic.features)
+ assert data.shape[0] == 1309
+ # Dynamically detect what this version of Pandas calls string columns.
+ str_dtype = data["name"].dtype.name
+
+ col_dtype = {
+ "pclass": "uint8",
+ "survived": "category",
+ "name": str_dtype,
+ "sex": "category",
+ "age": "float64",
+ "sibsp": "uint8",
+ "parch": "uint8",
+ "ticket": str_dtype,
+ "fare": "float64",
+ "cabin": str_dtype,
+ "embarked": "category",
+ "boat": str_dtype,
+ "body": "float64",
+ "home.dest": str_dtype,
+ }
+ for col_name in data.columns:
+ assert data[col_name].dtype.name == col_dtype[col_name]
+
+ X, y, _, _ = self.titanic.get_data(
+ target=self.titanic.default_target_attribute,
+ )
+ assert isinstance(X, pd.DataFrame)
+ assert isinstance(y, pd.Series)
+ assert X.shape == (1309, 13)
+ assert y.shape == (1309,)
+ for col_name in X.columns:
+ assert X[col_name].dtype.name == col_dtype[col_name]
+ assert y.dtype.name == col_dtype["survived"]
+
+ @pytest.mark.skip("https://github.com/openml/openml-python/issues/1157")
+ def test_get_data_boolean_pandas(self):
+ # test to check that we are converting properly True and False even
+ # with some inconsistency when dumping the data on openml
+ data, _, _, _ = self.jm1.get_data()
+ assert data["defects"].dtype.name == "category"
+ assert set(data["defects"].cat.categories) == {True, False}
+
+ data, _, _, _ = self.pc4.get_data()
+ assert data["c"].dtype.name == "category"
+ assert set(data["c"].cat.categories) == {True, False}
+
+ def _check_expected_type(self, dtype, is_cat, col):
+ if is_cat:
+ expected_type = "category"
+ elif not col.isna().any() and (col.astype("uint8") == col).all():
+ expected_type = "uint8"
+ else:
+ expected_type = "float64"
+
+ assert dtype.name == expected_type
+
+ @pytest.mark.skip("https://github.com/openml/openml-python/issues/1157")
+ def test_get_data_with_rowid(self):
+ self.dataset.row_id_attribute = "condition"
+ rval, _, categorical, _ = self.dataset.get_data(include_row_id=True)
+ assert isinstance(rval, pd.DataFrame)
+ for dtype, is_cat, col in zip(rval.dtypes, categorical, rval):
+ self._check_expected_type(dtype, is_cat, rval[col])
+ assert rval.shape == (898, 39)
+ assert len(categorical) == 39
+
+ rval, _, categorical, _ = self.dataset.get_data()
+ assert isinstance(rval, pd.DataFrame)
+ for dtype, is_cat, col in zip(rval.dtypes, categorical, rval):
+ self._check_expected_type(dtype, is_cat, rval[col])
+ assert rval.shape == (898, 38)
+ assert len(categorical) == 38
+
+ @pytest.mark.skip("https://github.com/openml/openml-python/issues/1157")
+ def test_get_data_with_target_pandas(self):
+ X, y, categorical, attribute_names = self.dataset.get_data(target="class")
+ assert isinstance(X, pd.DataFrame)
+ for dtype, is_cat, col in zip(X.dtypes, categorical, X):
+ self._check_expected_type(dtype, is_cat, X[col])
+ assert isinstance(y, pd.Series)
+ assert y.dtype.name == "category"
+
+ assert X.shape == (898, 38)
+ assert len(attribute_names) == 38
+ assert y.shape == (898,)
+
+ assert "class" not in attribute_names
+
+ def test_get_data_rowid_and_ignore_and_target(self):
+ self.dataset.ignore_attribute = ["condition"]
+ self.dataset.row_id_attribute = ["hardness"]
+ X, y, categorical, names = self.dataset.get_data(target="class")
+ assert X.shape == (898, 36)
+ assert len(categorical) == 36
+ cats = [True] * 3 + [False, True, True, False] + [True] * 23 + [False] * 3 + [True] * 3
+ self.assertListEqual(categorical, cats)
+ assert y.shape == (898,)
+
+ @pytest.mark.skip("https://github.com/openml/openml-python/issues/1157")
+ def test_get_data_with_ignore_attributes(self):
+ self.dataset.ignore_attribute = ["condition"]
+ rval, _, categorical, _ = self.dataset.get_data(include_ignore_attribute=True)
+ for dtype, is_cat, col in zip(rval.dtypes, categorical, rval):
+ self._check_expected_type(dtype, is_cat, rval[col])
+ assert rval.shape == (898, 39)
+ assert len(categorical) == 39
+
+ rval, _, categorical, _ = self.dataset.get_data(include_ignore_attribute=False)
+ for dtype, is_cat, col in zip(rval.dtypes, categorical, rval):
+ self._check_expected_type(dtype, is_cat, rval[col])
+ assert rval.shape == (898, 38)
+ assert len(categorical) == 38
+
+ def test_get_data_with_nonexisting_class(self):
+ # This class is using the anneal dataset with labels [1, 2, 3, 4, 5, 'U']. However,
+ # label 4 does not exist and we test that the features 5 and 'U' are correctly mapped to
+ # indices 4 and 5, and that nothing is mapped to index 3.
+ _, y, _, _ = self.dataset.get_data("class")
+ assert list(y.dtype.categories) == ["1", "2", "3", "4", "5", "U"]
+
+ def test_get_data_corrupt_pickle(self):
+ # Lazy loaded dataset, populate cache.
+ self.iris.get_data()
+ # Corrupt pickle file, overwrite as empty.
+ with open(self.iris.data_pickle_file, "w") as fh:
+ fh.write("")
+ # Despite the corrupt file, the data should be loaded from the ARFF file.
+ # A warning message is written to the python logger.
+ xy, _, _, _ = self.iris.get_data()
+ assert isinstance(xy, pd.DataFrame)
+ assert xy.shape == (150, 5)
+
+ def test_lazy_loading_metadata(self):
+ # Initial Setup
+ did_cache_dir = openml.utils._create_cache_directory_for_id(
+ openml.datasets.functions.DATASETS_CACHE_DIR_NAME,
+ 2,
+ )
+ _compare_dataset = openml.datasets.get_dataset(
+ 2,
+ download_data=False,
+ download_features_meta_data=True,
+ download_qualities=True,
+ )
+ change_time = os.stat(did_cache_dir).st_mtime
+
+ # Test with cache
+ _dataset = openml.datasets.get_dataset(
+ 2,
+ download_data=False,
+ download_features_meta_data=False,
+ download_qualities=False,
+ )
+ assert change_time == os.stat(did_cache_dir).st_mtime
+ assert _dataset.features == _compare_dataset.features
+ assert _dataset.qualities == _compare_dataset.qualities
+
+ # -- Test without cache
+ openml.utils._remove_cache_dir_for_id(
+ openml.datasets.functions.DATASETS_CACHE_DIR_NAME,
+ did_cache_dir,
+ )
+
+ _dataset = openml.datasets.get_dataset(
+ 2,
+ download_data=False,
+ download_features_meta_data=False,
+ download_qualities=False,
+ )
+ assert ["description.xml"] == os.listdir(did_cache_dir)
+ assert change_time != os.stat(did_cache_dir).st_mtime
+ assert _dataset.features == _compare_dataset.features
+ assert _dataset.qualities == _compare_dataset.qualities
+
+ def test_equality_comparison(self):
+ self.assertEqual(self.iris, self.iris)
+ self.assertNotEqual(self.iris, self.titanic)
+ self.assertNotEqual(self.titanic, "Wrong_object")
+
+
+@pytest.mark.test_server()
+def test_tagging():
+ dataset = openml.datasets.get_dataset(125, download_data=False)
+
+ # tags can be at most 64 alphanumeric (+ underscore) chars
+ unique_indicator = str(time()).replace(".", "")
+ tag = f"test_tag_OpenMLDatasetTestOnTestServer_{unique_indicator}"
+ datasets = openml.datasets.list_datasets(tag=tag)
+ assert datasets.empty
+ dataset.push_tag(tag)
+ datasets = openml.datasets.list_datasets(tag=tag)
+ assert len(datasets) == 1
+ assert 125 in datasets["did"]
+ dataset.remove_tag(tag)
+ datasets = openml.datasets.list_datasets(tag=tag)
+ assert datasets.empty
+
+@pytest.mark.test_server()
+def test_get_feature_with_ontology_data_id_11():
+ # test on car dataset, which has built-in ontology references
+ dataset = openml.datasets.get_dataset(11)
+ assert len(dataset.features) == 7
+ assert len(dataset.features[1].ontologies) >= 2
+ assert len(dataset.features[2].ontologies) >= 1
+ assert len(dataset.features[3].ontologies) >= 1
+
+@pytest.mark.test_server()
+def test_add_remove_ontology_to_dataset():
+ did = 1
+ feature_index = 1
+ ontology = "https://www.openml.org/unittest/" + str(time())
+ openml.datasets.functions.data_feature_add_ontology(did, feature_index, ontology)
+ openml.datasets.functions.data_feature_remove_ontology(did, feature_index, ontology)
+
+@pytest.mark.test_server()
+def test_add_same_ontology_multiple_features():
+ did = 1
+ ontology = "https://www.openml.org/unittest/" + str(time())
+
+ for i in range(3):
+ openml.datasets.functions.data_feature_add_ontology(did, i, ontology)
+
+
+@pytest.mark.test_server()
+def test_add_illegal_long_ontology():
+ did = 1
+ ontology = "http://www.google.com/" + ("a" * 257)
+ try:
+ openml.datasets.functions.data_feature_add_ontology(did, 1, ontology)
+ assert False
+ except openml.exceptions.OpenMLServerException as e:
+ assert e.code == 1105
+
+
+
+@pytest.mark.test_server()
+def test_add_illegal_url_ontology():
+ did = 1
+ ontology = "not_a_url" + str(time())
+ try:
+ openml.datasets.functions.data_feature_add_ontology(did, 1, ontology)
+ assert False
+ except openml.exceptions.OpenMLServerException as e:
+ assert e.code == 1106
+
+
+@pytest.mark.production_server()
+class OpenMLDatasetTestSparse(TestBase):
+ _multiprocess_can_split_ = True
+
+ def setUp(self):
+ super().setUp()
+ self.use_production_server()
+
+ self.sparse_dataset = openml.datasets.get_dataset(4136, download_data=False)
+
+ def test_get_sparse_dataset_dataframe_with_target(self):
+ X, y, _, attribute_names = self.sparse_dataset.get_data(target="class")
+ assert isinstance(X, pd.DataFrame)
+ assert isinstance(X.dtypes.iloc[0], pd.SparseDtype)
+ assert X.shape == (600, 20000)
+
+ assert isinstance(y, pd.Series)
+ assert isinstance(y.dtypes, pd.SparseDtype)
+ assert y.shape == (600,)
+
+ assert len(attribute_names) == 20000
+ assert "class" not in attribute_names
+
+ def test_get_sparse_dataset_dataframe(self):
+ rval, *_ = self.sparse_dataset.get_data()
+ assert isinstance(rval, pd.DataFrame)
+ np.testing.assert_array_equal(
+ [pd.SparseDtype(np.float32, fill_value=0.0)] * len(rval.dtypes),
+ rval.dtypes,
+ )
+ assert rval.shape == (600, 20001)
+
+ def test_get_sparse_dataset_rowid_and_ignore_and_target(self):
+ # TODO: re-add row_id and ignore attributes
+ self.sparse_dataset.ignore_attribute = ["V256"]
+ self.sparse_dataset.row_id_attribute = ["V512"]
+ X, y, categorical, _ = self.sparse_dataset.get_data(
+ target="class",
+ include_row_id=False,
+ include_ignore_attribute=False,
+ )
+ assert all(dtype == pd.SparseDtype(np.float32, fill_value=0.0) for dtype in X.dtypes)
+ # array format returned dense, but now we only return sparse and let the user handle it.
+ assert isinstance(y.dtypes, pd.SparseDtype)
+ assert X.shape == (600, 19998)
+
+ assert len(categorical) == 19998
+ self.assertListEqual(categorical, [False] * 19998)
+ assert y.shape == (600,)
+
+ def test_get_sparse_categorical_data_id_395(self):
+ dataset = openml.datasets.get_dataset(395, download_data=True)
+ feature = dataset.features[3758]
+ assert isinstance(dataset, OpenMLDataset)
+ assert isinstance(feature, OpenMLDataFeature)
+ assert dataset.name == "re1.wc"
+ assert feature.name == "CLASS_LABEL"
+ assert feature.data_type == "nominal"
+ assert len(feature.nominal_values) == 25
+
+
+@pytest.mark.test_server()
+def test__read_features(mocker, workdir, static_cache_dir):
+ """Test we read the features from the xml if no cache pickle is available.
+ This test also does some simple checks to verify that the features are read correctly
+ """
+ filename_mock = mocker.patch("openml.datasets.dataset._get_features_pickle_file")
+ pickle_mock = mocker.patch("openml.datasets.dataset.pickle")
+
+ filename_mock.return_value = os.path.join(workdir, "features.xml.pkl")
+ pickle_mock.load.side_effect = FileNotFoundError
+
+ features = openml.datasets.dataset._read_features(
+ os.path.join(
+ static_cache_dir,
+ "org",
+ "openml",
+ "test",
+ "datasets",
+ "2",
+ "features.xml",
+ ),
+ )
+ assert isinstance(features, dict)
+ assert len(features) == 39
+ assert isinstance(features[0], OpenMLDataFeature)
+ assert features[0].name == "family"
+ assert len(features[0].nominal_values) == 9
+ # pickle.load is never called because the features pickle file didn't exist
+ assert pickle_mock.load.call_count == 0
+ assert pickle_mock.dump.call_count == 1
+
+
+@pytest.mark.test_server()
+def test__read_qualities(static_cache_dir, workdir, mocker):
+ """Test we read the qualities from the xml if no cache pickle is available.
+ This test also does some minor checks to ensure that the qualities are read correctly.
+ """
+
+ filename_mock = mocker.patch("openml.datasets.dataset._get_qualities_pickle_file")
+ pickle_mock = mocker.patch("openml.datasets.dataset.pickle")
+
+ filename_mock.return_value=os.path.join(workdir, "qualities.xml.pkl")
+ pickle_mock.load.side_effect = FileNotFoundError
+
+ qualities = openml.datasets.dataset._read_qualities(
+ os.path.join(
+ static_cache_dir,
+ "org",
+ "openml",
+ "test",
+ "datasets",
+ "2",
+ "qualities.xml",
+ ),
+ )
+ assert isinstance(qualities, dict)
+ assert len(qualities) == 106
+ assert pickle_mock.load.call_count == 0
+ assert pickle_mock.dump.call_count == 1
+
+
+
+def test__check_qualities():
+ qualities = [{"oml:name": "a", "oml:value": "0.5"}]
+ qualities = openml.datasets.dataset._check_qualities(qualities)
+ assert qualities["a"] == 0.5
+
+ qualities = [{"oml:name": "a", "oml:value": "null"}]
+ qualities = openml.datasets.dataset._check_qualities(qualities)
+ assert qualities["a"] != qualities["a"]
+
+ qualities = [{"oml:name": "a", "oml:value": None}]
+ qualities = openml.datasets.dataset._check_qualities(qualities)
+ assert qualities["a"] != qualities["a"]
diff --git a/tests/test_datasets/test_dataset_functions.py b/tests/test_datasets/test_dataset_functions.py
new file mode 100644
index 000000000..151a9ac23
--- /dev/null
+++ b/tests/test_datasets/test_dataset_functions.py
@@ -0,0 +1,2011 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+import itertools
+import os
+import random
+import shutil
+import time
+import uuid
+from itertools import product
+from pathlib import Path
+from typing import Iterable
+from unittest import mock
+
+import arff
+import numpy as np
+import pandas as pd
+import pytest
+import requests
+import requests_mock
+import scipy.sparse
+from oslo_concurrency import lockutils
+
+import openml
+from openml import OpenMLDataset
+from openml._api_calls import _download_minio_file
+from openml.datasets import edit_dataset, fork_dataset
+from openml.datasets.functions import (
+ DATASETS_CACHE_DIR_NAME,
+ _get_dataset_arff,
+ _get_dataset_description,
+ _get_dataset_features_file,
+ _get_dataset_parquet,
+ _get_dataset_qualities_file,
+ _get_online_dataset_arff,
+ _get_online_dataset_format,
+ _topic_add_dataset,
+ _topic_delete_dataset,
+ attributes_arff_from_df,
+ create_dataset,
+)
+from openml.exceptions import (
+ OpenMLHashException,
+ OpenMLNotAuthorizedError,
+ OpenMLPrivateDatasetError,
+ OpenMLServerException,
+ OpenMLServerNoResult,
+)
+from openml.tasks import TaskType, create_task
+from openml.testing import TestBase, create_request_response
+from openml.utils import _create_cache_directory_for_id, _tag_entity
+
+
+class TestOpenMLDataset(TestBase):
+ _multiprocess_can_split_ = True
+
+ def tearDown(self):
+ self._remove_pickle_files()
+ super().tearDown()
+
+ def _remove_pickle_files(self):
+ self.lock_path = os.path.join(openml.config.get_cache_directory(), "locks")
+ for did in ["-1", "2"]:
+ with lockutils.external_lock(
+ name=f"datasets.functions.get_dataset:{did}",
+ lock_path=self.lock_path,
+ ):
+ pickle_path = os.path.join(
+ openml.config.get_cache_directory(),
+ "datasets",
+ did,
+ "dataset.pkl.py3",
+ )
+ try:
+ os.remove(pickle_path)
+ except (OSError, FileNotFoundError):
+ # Replaced a bare except. Not sure why either of these would be acceptable.
+ pass
+
+ def _get_empty_param_for_dataset(self):
+ return {
+ "name": None,
+ "description": None,
+ "creator": None,
+ "contributor": None,
+ "collection_date": None,
+ "language": None,
+ "licence": None,
+ "default_target_attribute": None,
+ "row_id_attribute": None,
+ "ignore_attribute": None,
+ "citation": None,
+ "attributes": None,
+ "data": None,
+ }
+
+ def _check_dataset(self, dataset):
+ assert type(dataset) == dict
+ assert len(dataset) >= 2
+ assert "did" in dataset
+ assert isinstance(dataset["did"], int)
+ assert "status" in dataset
+ assert isinstance(dataset["status"], str)
+ assert dataset["status"] in ["in_preparation", "active", "deactivated"]
+
+ def _check_datasets(self, datasets):
+ for did in datasets:
+ self._check_dataset(datasets[did])
+
+ @pytest.mark.test_server()
+ def test_tag_untag_dataset(self):
+ tag = "test_tag_%d" % random.randint(1, 1000000)
+ all_tags = _tag_entity("data", 1, tag)
+ assert tag in all_tags
+ all_tags = _tag_entity("data", 1, tag, untag=True)
+ assert tag not in all_tags
+
+ @pytest.mark.test_server()
+ def test_list_datasets_length(self):
+ datasets = openml.datasets.list_datasets()
+ assert len(datasets) >= 100
+
+ @pytest.mark.test_server()
+ def test_list_datasets_paginate(self):
+ size = 10
+ max = 100
+ for i in range(0, max, size):
+ datasets = openml.datasets.list_datasets(offset=i, size=size)
+ assert len(datasets) == size
+ assert len(datasets.columns) >= 2
+ assert "did" in datasets.columns
+ assert datasets["did"].dtype == int
+ assert "status" in datasets.columns
+ assert datasets["status"].dtype == pd.CategoricalDtype(
+ categories=["in_preparation", "active", "deactivated"],
+ )
+
+ @pytest.mark.test_server()
+ def test_list_datasets_empty(self):
+ datasets = openml.datasets.list_datasets(tag="NoOneWouldUseThisTagAnyway")
+ assert datasets.empty
+
+ @pytest.mark.production_server()
+ def test_check_datasets_active(self):
+ # Have to test on live because there is no deactivated dataset on the test server.
+ self.use_production_server()
+ active = openml.datasets.check_datasets_active(
+ [2, 17, 79],
+ raise_error_if_not_exist=False,
+ )
+ assert active[2]
+ assert not active[17]
+ assert active.get(79) is None
+ self.assertRaisesRegex(
+ ValueError,
+ r"Could not find dataset\(s\) 79 in OpenML dataset list.",
+ openml.datasets.check_datasets_active,
+ [79],
+ )
+ openml.config.server = self.test_server
+
+ @pytest.mark.test_server()
+ def test_illegal_character_tag(self):
+ dataset = openml.datasets.get_dataset(1)
+ tag = "illegal_tag&"
+ try:
+ dataset.push_tag(tag)
+ raise AssertionError()
+ except openml.exceptions.OpenMLServerException as e:
+ assert e.code == 477
+
+ @pytest.mark.test_server()
+ def test_illegal_length_tag(self):
+ dataset = openml.datasets.get_dataset(1)
+ tag = "a" * 65
+ try:
+ dataset.push_tag(tag)
+ raise AssertionError()
+ except openml.exceptions.OpenMLServerException as e:
+ assert e.code == 477
+
+ @pytest.mark.production_server()
+ def test__name_to_id_with_deactivated(self):
+ """Check that an activated dataset is returned if an earlier deactivated one exists."""
+ self.use_production_server()
+ # /d/1 was deactivated
+ assert openml.datasets.functions._name_to_id("anneal") == 2
+ openml.config.server = self.test_server
+
+ @pytest.mark.production_server()
+ def test__name_to_id_with_multiple_active(self):
+ """With multiple active datasets, retrieve the least recent active."""
+ self.use_production_server()
+ assert openml.datasets.functions._name_to_id("iris") == 61
+
+ @pytest.mark.production_server()
+ def test__name_to_id_with_version(self):
+ """With multiple active datasets, retrieve the least recent active."""
+ self.use_production_server()
+ assert openml.datasets.functions._name_to_id("iris", version=3) == 969
+
+ @pytest.mark.production_server()
+ def test__name_to_id_with_multiple_active_error(self):
+ """With multiple active datasets, retrieve the least recent active."""
+ self.use_production_server()
+ self.assertRaisesRegex(
+ ValueError,
+ "Multiple active datasets exist with name 'iris'.",
+ openml.datasets.functions._name_to_id,
+ dataset_name="iris",
+ error_if_multiple=True,
+ )
+
+ @pytest.mark.test_server()
+ def test__name_to_id_name_does_not_exist(self):
+ """With multiple active datasets, retrieve the least recent active."""
+ self.assertRaisesRegex(
+ RuntimeError,
+ "No active datasets exist with name 'does_not_exist'.",
+ openml.datasets.functions._name_to_id,
+ dataset_name="does_not_exist",
+ )
+
+ @pytest.mark.test_server()
+ def test__name_to_id_version_does_not_exist(self):
+ """With multiple active datasets, retrieve the least recent active."""
+ self.assertRaisesRegex(
+ RuntimeError,
+ "No active datasets exist with name 'iris' and version '100000'.",
+ openml.datasets.functions._name_to_id,
+ dataset_name="iris",
+ version=100000,
+ )
+
+ @pytest.mark.test_server()
+ def test_get_datasets_by_name(self):
+ # did 1 and 2 on the test server:
+ dids = ["anneal", "kr-vs-kp"]
+ datasets = openml.datasets.get_datasets(dids)
+ assert len(datasets) == 2
+ _assert_datasets_retrieved_successfully([1, 2])
+
+ @pytest.mark.test_server()
+ def test_get_datasets_by_mixed(self):
+ # did 1 and 2 on the test server:
+ dids = ["anneal", 2]
+ datasets = openml.datasets.get_datasets(dids)
+ assert len(datasets) == 2
+ _assert_datasets_retrieved_successfully([1, 2])
+
+ @pytest.mark.test_server()
+ def test_get_datasets(self):
+ dids = [1, 2]
+ datasets = openml.datasets.get_datasets(dids)
+ assert len(datasets) == 2
+ _assert_datasets_retrieved_successfully([1, 2])
+
+ @pytest.mark.test_server()
+ def test_get_dataset_by_name(self):
+ dataset = openml.datasets.get_dataset("anneal")
+ assert type(dataset) == OpenMLDataset
+ assert dataset.dataset_id == 1
+ _assert_datasets_retrieved_successfully([1])
+
+ assert len(dataset.features) > 1
+ assert len(dataset.qualities) > 4
+
+ @pytest.mark.skip("Feature is experimental, can not test against stable server.")
+ def test_get_dataset_download_all_files(self):
+ # openml.datasets.get_dataset(id, download_all_files=True)
+ # check for expected files
+ # checking that no additional files are downloaded if
+ # the default (false) is used, seems covered by
+ # test_get_dataset_lazy
+ raise NotImplementedError
+
+ @pytest.mark.test_server()
+ def test_get_dataset_uint8_dtype(self):
+ dataset = openml.datasets.get_dataset(1)
+ assert type(dataset) == OpenMLDataset
+ assert dataset.name == "anneal"
+ df, _, _, _ = dataset.get_data()
+ assert df["carbon"].dtype == "uint8"
+
+ @pytest.mark.production_server()
+ def test_get_dataset_cannot_access_private_data(self):
+ # Issue324 Properly handle private datasets when trying to access them
+ self.use_production_server()
+ self.assertRaises(OpenMLPrivateDatasetError, openml.datasets.get_dataset, 45)
+
+ @pytest.mark.skip("Need to find dataset name of private dataset")
+ def test_dataset_by_name_cannot_access_private_data(self):
+ self.use_production_server()
+ self.assertRaises(OpenMLPrivateDatasetError, openml.datasets.get_dataset, "NAME_GOES_HERE")
+
+ @pytest.mark.test_server()
+ def test_get_dataset_lazy_all_functions(self):
+ """Test that all expected functionality is available without downloading the dataset."""
+ dataset = openml.datasets.get_dataset(1)
+ # We only tests functions as general integrity is tested by test_get_dataset_lazy
+
+ def ensure_absence_of_real_data():
+ assert not os.path.exists(
+ os.path.join(openml.config.get_cache_directory(), "datasets", "1", "dataset.arff")
+ )
+
+ tag = "test_lazy_tag_%d" % random.randint(1, 1000000)
+ dataset.push_tag(tag)
+ ensure_absence_of_real_data()
+
+ dataset.remove_tag(tag)
+ ensure_absence_of_real_data()
+
+ nominal_indices = dataset.get_features_by_type("nominal")
+ # fmt: off
+ correct = [0, 1, 2, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 35, 36, 37, 38]
+ # fmt: on
+ assert nominal_indices == correct
+ ensure_absence_of_real_data()
+
+ classes = dataset.retrieve_class_labels()
+ assert classes == ["1", "2", "3", "4", "5", "U"]
+ ensure_absence_of_real_data()
+
+ @pytest.mark.test_server()
+ def test_get_dataset_sparse(self):
+ dataset = openml.datasets.get_dataset(102)
+ X, *_ = dataset.get_data()
+ assert isinstance(X, pd.DataFrame)
+ assert all(isinstance(col, pd.SparseDtype) for col in X.dtypes)
+
+ @pytest.mark.test_server()
+ def test_download_rowid(self):
+ # Smoke test which checks that the dataset has the row-id set correctly
+ did = 44
+ dataset = openml.datasets.get_dataset(did)
+ assert dataset.row_id_attribute == "Counter"
+
+ @pytest.mark.test_server()
+ def test__get_dataset_description(self):
+ description = _get_dataset_description(self.workdir, 2)
+ assert isinstance(description, dict)
+ description_xml_path = os.path.join(self.workdir, "description.xml")
+ assert os.path.exists(description_xml_path)
+
+ @pytest.mark.test_server()
+ def test__getarff_path_dataset_arff(self):
+ openml.config.set_root_cache_directory(self.static_cache_dir)
+ description = _get_dataset_description(self.workdir, 2)
+ arff_path = _get_dataset_arff(description, cache_directory=self.workdir)
+ assert isinstance(arff_path, Path)
+ assert arff_path.exists()
+
+ def test__download_minio_file_object_does_not_exist(self):
+ self.assertRaisesRegex(
+ FileNotFoundError,
+ r"Object at .* does not exist",
+ _download_minio_file,
+ source="http://data.openml.org/dataset20/i_do_not_exist.pq",
+ destination=self.workdir,
+ exists_ok=True,
+ )
+
+ def test__download_minio_file_to_directory(self):
+ _download_minio_file(
+ source="http://data.openml.org/dataset20/dataset_20.pq",
+ destination=self.workdir,
+ exists_ok=True,
+ )
+ assert os.path.isfile(
+ os.path.join(self.workdir, "dataset_20.pq")
+ ), "_download_minio_file can save to a folder by copying the object name"
+
+ def test__download_minio_file_to_path(self):
+ file_destination = os.path.join(self.workdir, "custom.pq")
+ _download_minio_file(
+ source="http://data.openml.org/dataset20/dataset_20.pq",
+ destination=file_destination,
+ exists_ok=True,
+ )
+ assert os.path.isfile(
+ file_destination
+ ), "_download_minio_file can save to a folder by copying the object name"
+
+ def test__download_minio_file_raises_FileExists_if_destination_in_use(self):
+ file_destination = Path(self.workdir, "custom.pq")
+ file_destination.touch()
+
+ self.assertRaises(
+ FileExistsError,
+ _download_minio_file,
+ source="http://data.openml.org/dataset20/dataset_20.pq",
+ destination=str(file_destination),
+ exists_ok=False,
+ )
+
+ def test__download_minio_file_works_with_bucket_subdirectory(self):
+ file_destination = Path(self.workdir, "custom.pq")
+ _download_minio_file(
+ source="http://data.openml.org/dataset61/dataset_61.pq",
+ destination=file_destination,
+ exists_ok=True,
+ )
+ assert os.path.isfile(
+ file_destination
+ ), "_download_minio_file can download from subdirectories"
+
+
+ @mock.patch("openml._api_calls._download_minio_file")
+ @pytest.mark.test_server()
+ def test__get_dataset_parquet_is_cached(self, patch):
+ openml.config.set_root_cache_directory(self.static_cache_dir)
+ patch.side_effect = RuntimeError(
+ "_download_parquet_url should not be called when loading from cache",
+ )
+ description = {
+ "oml:parquet_url": "http://data.openml.org/dataset30/dataset_30.pq",
+ "oml:id": "30",
+ }
+ path = _get_dataset_parquet(description, cache_directory=None)
+ assert isinstance(path, Path), "_get_dataset_parquet returns a path"
+ assert path.is_file(), "_get_dataset_parquet returns path to real file"
+
+ def test__get_dataset_parquet_file_does_not_exist(self):
+ description = {
+ "oml:parquet_url": "http://data.openml.org/dataset20/does_not_exist.pq",
+ "oml:id": "20",
+ }
+ path = _get_dataset_parquet(description, cache_directory=self.workdir)
+ assert path is None, "_get_dataset_parquet returns None if no file is found"
+
+ def test__getarff_md5_issue(self):
+ description = {
+ "oml:id": 5,
+ "oml:md5_checksum": "abc",
+ "oml:url": "https://www.openml.org/data/download/61",
+ }
+ n = openml.config.connection_n_retries
+ openml.config.connection_n_retries = 1
+
+ self.assertRaisesRegex(
+ OpenMLHashException,
+ "Checksum of downloaded file is unequal to the expected checksum abc when downloading "
+ "https://www.openml.org/data/download/61. Raised when downloading dataset 5.",
+ _get_dataset_arff,
+ description,
+ )
+
+ openml.config.connection_n_retries = n
+
+ @pytest.mark.test_server()
+ def test__get_dataset_features(self):
+ features_file = _get_dataset_features_file(self.workdir, 2)
+ assert isinstance(features_file, Path)
+ features_xml_path = self.workdir / "features.xml"
+ assert features_xml_path.exists()
+
+ @pytest.mark.test_server()
+ def test__get_dataset_qualities(self):
+ qualities = _get_dataset_qualities_file(self.workdir, 2)
+ assert isinstance(qualities, Path)
+ qualities_xml_path = self.workdir / "qualities.xml"
+ assert qualities_xml_path.exists()
+
+ @pytest.mark.test_server()
+ def test_get_dataset_force_refresh_cache(self):
+ did_cache_dir = _create_cache_directory_for_id(
+ DATASETS_CACHE_DIR_NAME,
+ 2,
+ )
+ openml.datasets.get_dataset(2)
+ change_time = os.stat(did_cache_dir).st_mtime
+
+ # Test default
+ openml.datasets.get_dataset(2)
+ assert change_time == os.stat(did_cache_dir).st_mtime
+
+ # Test refresh
+ openml.datasets.get_dataset(2, force_refresh_cache=True)
+ assert change_time != os.stat(did_cache_dir).st_mtime
+
+ # Final clean up
+ openml.utils._remove_cache_dir_for_id(
+ DATASETS_CACHE_DIR_NAME,
+ did_cache_dir,
+ )
+
+ @pytest.mark.test_server()
+ def test_get_dataset_force_refresh_cache_clean_start(self):
+ did_cache_dir = _create_cache_directory_for_id(
+ DATASETS_CACHE_DIR_NAME,
+ 2,
+ )
+ # Clean up
+ openml.utils._remove_cache_dir_for_id(
+ DATASETS_CACHE_DIR_NAME,
+ did_cache_dir,
+ )
+
+ # Test clean start
+ openml.datasets.get_dataset(2, force_refresh_cache=True)
+ assert os.path.exists(did_cache_dir)
+
+ # Final clean up
+ openml.utils._remove_cache_dir_for_id(
+ DATASETS_CACHE_DIR_NAME,
+ did_cache_dir,
+ )
+
+ def test_deletion_of_cache_dir(self):
+ # Simple removal
+ did_cache_dir = _create_cache_directory_for_id(
+ DATASETS_CACHE_DIR_NAME,
+ 1,
+ )
+ assert os.path.exists(did_cache_dir)
+ openml.utils._remove_cache_dir_for_id(
+ DATASETS_CACHE_DIR_NAME,
+ did_cache_dir,
+ )
+ assert not os.path.exists(did_cache_dir)
+
+ # get_dataset_description is the only data guaranteed to be downloaded
+ @mock.patch("openml.datasets.functions._get_dataset_description")
+ @pytest.mark.test_server()
+ def test_deletion_of_cache_dir_faulty_download(self, patch):
+ patch.side_effect = Exception("Boom!")
+ self.assertRaisesRegex(Exception, "Boom!", openml.datasets.get_dataset, dataset_id=1)
+ datasets_cache_dir = os.path.join(openml.config.get_cache_directory(), "datasets")
+ assert len(os.listdir(datasets_cache_dir)) == 0
+
+ @pytest.mark.test_server()
+ def test_publish_dataset(self):
+ arff_file_path = self.static_cache_dir / "org" / "openml" / "test" / "datasets" / "2" / "dataset.arff"
+ dataset = OpenMLDataset(
+ "anneal",
+ "test",
+ data_format="arff",
+ version=1,
+ licence="public",
+ default_target_attribute="class",
+ data_file=arff_file_path,
+ )
+ dataset.publish()
+ TestBase._mark_entity_for_removal("data", dataset.dataset_id)
+ TestBase.logger.info(
+ f"collected from {__file__.split('/')[-1]}: {dataset.dataset_id}",
+ )
+ assert isinstance(dataset.dataset_id, int)
+
+ @pytest.mark.test_server()
+ def test__retrieve_class_labels(self):
+ openml.config.set_root_cache_directory(self.static_cache_dir)
+ labels = openml.datasets.get_dataset(2).retrieve_class_labels()
+ assert labels == ["1", "2", "3", "4", "5", "U"]
+
+ labels = openml.datasets.get_dataset(2).retrieve_class_labels(
+ target_name="product-type",
+ )
+ assert labels == ["C", "H", "G"]
+
+ # Test workaround for string-typed class labels
+ custom_ds = openml.datasets.get_dataset(2)
+ custom_ds.features[31].data_type = "string"
+ labels = custom_ds.retrieve_class_labels(target_name=custom_ds.features[31].name)
+ assert labels == ["COIL", "SHEET"]
+
+ @pytest.mark.test_server()
+ def test_upload_dataset_with_url(self):
+ dataset = OpenMLDataset(
+ f"{self._get_sentinel()}-UploadTestWithURL",
+ "test",
+ data_format="arff",
+ version=1,
+ url="https://www.openml.org/data/download/61/dataset_61_iris.arff",
+ )
+ dataset.publish()
+ TestBase._mark_entity_for_removal("data", dataset.dataset_id)
+ TestBase.logger.info(
+ f"collected from {__file__.split('/')[-1]}: {dataset.dataset_id}",
+ )
+ assert isinstance(dataset.dataset_id, int)
+
+ def _assert_status_of_dataset(self, *, did: int, status: str):
+ """Asserts there is exactly one dataset with id `did` and its current status is `status`"""
+ # need to use listing fn, as this is immune to cache
+ result = openml.datasets.list_datasets(data_id=[did], status="all")
+ result = result.to_dict(orient="index")
+ # I think we should drop the test that one result is returned,
+ # the server should never return multiple results?
+ assert len(result) == 1
+ assert result[did]["status"] == status
+
+ @pytest.mark.skipif(
+ not os.environ.get(openml.config.OPENML_TEST_SERVER_ADMIN_KEY_ENV_VAR),
+ reason="Test requires admin key. Set OPENML_TEST_SERVER_ADMIN_KEY environment variable.",
+ )
+ @pytest.mark.flaky()
+ @pytest.mark.test_server()
+ def test_data_status(self):
+ dataset = OpenMLDataset(
+ f"{self._get_sentinel()}-UploadTestWithURL",
+ "test",
+ "ARFF",
+ version=1,
+ url="https://www.openml.org/data/download/61/dataset_61_iris.arff",
+ )
+ dataset.publish()
+ TestBase._mark_entity_for_removal("data", dataset.id)
+ TestBase.logger.info(f"collected from {__file__.split('/')[-1]}: {dataset.id}")
+ did = dataset.id
+
+ # admin key for test server (only admins can activate datasets.
+ # all users can deactivate their own datasets)
+ openml.config.apikey = TestBase.admin_key
+
+ openml.datasets.status_update(did, "active")
+ self._assert_status_of_dataset(did=did, status="active")
+
+ openml.datasets.status_update(did, "deactivated")
+ self._assert_status_of_dataset(did=did, status="deactivated")
+
+ openml.datasets.status_update(did, "active")
+ self._assert_status_of_dataset(did=did, status="active")
+
+ with pytest.raises(ValueError):
+ openml.datasets.status_update(did, "in_preparation")
+ self._assert_status_of_dataset(did=did, status="active")
+
+ def test_attributes_arff_from_df(self):
+ # DataFrame case
+ df = pd.DataFrame(
+ [[1, 1.0, "xxx", "A", True], [2, 2.0, "yyy", "B", False]],
+ columns=["integer", "floating", "string", "category", "boolean"],
+ )
+ df["category"] = df["category"].astype("category")
+ attributes = attributes_arff_from_df(df)
+ assert attributes == [
+ ("integer", "INTEGER"),
+ ("floating", "REAL"),
+ ("string", "STRING"),
+ ("category", ["A", "B"]),
+ ("boolean", ["True", "False"]),
+ ]
+ # DataFrame with Sparse columns case
+ df = pd.DataFrame(
+ {
+ "integer": pd.arrays.SparseArray([1, 2, 0], fill_value=0),
+ "floating": pd.arrays.SparseArray([1.0, 2.0, 0], fill_value=0.0),
+ },
+ )
+ df["integer"] = df["integer"].astype(np.int64)
+ attributes = attributes_arff_from_df(df)
+ assert attributes == [("integer", "INTEGER"), ("floating", "REAL")]
+
+ def test_attributes_arff_from_df_numeric_column(self):
+ # Test column names are automatically converted to str if needed (#819)
+ df = pd.DataFrame({0: [1, 2, 3], 0.5: [4, 5, 6], "target": [0, 1, 1]})
+ attributes = attributes_arff_from_df(df)
+ assert attributes == [
+ ("0", "INTEGER"),
+ ("0.5", "INTEGER"),
+ ("target", "INTEGER"),
+ ]
+
+ def test_attributes_arff_from_df_mixed_dtype_categories(self):
+ # liac-arff imposed categorical attributes to be of sting dtype. We
+ # raise an error if this is not the case.
+ df = pd.DataFrame([[1], ["2"], [3.0]])
+ df[0] = df[0].astype("category")
+ err_msg = "The column '0' of the dataframe is of 'category' dtype."
+ with pytest.raises(ValueError, match=err_msg):
+ attributes_arff_from_df(df)
+
+ def test_attributes_arff_from_df_unknown_dtype(self):
+ # check that an error is raised when the dtype is not supptagorted by
+ # liac-arff
+ data = [
+ [[1], ["2"], [3.0]],
+ [pd.Timestamp("2012-05-01"), pd.Timestamp("2012-05-02")],
+ ]
+ dtype = ["mixed-integer", "datetime64"]
+ for arr, dt in zip(data, dtype):
+ df = pd.DataFrame(arr)
+ err_msg = (
+ f"The dtype '{dt}' of the column '0' is not currently " "supported by liac-arff"
+ )
+ with pytest.raises(ValueError, match=err_msg):
+ attributes_arff_from_df(df)
+
+ @pytest.mark.test_server()
+ def test_create_dataset_numpy(self):
+ data = np.array([[1, 2, 3], [1.2, 2.5, 3.8], [2, 5, 8], [0, 1, 0]]).T
+
+ attributes = [(f"col_{i}", "REAL") for i in range(data.shape[1])]
+
+ dataset = create_dataset(
+ name=f"{self._get_sentinel()}-NumPy_testing_dataset",
+ description="Synthetic dataset created from a NumPy array",
+ creator="OpenML tester",
+ contributor=None,
+ collection_date="01-01-2018",
+ language="English",
+ licence="MIT",
+ default_target_attribute=f"col_{data.shape[1] - 1}",
+ row_id_attribute=None,
+ ignore_attribute=None,
+ citation="None",
+ attributes=attributes,
+ data=data,
+ version_label="test",
+ original_data_url="http://openml.github.io/openml-python",
+ paper_url="http://openml.github.io/openml-python",
+ )
+
+ dataset.publish()
+ TestBase._mark_entity_for_removal("data", dataset.id)
+ TestBase.logger.info(f"collected from {__file__.split('/')[-1]}: {dataset.id}")
+
+ assert (
+ _get_online_dataset_arff(dataset.id) == dataset._dataset
+ ), "Uploaded arff does not match original one"
+ assert _get_online_dataset_format(dataset.id) == "arff", "Wrong format for dataset"
+
+ @pytest.mark.test_server()
+ def test_create_dataset_list(self):
+ data = [
+ ["a", "sunny", 85.0, 85.0, "FALSE", "no"],
+ ["b", "sunny", 80.0, 90.0, "TRUE", "no"],
+ ["c", "overcast", 83.0, 86.0, "FALSE", "yes"],
+ ["d", "rainy", 70.0, 96.0, "FALSE", "yes"],
+ ["e", "rainy", 68.0, 80.0, "FALSE", "yes"],
+ ["f", "rainy", 65.0, 70.0, "TRUE", "no"],
+ ["g", "overcast", 64.0, 65.0, "TRUE", "yes"],
+ ["h", "sunny", 72.0, 95.0, "FALSE", "no"],
+ ["i", "sunny", 69.0, 70.0, "FALSE", "yes"],
+ ["j", "rainy", 75.0, 80.0, "FALSE", "yes"],
+ ["k", "sunny", 75.0, 70.0, "TRUE", "yes"],
+ ["l", "overcast", 72.0, 90.0, "TRUE", "yes"],
+ ["m", "overcast", 81.0, 75.0, "FALSE", "yes"],
+ ["n", "rainy", 71.0, 91.0, "TRUE", "no"],
+ ]
+
+ attributes = [
+ ("rnd_str", "STRING"),
+ ("outlook", ["sunny", "overcast", "rainy"]),
+ ("temperature", "REAL"),
+ ("humidity", "REAL"),
+ ("windy", ["TRUE", "FALSE"]),
+ ("play", ["yes", "no"]),
+ ]
+
+ dataset = create_dataset(
+ name=f"{self._get_sentinel()}-ModifiedWeather",
+ description=("Testing dataset upload when the data is a list of lists"),
+ creator="OpenML test",
+ contributor=None,
+ collection_date="21-09-2018",
+ language="English",
+ licence="MIT",
+ default_target_attribute="play",
+ row_id_attribute=None,
+ ignore_attribute=None,
+ citation="None",
+ attributes=attributes,
+ data=data,
+ version_label="test",
+ original_data_url="http://openml.github.io/openml-python",
+ paper_url="http://openml.github.io/openml-python",
+ )
+
+ dataset.publish()
+ TestBase._mark_entity_for_removal("data", dataset.id)
+ TestBase.logger.info(f"collected from {__file__.split('/')[-1]}: {dataset.id}")
+ assert (
+ _get_online_dataset_arff(dataset.id) == dataset._dataset
+ ), "Uploaded ARFF does not match original one"
+ assert _get_online_dataset_format(dataset.id) == "arff", "Wrong format for dataset"
+
+ @pytest.mark.test_server()
+ def test_create_dataset_sparse(self):
+ # test the scipy.sparse.coo_matrix
+ sparse_data = scipy.sparse.coo_matrix(
+ (
+ [0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0],
+ ([0, 1, 1, 2, 2, 3, 3], [0, 1, 2, 0, 2, 0, 1]),
+ ),
+ )
+
+ column_names = [
+ ("input1", "REAL"),
+ ("input2", "REAL"),
+ ("y", "REAL"),
+ ]
+
+ xor_dataset = create_dataset(
+ name=f"{self._get_sentinel()}-XOR",
+ description="Dataset representing the XOR operation",
+ creator=None,
+ contributor=None,
+ collection_date=None,
+ language="English",
+ licence=None,
+ default_target_attribute="y",
+ row_id_attribute=None,
+ ignore_attribute=None,
+ citation=None,
+ attributes=column_names,
+ data=sparse_data,
+ version_label="test",
+ )
+
+ xor_dataset.publish()
+ TestBase._mark_entity_for_removal("data", xor_dataset.id)
+ TestBase.logger.info(
+ f"collected from {__file__.split('/')[-1]}: {xor_dataset.id}",
+ )
+ assert (
+ _get_online_dataset_arff(xor_dataset.id) == xor_dataset._dataset
+ ), "Uploaded ARFF does not match original one"
+ assert (
+ _get_online_dataset_format(xor_dataset.id) == "sparse_arff"
+ ), "Wrong format for dataset"
+
+ # test the list of dicts sparse representation
+ sparse_data = [{0: 0.0}, {1: 1.0, 2: 1.0}, {0: 1.0, 2: 1.0}, {0: 1.0, 1: 1.0}]
+
+ xor_dataset = create_dataset(
+ name=f"{self._get_sentinel()}-XOR",
+ description="Dataset representing the XOR operation",
+ creator=None,
+ contributor=None,
+ collection_date=None,
+ language="English",
+ licence=None,
+ default_target_attribute="y",
+ row_id_attribute=None,
+ ignore_attribute=None,
+ citation=None,
+ attributes=column_names,
+ data=sparse_data,
+ version_label="test",
+ )
+
+ xor_dataset.publish()
+ TestBase._mark_entity_for_removal("data", xor_dataset.id)
+ TestBase.logger.info(
+ f"collected from {__file__.split('/')[-1]}: {xor_dataset.id}",
+ )
+ assert (
+ _get_online_dataset_arff(xor_dataset.id) == xor_dataset._dataset
+ ), "Uploaded ARFF does not match original one"
+ assert (
+ _get_online_dataset_format(xor_dataset.id) == "sparse_arff"
+ ), "Wrong format for dataset"
+
+ def test_create_invalid_dataset(self):
+ data = [
+ "sunny",
+ "overcast",
+ "overcast",
+ "rainy",
+ "rainy",
+ "rainy",
+ "overcast",
+ "sunny",
+ "sunny",
+ "rainy",
+ "sunny",
+ "overcast",
+ "overcast",
+ "rainy",
+ ]
+
+ param = self._get_empty_param_for_dataset()
+ param["data"] = data
+
+ self.assertRaises(ValueError, create_dataset, **param)
+
+ param["data"] = data[0]
+ self.assertRaises(ValueError, create_dataset, **param)
+
+ @pytest.mark.test_server()
+ def test_get_online_dataset_arff(self):
+ dataset_id = 128 # iris -- one of the few datasets without parquet file
+ # lazy loading not used as arff file is checked.
+ dataset = openml.datasets.get_dataset(dataset_id, download_data=True)
+ decoder = arff.ArffDecoder()
+ # check if the arff from the dataset is
+ # the same as the arff from _get_arff function
+ d_format = (dataset.format).lower()
+
+ assert dataset._get_arff(d_format) == decoder.decode(
+ _get_online_dataset_arff(dataset_id),
+ encode_nominal=True,
+ return_type=arff.DENSE if d_format == "arff" else arff.COO,
+ ), "ARFF files are not equal"
+
+ @pytest.mark.test_server()
+ def test_topic_api_error(self):
+ # Check server exception when non-admin accessses apis
+ self.assertRaisesRegex(
+ OpenMLServerException,
+ "Topic can only be added/removed by admin.",
+ _topic_add_dataset,
+ data_id=31,
+ topic="business",
+ )
+ # Check server exception when non-admin accessses apis
+ self.assertRaisesRegex(
+ OpenMLServerException,
+ "Topic can only be added/removed by admin.",
+ _topic_delete_dataset,
+ data_id=31,
+ topic="business",
+ )
+
+ @pytest.mark.test_server()
+ def test_get_online_dataset_format(self):
+ # Phoneme dataset
+ dataset_id = 77
+ dataset = openml.datasets.get_dataset(dataset_id)
+
+ assert dataset.format.lower() == _get_online_dataset_format(
+ dataset_id
+ ), "The format of the ARFF files is different"
+
+ @pytest.mark.test_server()
+ def test_create_dataset_pandas(self):
+ data = [
+ ["a", "sunny", 85.0, 85.0, "FALSE", "no"],
+ ["b", "sunny", 80.0, 90.0, "TRUE", "no"],
+ ["c", "overcast", 83.0, 86.0, "FALSE", "yes"],
+ ["d", "rainy", 70.0, 96.0, "FALSE", "yes"],
+ ["e", "rainy", 68.0, 80.0, "FALSE", "yes"],
+ ]
+ column_names = [
+ "rnd_str",
+ "outlook",
+ "temperature",
+ "humidity",
+ "windy",
+ "play",
+ ]
+ df = pd.DataFrame(data, columns=column_names)
+ # enforce the type of each column
+ df["outlook"] = df["outlook"].astype("category")
+ df["windy"] = df["windy"].astype("bool")
+ df["play"] = df["play"].astype("category")
+ # meta-information
+ name = f"{self._get_sentinel()}-pandas_testing_dataset"
+ description = "Synthetic dataset created from a Pandas DataFrame"
+ creator = "OpenML tester"
+ collection_date = "01-01-2018"
+ language = "English"
+ licence = "MIT"
+ citation = "None"
+ original_data_url = "http://openml.github.io/openml-python"
+ paper_url = "http://openml.github.io/openml-python"
+ dataset = openml.datasets.functions.create_dataset(
+ name=name,
+ description=description,
+ creator=creator,
+ contributor=None,
+ collection_date=collection_date,
+ language=language,
+ licence=licence,
+ default_target_attribute="play",
+ row_id_attribute=None,
+ ignore_attribute=None,
+ citation=citation,
+ attributes="auto",
+ data=df,
+ version_label="test",
+ original_data_url=original_data_url,
+ paper_url=paper_url,
+ )
+ dataset.publish()
+ TestBase._mark_entity_for_removal("data", dataset.id)
+ TestBase.logger.info(f"collected from {__file__.split('/')[-1]}: {dataset.id}")
+ assert (
+ _get_online_dataset_arff(dataset.id) == dataset._dataset
+ ), "Uploaded ARFF does not match original one"
+
+ # Check that DataFrame with Sparse columns are supported properly
+ sparse_data = scipy.sparse.coo_matrix(
+ (
+ [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0],
+ ([0, 1, 1, 2, 2, 3, 3], [0, 1, 2, 0, 2, 0, 1]),
+ ),
+ )
+ column_names = ["input1", "input2", "y"]
+ df = pd.DataFrame.sparse.from_spmatrix(sparse_data, columns=column_names)
+ # meta-information
+ description = "Synthetic dataset created from a Pandas DataFrame with Sparse columns"
+ dataset = openml.datasets.functions.create_dataset(
+ name=name,
+ description=description,
+ creator=creator,
+ contributor=None,
+ collection_date=collection_date,
+ language=language,
+ licence=licence,
+ default_target_attribute="y",
+ row_id_attribute=None,
+ ignore_attribute=None,
+ citation=citation,
+ attributes="auto",
+ data=df,
+ version_label="test",
+ original_data_url=original_data_url,
+ paper_url=paper_url,
+ )
+ dataset.publish()
+ TestBase._mark_entity_for_removal("data", dataset.id)
+ TestBase.logger.info(f"collected from {__file__.split('/')[-1]}: {dataset.id}")
+ assert (
+ _get_online_dataset_arff(dataset.id) == dataset._dataset
+ ), "Uploaded ARFF does not match original one"
+ assert _get_online_dataset_format(dataset.id) == "sparse_arff", "Wrong format for dataset"
+
+ # Check that we can overwrite the attributes
+ data = [["a"], ["b"], ["c"], ["d"], ["e"]]
+ column_names = ["rnd_str"]
+ df = pd.DataFrame(data, columns=column_names)
+ df["rnd_str"] = df["rnd_str"].astype("category")
+ attributes = {"rnd_str": ["a", "b", "c", "d", "e", "f", "g"]}
+ dataset = openml.datasets.functions.create_dataset(
+ name=name,
+ description=description,
+ creator=creator,
+ contributor=None,
+ collection_date=collection_date,
+ language=language,
+ licence=licence,
+ default_target_attribute="rnd_str",
+ row_id_attribute=None,
+ ignore_attribute=None,
+ citation=citation,
+ attributes=attributes,
+ data=df,
+ version_label="test",
+ original_data_url=original_data_url,
+ paper_url=paper_url,
+ )
+ dataset.publish()
+ TestBase._mark_entity_for_removal("data", dataset.id)
+ TestBase.logger.info(f"collected from {__file__.split('/')[-1]}: {dataset.id}")
+ downloaded_data = _get_online_dataset_arff(dataset.id)
+ assert downloaded_data == dataset._dataset, "Uploaded ARFF does not match original one"
+ assert "@ATTRIBUTE rnd_str {a, b, c, d, e, f, g}" in downloaded_data
+
+ def test_ignore_attributes_dataset(self):
+ data = [
+ ["a", "sunny", 85.0, 85.0, "FALSE", "no"],
+ ["b", "sunny", 80.0, 90.0, "TRUE", "no"],
+ ["c", "overcast", 83.0, 86.0, "FALSE", "yes"],
+ ["d", "rainy", 70.0, 96.0, "FALSE", "yes"],
+ ["e", "rainy", 68.0, 80.0, "FALSE", "yes"],
+ ]
+ column_names = [
+ "rnd_str",
+ "outlook",
+ "temperature",
+ "humidity",
+ "windy",
+ "play",
+ ]
+ df = pd.DataFrame(data, columns=column_names)
+ # enforce the type of each column
+ df["outlook"] = df["outlook"].astype("category")
+ df["windy"] = df["windy"].astype("bool")
+ df["play"] = df["play"].astype("category")
+ # meta-information
+ name = f"{self._get_sentinel()}-pandas_testing_dataset"
+ description = "Synthetic dataset created from a Pandas DataFrame"
+ creator = "OpenML tester"
+ collection_date = "01-01-2018"
+ language = "English"
+ licence = "MIT"
+ default_target_attribute = "play"
+ citation = "None"
+ original_data_url = "http://openml.github.io/openml-python"
+ paper_url = "http://openml.github.io/openml-python"
+
+ # we use the create_dataset function which call the OpenMLDataset
+ # constructor
+ # pass a string to ignore_attribute
+ dataset = openml.datasets.functions.create_dataset(
+ name=name,
+ description=description,
+ creator=creator,
+ contributor=None,
+ collection_date=collection_date,
+ language=language,
+ licence=licence,
+ default_target_attribute=default_target_attribute,
+ row_id_attribute=None,
+ ignore_attribute="outlook",
+ citation=citation,
+ attributes="auto",
+ data=df,
+ version_label="test",
+ original_data_url=original_data_url,
+ paper_url=paper_url,
+ )
+ assert dataset.ignore_attribute == ["outlook"]
+
+ # pass a list to ignore_attribute
+ ignore_attribute = ["outlook", "windy"]
+ dataset = openml.datasets.functions.create_dataset(
+ name=name,
+ description=description,
+ creator=creator,
+ contributor=None,
+ collection_date=collection_date,
+ language=language,
+ licence=licence,
+ default_target_attribute=default_target_attribute,
+ row_id_attribute=None,
+ ignore_attribute=ignore_attribute,
+ citation=citation,
+ attributes="auto",
+ data=df,
+ version_label="test",
+ original_data_url=original_data_url,
+ paper_url=paper_url,
+ )
+ assert dataset.ignore_attribute == ignore_attribute
+
+ # raise an error if unknown type
+ err_msg = "Wrong data type for ignore_attribute. Should be list."
+ with pytest.raises(ValueError, match=err_msg):
+ openml.datasets.functions.create_dataset(
+ name=name,
+ description=description,
+ creator=creator,
+ contributor=None,
+ collection_date=collection_date,
+ language=language,
+ licence=licence,
+ default_target_attribute=default_target_attribute,
+ row_id_attribute=None,
+ ignore_attribute=("outlook", "windy"),
+ citation=citation,
+ attributes="auto",
+ data=df,
+ version_label="test",
+ original_data_url=original_data_url,
+ paper_url=paper_url,
+ )
+
+ @pytest.mark.test_server()
+ def test_publish_fetch_ignore_attribute(self):
+ """Test to upload and retrieve dataset and check ignore_attributes"""
+ data = [
+ ["a", "sunny", 85.0, 85.0, "FALSE", "no"],
+ ["b", "sunny", 80.0, 90.0, "TRUE", "no"],
+ ["c", "overcast", 83.0, 86.0, "FALSE", "yes"],
+ ["d", "rainy", 70.0, 96.0, "FALSE", "yes"],
+ ["e", "rainy", 68.0, 80.0, "FALSE", "yes"],
+ ]
+ column_names = [
+ "rnd_str",
+ "outlook",
+ "temperature",
+ "humidity",
+ "windy",
+ "play",
+ ]
+ df = pd.DataFrame(data, columns=column_names)
+ # enforce the type of each column
+ df["outlook"] = df["outlook"].astype("category")
+ df["windy"] = df["windy"].astype("bool")
+ df["play"] = df["play"].astype("category")
+ # meta-information
+ name = f"{self._get_sentinel()}-pandas_testing_dataset"
+ description = "Synthetic dataset created from a Pandas DataFrame"
+ creator = "OpenML tester"
+ collection_date = "01-01-2018"
+ language = "English"
+ licence = "MIT"
+ default_target_attribute = "play"
+ citation = "None"
+ original_data_url = "http://openml.github.io/openml-python"
+ paper_url = "http://openml.github.io/openml-python"
+
+ # pass a list to ignore_attribute
+ ignore_attribute = ["outlook", "windy"]
+ dataset = openml.datasets.functions.create_dataset(
+ name=name,
+ description=description,
+ creator=creator,
+ contributor=None,
+ collection_date=collection_date,
+ language=language,
+ licence=licence,
+ default_target_attribute=default_target_attribute,
+ row_id_attribute=None,
+ ignore_attribute=ignore_attribute,
+ citation=citation,
+ attributes="auto",
+ data=df,
+ version_label="test",
+ original_data_url=original_data_url,
+ paper_url=paper_url,
+ )
+
+ # publish dataset
+ dataset.publish()
+ TestBase._mark_entity_for_removal("data", dataset.id)
+ TestBase.logger.info(f"collected from {__file__.split('/')[-1]}: {dataset.id}")
+ # test if publish was successful
+ assert isinstance(dataset.id, int)
+
+ downloaded_dataset = self._wait_for_dataset_being_processed(dataset.id)
+ assert downloaded_dataset.ignore_attribute == ignore_attribute
+
+ def _wait_for_dataset_being_processed(
+ self, dataset_id, poll_delay: int = 10, max_waiting_time_seconds: int = 600
+ ):
+ start_time = time.time()
+ while (time.time() - start_time) < max_waiting_time_seconds:
+ try:
+ # being able to download qualities is a sign that the dataset is processed
+ return openml.datasets.get_dataset(dataset_id, download_qualities=True)
+ except OpenMLServerException as e:
+ TestBase.logger.error(
+ f"Failed to fetch dataset:{dataset_id} with '{e!s}'.",
+ )
+ time.sleep(poll_delay)
+ raise ValueError(f"TIMEOUT: Failed to fetch uploaded dataset - {dataset_id}")
+
+ def test_create_dataset_row_id_attribute_error(self):
+ # meta-information
+ name = f"{self._get_sentinel()}-pandas_testing_dataset"
+ description = "Synthetic dataset created from a Pandas DataFrame"
+ creator = "OpenML tester"
+ collection_date = "01-01-2018"
+ language = "English"
+ licence = "MIT"
+ default_target_attribute = "target"
+ citation = "None"
+ original_data_url = "http://openml.github.io/openml-python"
+ paper_url = "http://openml.github.io/openml-python"
+ # Check that the index name is well inferred.
+ data = [["a", 1, 0], ["b", 2, 1], ["c", 3, 0], ["d", 4, 1], ["e", 5, 0]]
+ column_names = ["rnd_str", "integer", "target"]
+ df = pd.DataFrame(data, columns=column_names)
+ # affecting row_id_attribute to an unknown column should raise an error
+ err_msg = "should be one of the data attribute."
+ with pytest.raises(ValueError, match=err_msg):
+ openml.datasets.functions.create_dataset(
+ name=name,
+ description=description,
+ creator=creator,
+ contributor=None,
+ collection_date=collection_date,
+ language=language,
+ licence=licence,
+ default_target_attribute=default_target_attribute,
+ ignore_attribute=None,
+ citation=citation,
+ attributes="auto",
+ data=df,
+ row_id_attribute="unknown_row_id",
+ version_label="test",
+ original_data_url=original_data_url,
+ paper_url=paper_url,
+ )
+
+ @pytest.mark.test_server()
+ def test_create_dataset_row_id_attribute_inference(self):
+ # meta-information
+ name = f"{self._get_sentinel()}-pandas_testing_dataset"
+ description = "Synthetic dataset created from a Pandas DataFrame"
+ creator = "OpenML tester"
+ collection_date = "01-01-2018"
+ language = "English"
+ licence = "MIT"
+ default_target_attribute = "target"
+ citation = "None"
+ original_data_url = "http://openml.github.io/openml-python"
+ paper_url = "http://openml.github.io/openml-python"
+ # Check that the index name is well inferred.
+ data = [["a", 1, 0], ["b", 2, 1], ["c", 3, 0], ["d", 4, 1], ["e", 5, 0]]
+ column_names = ["rnd_str", "integer", "target"]
+ df = pd.DataFrame(data, columns=column_names)
+ row_id_attr = [None, "integer"]
+ df_index_name = [None, "index_name"]
+ expected_row_id = [None, "index_name", "integer", "integer"]
+ for output_row_id, (row_id, index_name) in zip(
+ expected_row_id,
+ product(row_id_attr, df_index_name),
+ ):
+ df.index.name = index_name
+ dataset = openml.datasets.functions.create_dataset(
+ name=name,
+ description=description,
+ creator=creator,
+ contributor=None,
+ collection_date=collection_date,
+ language=language,
+ licence=licence,
+ default_target_attribute=default_target_attribute,
+ ignore_attribute=None,
+ citation=citation,
+ attributes="auto",
+ data=df,
+ row_id_attribute=row_id,
+ version_label="test",
+ original_data_url=original_data_url,
+ paper_url=paper_url,
+ )
+ assert dataset.row_id_attribute == output_row_id
+ dataset.publish()
+ TestBase._mark_entity_for_removal("data", dataset.id)
+ TestBase.logger.info(
+ f"collected from {__file__.split('/')[-1]}: {dataset.id}",
+ )
+ arff_dataset = arff.loads(_get_online_dataset_arff(dataset.id))
+ arff_data = np.array(arff_dataset["data"], dtype=object)
+ # if we set the name of the index then the index will be added to
+ # the data
+ expected_shape = (5, 3) if index_name is None else (5, 4)
+ assert arff_data.shape == expected_shape
+
+ def test_create_dataset_attributes_auto_without_df(self):
+ # attributes cannot be inferred without passing a dataframe
+ data = np.array([[1, 2, 3], [1.2, 2.5, 3.8], [2, 5, 8], [0, 1, 0]]).T
+ attributes = "auto"
+ name = "NumPy_testing_dataset"
+ description = "Synthetic dataset created from a NumPy array"
+ creator = "OpenML tester"
+ collection_date = "01-01-2018"
+ language = "English"
+ licence = "MIT"
+ default_target_attribute = f"col_{data.shape[1] - 1}"
+ citation = "None"
+ original_data_url = "http://openml.github.io/openml-python"
+ paper_url = "http://openml.github.io/openml-python"
+ err_msg = "Automatically inferring attributes requires a pandas"
+ with pytest.raises(ValueError, match=err_msg):
+ openml.datasets.functions.create_dataset(
+ name=name,
+ description=description,
+ creator=creator,
+ contributor=None,
+ collection_date=collection_date,
+ language=language,
+ licence=licence,
+ default_target_attribute=default_target_attribute,
+ row_id_attribute=None,
+ ignore_attribute=None,
+ citation=citation,
+ attributes=attributes,
+ data=data,
+ version_label="test",
+ original_data_url=original_data_url,
+ paper_url=paper_url,
+ )
+
+ @pytest.mark.test_server()
+ def test_list_qualities(self):
+ qualities = openml.datasets.list_qualities()
+ assert isinstance(qualities, list) is True
+ assert all(isinstance(q, str) for q in qualities) is True
+
+ @pytest.mark.test_server()
+ def test_get_dataset_cache_format_pickle(self):
+ dataset = openml.datasets.get_dataset(1)
+ dataset.get_data()
+
+ assert type(dataset) == OpenMLDataset
+ assert dataset.name == "anneal"
+ assert len(dataset.features) > 1
+ assert len(dataset.qualities) > 4
+
+ X, y, categorical, attribute_names = dataset.get_data()
+ assert isinstance(X, pd.DataFrame)
+ assert X.shape == (898, 39)
+ assert len(categorical) == X.shape[1]
+ assert len(attribute_names) == X.shape[1]
+
+ @pytest.mark.test_server()
+ def test_get_dataset_cache_format_feather(self):
+ # This test crashed due to using the parquet file by default, which is downloaded
+ # from minio. However, there is a mismatch between OpenML test server and minio IDs.
+ # The parquet file on minio with ID 128 is not the iris dataset from the test server.
+ dataset = openml.datasets.get_dataset(128, cache_format="feather")
+ # Workaround
+ dataset._parquet_url = None
+ dataset.parquet_file = None
+ dataset.get_data()
+
+ # Check if dataset is written to cache directory using feather
+ cache_dir = openml.config.get_cache_directory()
+ cache_dir_for_id = os.path.join(cache_dir, "datasets", "128")
+ feather_file = os.path.join(cache_dir_for_id, "dataset.feather")
+ pickle_file = os.path.join(cache_dir_for_id, "dataset.feather.attributes.pkl.py3")
+ data = pd.read_feather(feather_file)
+ assert os.path.isfile(feather_file), "Feather file is missing"
+ assert os.path.isfile(pickle_file), "Attributes pickle file is missing"
+ assert data.shape == (150, 5)
+
+ # Check if get_data is able to retrieve feather data
+ assert type(dataset) == OpenMLDataset
+ assert dataset.name == "iris"
+ assert len(dataset.features) > 1
+ assert len(dataset.qualities) > 4
+
+ X, y, categorical, attribute_names = dataset.get_data()
+ assert isinstance(X, pd.DataFrame)
+ assert X.shape == (150, 5)
+ assert len(categorical) == X.shape[1]
+ assert len(attribute_names) == X.shape[1]
+
+ @pytest.mark.test_server()
+ def test_data_edit_non_critical_field(self):
+ # Case 1
+ # All users can edit non-critical fields of datasets
+ desc = (
+ "This data sets consists of 3 different types of irises' "
+ "(Setosa, Versicolour, and Virginica) petal and sepal length,"
+ " stored in a 150x4 numpy.ndarray"
+ )
+ did = 128
+ result = edit_dataset(
+ did,
+ description=desc,
+ creator="R.A.Fisher",
+ collection_date="1937",
+ citation="The use of multiple measurements in taxonomic problems",
+ language="English",
+ )
+ assert did == result
+ edited_dataset = openml.datasets.get_dataset(did)
+ assert edited_dataset.description == desc
+
+ @pytest.mark.test_server()
+ def test_data_edit_critical_field(self):
+ # Case 2
+ # only owners (or admin) can edit all critical fields of datasets
+ # for this, we need to first clone a dataset to do changes
+ did = fork_dataset(1)
+ self._wait_for_dataset_being_processed(did)
+ result = edit_dataset(did, default_target_attribute="shape", ignore_attribute="oil")
+ assert did == result
+
+ n_tries = 10
+ # we need to wait for the edit to be reflected on the server
+ for i in range(n_tries):
+ edited_dataset = openml.datasets.get_dataset(did)
+ try:
+ assert edited_dataset.default_target_attribute == "shape", edited_dataset
+ assert edited_dataset.ignore_attribute == ["oil"], edited_dataset
+ break
+ except AssertionError as e:
+ if i == n_tries - 1:
+ raise e
+ time.sleep(10)
+ # Delete the cache dir to get the newer version of the dataset
+
+ shutil.rmtree(
+ os.path.join(openml.config.get_cache_directory(), "datasets", str(did)),
+ )
+
+ @pytest.mark.test_server()
+ def test_data_edit_requires_field(self):
+ # Check server exception when no field to edit is provided
+ self.assertRaisesRegex(
+ OpenMLServerException,
+ "Please provide atleast one field among description, creator, "
+ "contributor, collection_date, language, citation, "
+ "original_data_url, default_target_attribute, row_id_attribute, "
+ "ignore_attribute or paper_url to edit.",
+ edit_dataset,
+ data_id=64, # blood-transfusion-service-center
+ )
+
+ @pytest.mark.test_server()
+ def test_data_edit_requires_valid_dataset(self):
+ # Check server exception when unknown dataset is provided
+ self.assertRaisesRegex(
+ OpenMLServerException,
+ "Unknown dataset",
+ edit_dataset,
+ data_id=999999,
+ description="xor operation dataset",
+ )
+
+ @pytest.mark.test_server()
+ def test_data_edit_cannot_edit_critical_field_if_dataset_has_task(self):
+ # Need to own a dataset to be able to edit meta-data
+ # Will be creating a forked version of an existing dataset to allow the unit test user
+ # to edit meta-data of a dataset
+ did = fork_dataset(1)
+ self._wait_for_dataset_being_processed(did)
+ TestBase._mark_entity_for_removal("data", did)
+ # Need to upload a task attached to this data to test edit failure
+ task = create_task(
+ task_type=TaskType.SUPERVISED_CLASSIFICATION,
+ dataset_id=did,
+ target_name="class",
+ estimation_procedure_id=1,
+ )
+ task = task.publish()
+ TestBase._mark_entity_for_removal("task", task.task_id)
+ # Check server exception when owner/admin edits critical fields of dataset with tasks
+ self.assertRaisesRegex(
+ OpenMLServerException,
+ "Critical features default_target_attribute, row_id_attribute and ignore_attribute "
+ "can only be edited for datasets without any tasks.",
+ edit_dataset,
+ data_id=did,
+ default_target_attribute="y",
+ )
+
+ @pytest.mark.test_server()
+ def test_edit_data_user_cannot_edit_critical_field_of_other_users_dataset(self):
+ # Check server exception when a non-owner or non-admin tries to edit critical fields
+ self.assertRaisesRegex(
+ OpenMLServerException,
+ "Critical features default_target_attribute, row_id_attribute and ignore_attribute "
+ "can be edited only by the owner. Fork the dataset if changes are required.",
+ edit_dataset,
+ data_id=128,
+ default_target_attribute="y",
+ )
+
+ @pytest.mark.test_server()
+ def test_data_fork(self):
+ did = 1
+ result = fork_dataset(did)
+ assert did != result
+ # Check server exception when unknown dataset is provided
+ self.assertRaisesRegex(
+ OpenMLServerException,
+ "Unknown dataset",
+ fork_dataset,
+ data_id=999999,
+ )
+
+
+ @pytest.mark.production_server()
+ def test_list_datasets_with_high_size_parameter(self):
+ # Testing on prod since concurrent deletion of uploded datasets make the test fail
+ self.use_production_server()
+
+ datasets_a = openml.datasets.list_datasets()
+ datasets_b = openml.datasets.list_datasets(size=np.inf)
+
+ # Reverting to test server
+ openml.config.server = self.test_server
+ assert len(datasets_a) == len(datasets_b)
+
+
+@pytest.mark.parametrize(
+ ("default_target_attribute", "row_id_attribute", "ignore_attribute"),
+ [
+ ("wrong", None, None),
+ (None, "wrong", None),
+ (None, None, "wrong"),
+ ("wrong,sunny", None, None),
+ (None, None, "wrong,sunny"),
+ (["wrong", "sunny"], None, None),
+ (None, None, ["wrong", "sunny"]),
+ ],
+)
+def test_invalid_attribute_validations(
+ default_target_attribute,
+ row_id_attribute,
+ ignore_attribute,
+):
+ data = [
+ ["a", "sunny", 85.0, 85.0, "FALSE", "no"],
+ ["b", "sunny", 80.0, 90.0, "TRUE", "no"],
+ ["c", "overcast", 83.0, 86.0, "FALSE", "yes"],
+ ["d", "rainy", 70.0, 96.0, "FALSE", "yes"],
+ ["e", "rainy", 68.0, 80.0, "FALSE", "yes"],
+ ]
+ column_names = ["rnd_str", "outlook", "temperature", "humidity", "windy", "play"]
+ df = pd.DataFrame(data, columns=column_names)
+ # enforce the type of each column
+ df["outlook"] = df["outlook"].astype("category")
+ df["windy"] = df["windy"].astype("bool")
+ df["play"] = df["play"].astype("category")
+ # meta-information
+ name = "pandas_testing_dataset"
+ description = "Synthetic dataset created from a Pandas DataFrame"
+ creator = "OpenML tester"
+ collection_date = "01-01-2018"
+ language = "English"
+ licence = "MIT"
+ citation = "None"
+ original_data_url = "http://openml.github.io/openml-python"
+ paper_url = "http://openml.github.io/openml-python"
+ with pytest.raises(ValueError, match="should be one of the data attribute"):
+ _ = openml.datasets.functions.create_dataset(
+ name=name,
+ description=description,
+ creator=creator,
+ contributor=None,
+ collection_date=collection_date,
+ language=language,
+ licence=licence,
+ default_target_attribute=default_target_attribute,
+ row_id_attribute=row_id_attribute,
+ ignore_attribute=ignore_attribute,
+ citation=citation,
+ attributes="auto",
+ data=df,
+ version_label="test",
+ original_data_url=original_data_url,
+ paper_url=paper_url,
+ )
+
+
+@pytest.mark.parametrize(
+ ("default_target_attribute", "row_id_attribute", "ignore_attribute"),
+ [
+ ("outlook", None, None),
+ (None, "outlook", None),
+ (None, None, "outlook"),
+ ("outlook,windy", None, None),
+ (None, None, "outlook,windy"),
+ (["outlook", "windy"], None, None),
+ (None, None, ["outlook", "windy"]),
+ ],
+)
+def test_valid_attribute_validations(default_target_attribute, row_id_attribute, ignore_attribute):
+ data = [
+ ["a", "sunny", 85.0, 85.0, "FALSE", "no"],
+ ["b", "sunny", 80.0, 90.0, "TRUE", "no"],
+ ["c", "overcast", 83.0, 86.0, "FALSE", "yes"],
+ ["d", "rainy", 70.0, 96.0, "FALSE", "yes"],
+ ["e", "rainy", 68.0, 80.0, "FALSE", "yes"],
+ ]
+ column_names = ["rnd_str", "outlook", "temperature", "humidity", "windy", "play"]
+ df = pd.DataFrame(data, columns=column_names)
+ # enforce the type of each column
+ df["outlook"] = df["outlook"].astype("category")
+ df["windy"] = df["windy"].astype("bool")
+ df["play"] = df["play"].astype("category")
+ # meta-information
+ name = "pandas_testing_dataset"
+ description = "Synthetic dataset created from a Pandas DataFrame"
+ creator = "OpenML tester"
+ collection_date = "01-01-2018"
+ language = "English"
+ licence = "MIT"
+ citation = "None"
+ original_data_url = "http://openml.github.io/openml-python"
+ paper_url = "http://openml.github.io/openml-python"
+ _ = openml.datasets.functions.create_dataset(
+ name=name,
+ description=description,
+ creator=creator,
+ contributor=None,
+ collection_date=collection_date,
+ language=language,
+ licence=licence,
+ default_target_attribute=default_target_attribute,
+ row_id_attribute=row_id_attribute,
+ ignore_attribute=ignore_attribute,
+ citation=citation,
+ attributes="auto",
+ data=df,
+ version_label="test",
+ original_data_url=original_data_url,
+ paper_url=paper_url,
+ )
+
+ def test_delete_dataset(self):
+ data = [
+ ["a", "sunny", 85.0, 85.0, "FALSE", "no"],
+ ["b", "sunny", 80.0, 90.0, "TRUE", "no"],
+ ["c", "overcast", 83.0, 86.0, "FALSE", "yes"],
+ ["d", "rainy", 70.0, 96.0, "FALSE", "yes"],
+ ["e", "rainy", 68.0, 80.0, "FALSE", "yes"],
+ ]
+ column_names = [
+ "rnd_str",
+ "outlook",
+ "temperature",
+ "humidity",
+ "windy",
+ "play",
+ ]
+ df = pd.DataFrame(data, columns=column_names)
+ # enforce the type of each column
+ df["outlook"] = df["outlook"].astype("category")
+ df["windy"] = df["windy"].astype("bool")
+ df["play"] = df["play"].astype("category")
+ # meta-information
+ name = f"{self._get_sentinel()}-pandas_testing_dataset"
+ description = "Synthetic dataset created from a Pandas DataFrame"
+ creator = "OpenML tester"
+ collection_date = "01-01-2018"
+ language = "English"
+ licence = "MIT"
+ citation = "None"
+ original_data_url = "http://openml.github.io/openml-python"
+ paper_url = "http://openml.github.io/openml-python"
+ dataset = openml.datasets.functions.create_dataset(
+ name=name,
+ description=description,
+ creator=creator,
+ contributor=None,
+ collection_date=collection_date,
+ language=language,
+ licence=licence,
+ default_target_attribute="play",
+ row_id_attribute=None,
+ ignore_attribute=None,
+ citation=citation,
+ attributes="auto",
+ data=df,
+ version_label="test",
+ original_data_url=original_data_url,
+ paper_url=paper_url,
+ )
+ dataset.publish()
+ _dataset_id = dataset.id
+ assert openml.datasets.delete_dataset(_dataset_id)
+
+
+@mock.patch.object(requests.Session, "delete")
+def test_delete_dataset_not_owned(mock_delete, test_files_directory, test_api_key):
+ content_file = (
+ test_files_directory / "mock_responses" / "datasets" / "data_delete_not_owned.xml"
+ )
+ mock_delete.return_value = create_request_response(
+ status_code=412,
+ content_filepath=content_file,
+ )
+
+ with pytest.raises(
+ OpenMLNotAuthorizedError,
+ match="The data can not be deleted because it was not uploaded by you.",
+ ):
+ openml.datasets.delete_dataset(40_000)
+
+ dataset_url = f"{openml.config.TEST_SERVER_URL}/api/v1/xml/data/40000"
+ assert dataset_url == mock_delete.call_args.args[0]
+ assert test_api_key == mock_delete.call_args.kwargs.get("params", {}).get("api_key")
+
+
+@mock.patch.object(requests.Session, "delete")
+def test_delete_dataset_with_run(mock_delete, test_files_directory, test_api_key):
+ content_file = (
+ test_files_directory / "mock_responses" / "datasets" / "data_delete_has_tasks.xml"
+ )
+ mock_delete.return_value = create_request_response(
+ status_code=412,
+ content_filepath=content_file,
+ )
+
+ with pytest.raises(
+ OpenMLNotAuthorizedError,
+ match="The data can not be deleted because it still has associated entities:",
+ ):
+ openml.datasets.delete_dataset(40_000)
+
+ dataset_url = f"{openml.config.TEST_SERVER_URL}/api/v1/xml/data/40000"
+ assert dataset_url == mock_delete.call_args.args[0]
+ assert test_api_key == mock_delete.call_args.kwargs.get("params", {}).get("api_key")
+
+
+@mock.patch.object(requests.Session, "delete")
+def test_delete_dataset_success(mock_delete, test_files_directory, test_api_key):
+ content_file = (
+ test_files_directory / "mock_responses" / "datasets" / "data_delete_successful.xml"
+ )
+ mock_delete.return_value = create_request_response(
+ status_code=200,
+ content_filepath=content_file,
+ )
+
+ success = openml.datasets.delete_dataset(40000)
+ assert success
+
+ dataset_url = f"{openml.config.TEST_SERVER_URL}/api/v1/xml/data/40000"
+ assert dataset_url == mock_delete.call_args.args[0]
+ assert test_api_key == mock_delete.call_args.kwargs.get("params", {}).get("api_key")
+
+
+@mock.patch.object(requests.Session, "delete")
+def test_delete_unknown_dataset(mock_delete, test_files_directory, test_api_key):
+ content_file = (
+ test_files_directory / "mock_responses" / "datasets" / "data_delete_not_exist.xml"
+ )
+ mock_delete.return_value = create_request_response(
+ status_code=412,
+ content_filepath=content_file,
+ )
+
+ with pytest.raises(
+ OpenMLServerException,
+ match="Dataset does not exist",
+ ):
+ openml.datasets.delete_dataset(9_999_999)
+
+ dataset_url = f"{openml.config.TEST_SERVER_URL}/api/v1/xml/data/9999999"
+ assert dataset_url == mock_delete.call_args.args[0]
+ assert test_api_key == mock_delete.call_args.kwargs.get("params", {}).get("api_key")
+
+
+def _assert_datasets_have_id_and_valid_status(datasets: pd.DataFrame):
+ assert pd.api.types.is_integer_dtype(datasets["did"])
+ assert {"in_preparation", "active", "deactivated"} >= set(datasets["status"])
+
+
+@pytest.fixture(scope="module")
+def all_datasets():
+ return openml.datasets.list_datasets()
+
+
+@pytest.mark.test_server()
+def test_list_datasets(all_datasets: pd.DataFrame):
+ # We can only perform a smoke test here because we test on dynamic
+ # data from the internet...
+ # 1087 as the number of datasets on openml.org
+ assert len(all_datasets) >= 100
+ _assert_datasets_have_id_and_valid_status(all_datasets)
+
+
+@pytest.mark.test_server()
+def test_list_datasets_by_tag(all_datasets: pd.DataFrame):
+ tag_datasets = openml.datasets.list_datasets(tag="study_14")
+ assert 0 < len(tag_datasets) < len(all_datasets)
+ _assert_datasets_have_id_and_valid_status(tag_datasets)
+
+
+@pytest.mark.test_server()
+def test_list_datasets_by_size():
+ datasets = openml.datasets.list_datasets(size=5)
+ assert len(datasets) == 5
+ _assert_datasets_have_id_and_valid_status(datasets)
+
+
+@pytest.mark.test_server()
+def test_list_datasets_by_number_instances(all_datasets: pd.DataFrame):
+ small_datasets = openml.datasets.list_datasets(number_instances="5..100")
+ assert 0 < len(small_datasets) <= len(all_datasets)
+ _assert_datasets_have_id_and_valid_status(small_datasets)
+
+
+@pytest.mark.test_server()
+def test_list_datasets_by_number_features(all_datasets: pd.DataFrame):
+ wide_datasets = openml.datasets.list_datasets(number_features="50..100")
+ assert 8 <= len(wide_datasets) < len(all_datasets)
+ _assert_datasets_have_id_and_valid_status(wide_datasets)
+
+
+@pytest.mark.test_server()
+def test_list_datasets_by_number_classes(all_datasets: pd.DataFrame):
+ five_class_datasets = openml.datasets.list_datasets(number_classes="5")
+ assert 3 <= len(five_class_datasets) < len(all_datasets)
+ _assert_datasets_have_id_and_valid_status(five_class_datasets)
+
+
+@pytest.mark.test_server()
+def test_list_datasets_by_number_missing_values(all_datasets: pd.DataFrame):
+ na_datasets = openml.datasets.list_datasets(number_missing_values="5..100")
+ assert 5 <= len(na_datasets) < len(all_datasets)
+ _assert_datasets_have_id_and_valid_status(na_datasets)
+
+
+@pytest.mark.test_server()
+def test_list_datasets_combined_filters(all_datasets: pd.DataFrame):
+ combined_filter_datasets = openml.datasets.list_datasets(
+ tag="study_14",
+ number_instances="100..1000",
+ number_missing_values="800..1000",
+ )
+ assert 1 <= len(combined_filter_datasets) < len(all_datasets)
+ _assert_datasets_have_id_and_valid_status(combined_filter_datasets)
+
+
+def _dataset_file_is_downloaded(did: int, file: str):
+ cache_directory = Path(openml.config.get_cache_directory()) / "datasets" / str(did)
+ return (cache_directory / file).exists()
+
+
+def _dataset_description_is_downloaded(did: int):
+ return _dataset_file_is_downloaded(did, "description.xml")
+
+
+def _dataset_qualities_is_downloaded(did: int):
+ return _dataset_file_is_downloaded(did, "qualities.xml")
+
+
+def _dataset_features_is_downloaded(did: int):
+ return _dataset_file_is_downloaded(did, "features.xml")
+
+
+def _dataset_data_file_is_downloaded(did: int):
+ cache_directory = Path(openml.config.get_cache_directory()) / "datasets" / str(did)
+ return any(f.suffix in (".pq", ".arff") for f in cache_directory.iterdir())
+
+
+def _assert_datasets_retrieved_successfully(
+ dids: Iterable[int],
+ with_qualities: bool = False,
+ with_features: bool = False,
+ with_data: bool = False,
+):
+ """Checks that all files for the given dids have been downloaded.
+
+ This includes:
+ - description
+ - qualities
+ - features
+ - absence of data arff if metadata_only, else it must be present too.
+ """
+ for did in dids:
+ assert _dataset_description_is_downloaded(did)
+
+ has_qualities = _dataset_qualities_is_downloaded(did)
+ assert has_qualities if with_qualities else not has_qualities
+
+ has_features = _dataset_features_is_downloaded(did)
+ assert has_features if with_features else not has_features
+
+ has_data = _dataset_data_file_is_downloaded(did)
+ assert has_data if with_data else not has_data
+
+
+@pytest.fixture()
+def isolate_for_test():
+ t = TestOpenMLDataset()
+ t.setUp(tmpdir_suffix=uuid.uuid4().hex)
+ yield
+ t.tearDown()
+
+
+@pytest.mark.parametrize(
+ ("with_data", "with_qualities", "with_features"),
+ itertools.product([True, False], repeat=3),
+)
+@pytest.mark.test_server()
+def test_get_dataset_lazy_behavior(
+ isolate_for_test, with_data: bool, with_qualities: bool, with_features: bool
+):
+ dataset = openml.datasets.get_dataset(
+ 1,
+ download_data=with_data,
+ download_qualities=with_qualities,
+ download_features_meta_data=with_features,
+ )
+ assert type(dataset) == OpenMLDataset
+ assert dataset.name == "anneal"
+
+ _assert_datasets_retrieved_successfully(
+ [1],
+ with_qualities=with_qualities,
+ with_features=with_features,
+ with_data=with_data,
+ )
+ assert dataset.features, "Features should be downloaded on-demand if not during get_dataset"
+ assert dataset.qualities, "Qualities should be downloaded on-demand if not during get_dataset"
+ assert dataset.get_data(), "Data should be downloaded on-demand if not during get_dataset"
+ _assert_datasets_retrieved_successfully(
+ [1], with_qualities=True, with_features=True, with_data=True
+ )
+
+
+@pytest.mark.test_server()
+def test_get_dataset_with_invalid_id() -> None:
+ INVALID_ID = 123819023109238 # Well, at some point this will probably be valid...
+ with pytest.raises(OpenMLServerNoResult, match="Unknown dataset") as e:
+ openml.datasets.get_dataset(INVALID_ID)
+ assert e.value.code == 111
+
+
+def test__get_dataset_parquet_not_cached():
+ description = {
+ "oml:parquet_url": "http://data.openml.org/dataset20/dataset_20.pq",
+ "oml:id": "20",
+ }
+ path = _get_dataset_parquet(description, cache_directory=Path(openml.config.get_cache_directory()))
+ assert isinstance(path, Path), "_get_dataset_parquet returns a path"
+ assert path.is_file(), "_get_dataset_parquet returns path to real file"
+
+
+def test_read_features_from_xml_with_whitespace() -> None:
+ from openml.datasets.dataset import _read_features
+
+ features_file = (
+ Path(__file__).parent.parent / "files" / "misc" / "features_with_whitespaces.xml"
+ )
+ dict = _read_features(features_file)
+ assert dict[1].nominal_values == [" - 50000.", " 50000+."]
+
+
+@pytest.mark.test_server()
+def test_get_dataset_parquet(requests_mock, test_files_directory):
+ # Parquet functionality is disabled on the test server
+ # There is no parquet-copy of the test server yet.
+ content_file = (
+ test_files_directory / "mock_responses" / "datasets" / "data_description_61.xml"
+ )
+ # While the mocked example is from production, unit tests by default connect to the test server.
+ requests_mock.get(f"{openml.config.TEST_SERVER_URL}/api/v1/xml/data/61", text=content_file.read_text())
+ dataset = openml.datasets.get_dataset(61, download_data=True)
+ assert dataset._parquet_url is not None
+ assert dataset.parquet_file is not None
+ assert os.path.isfile(dataset.parquet_file)
+ assert dataset.data_file is None # is alias for arff path
diff --git a/tests/entities/__init__.py b/tests/test_evaluations/__init__.py
similarity index 100%
rename from tests/entities/__init__.py
rename to tests/test_evaluations/__init__.py
diff --git a/tests/test_evaluations/test_evaluation_functions.py b/tests/test_evaluations/test_evaluation_functions.py
new file mode 100644
index 000000000..e15556d7b
--- /dev/null
+++ b/tests/test_evaluations/test_evaluation_functions.py
@@ -0,0 +1,266 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+import pytest
+
+import openml
+import openml.evaluations
+from openml.testing import TestBase
+
+
+@pytest.mark.usefixtures("long_version")
+class TestEvaluationFunctions(TestBase):
+ _multiprocess_can_split_ = True
+
+ def _check_list_evaluation_setups(self, **kwargs):
+ evals_setups = openml.evaluations.list_evaluations_setups(
+ "predictive_accuracy",
+ **kwargs,
+ sort_order="desc",
+ )
+ evals = openml.evaluations.list_evaluations(
+ "predictive_accuracy",
+ **kwargs,
+ sort_order="desc",
+ output_format="dataframe",
+ )
+
+ # Check if list is non-empty
+ assert len(evals_setups) > 0
+ # Check if length is accurate
+ assert len(evals_setups) == len(evals)
+ # Check if output from sort is sorted in the right order
+ self.assertSequenceEqual(
+ sorted(evals_setups["value"].tolist(), reverse=True),
+ evals_setups["value"].tolist(),
+ )
+
+ # Check if output and order of list_evaluations is preserved
+ self.assertSequenceEqual(evals_setups["run_id"].tolist(), evals["run_id"].tolist())
+
+ if not self.long_version:
+ evals_setups = evals_setups.head(1)
+
+ # Check if the hyper-parameter column is as accurate and flow_id
+ for _index, row in evals_setups.iterrows():
+ params = openml.runs.get_run(row["run_id"]).parameter_settings
+ list1 = [param["oml:value"] for param in params]
+ list2 = list(row["parameters"].values())
+ # check if all values are equal
+ self.assertSequenceEqual(sorted(list1), sorted(list2))
+ return evals_setups
+
+ @pytest.mark.production_server()
+ def test_evaluation_list_filter_task(self):
+ self.use_production_server()
+
+ task_id = 7312
+
+ evaluations = openml.evaluations.list_evaluations(
+ "predictive_accuracy",
+ size=110,
+ tasks=[task_id],
+ )
+
+ assert len(evaluations) > 100
+ for run_id in evaluations:
+ assert evaluations[run_id].task_id == task_id
+ # default behaviour of this method: return aggregated results (not
+ # per fold)
+ assert evaluations[run_id].value is not None
+ assert evaluations[run_id].values is None
+
+ @pytest.mark.production_server()
+ def test_evaluation_list_filter_uploader_ID_16(self):
+ self.use_production_server()
+
+ uploader_id = 16
+ evaluations = openml.evaluations.list_evaluations(
+ "predictive_accuracy",
+ size=60,
+ uploaders=[uploader_id],
+ output_format="dataframe",
+ )
+ assert evaluations["uploader"].unique() == [uploader_id]
+
+ assert len(evaluations) > 50
+
+ @pytest.mark.production_server()
+ def test_evaluation_list_filter_uploader_ID_10(self):
+ self.use_production_server()
+
+ setup_id = 10
+ evaluations = openml.evaluations.list_evaluations(
+ "predictive_accuracy",
+ size=60,
+ setups=[setup_id],
+ )
+
+ assert len(evaluations) > 50
+ for run_id in evaluations:
+ assert evaluations[run_id].setup_id == setup_id
+ # default behaviour of this method: return aggregated results (not
+ # per fold)
+ assert evaluations[run_id].value is not None
+ assert evaluations[run_id].values is None
+
+ @pytest.mark.production_server()
+ def test_evaluation_list_filter_flow(self):
+ self.use_production_server()
+
+ flow_id = 100
+
+ evaluations = openml.evaluations.list_evaluations(
+ "predictive_accuracy",
+ size=10,
+ flows=[flow_id],
+ )
+
+ assert len(evaluations) > 2
+ for run_id in evaluations:
+ assert evaluations[run_id].flow_id == flow_id
+ # default behaviour of this method: return aggregated results (not
+ # per fold)
+ assert evaluations[run_id].value is not None
+ assert evaluations[run_id].values is None
+
+ @pytest.mark.production_server()
+ def test_evaluation_list_filter_run(self):
+ self.use_production_server()
+
+ run_id = 12
+
+ evaluations = openml.evaluations.list_evaluations(
+ "predictive_accuracy",
+ size=2,
+ runs=[run_id],
+ )
+
+ assert len(evaluations) == 1
+ for run_id in evaluations:
+ assert evaluations[run_id].run_id == run_id
+ # default behaviour of this method: return aggregated results (not
+ # per fold)
+ assert evaluations[run_id].value is not None
+ assert evaluations[run_id].values is None
+
+ @pytest.mark.production_server()
+ def test_evaluation_list_limit(self):
+ self.use_production_server()
+
+ evaluations = openml.evaluations.list_evaluations(
+ "predictive_accuracy",
+ size=100,
+ offset=100,
+ )
+ assert len(evaluations) == 100
+
+ @pytest.mark.test_server()
+ def test_list_evaluations_empty(self):
+ evaluations = openml.evaluations.list_evaluations("unexisting_measure")
+ if len(evaluations) > 0:
+ raise ValueError("UnitTest Outdated, got somehow results")
+
+ assert isinstance(evaluations, dict)
+
+ @pytest.mark.production_server()
+ def test_evaluation_list_per_fold(self):
+ self.use_production_server()
+ size = 1000
+ task_ids = [6]
+ uploader_ids = [1]
+ flow_ids = [6969]
+
+ evaluations = openml.evaluations.list_evaluations(
+ "predictive_accuracy",
+ size=size,
+ offset=0,
+ tasks=task_ids,
+ flows=flow_ids,
+ uploaders=uploader_ids,
+ per_fold=True,
+ )
+
+ assert len(evaluations) == size
+ for run_id in evaluations:
+ assert evaluations[run_id].value is None
+ assert evaluations[run_id].values is not None
+ # potentially we could also test array values, but these might be
+ # added in the future
+
+ evaluations = openml.evaluations.list_evaluations(
+ "predictive_accuracy",
+ size=size,
+ offset=0,
+ tasks=task_ids,
+ flows=flow_ids,
+ uploaders=uploader_ids,
+ per_fold=False,
+ )
+ for run_id in evaluations:
+ assert evaluations[run_id].value is not None
+ assert evaluations[run_id].values is None
+
+ @pytest.mark.production_server()
+ def test_evaluation_list_sort(self):
+ self.use_production_server()
+ size = 10
+ task_id = 6
+ # Get all evaluations of the task
+ unsorted_eval = openml.evaluations.list_evaluations(
+ "predictive_accuracy",
+ size=None,
+ offset=0,
+ tasks=[task_id],
+ )
+ # Get top 10 evaluations of the same task
+ sorted_eval = openml.evaluations.list_evaluations(
+ "predictive_accuracy",
+ size=size,
+ offset=0,
+ tasks=[task_id],
+ sort_order="desc",
+ )
+ assert len(sorted_eval) == size
+ assert len(unsorted_eval) > 0
+ sorted_output = [evaluation.value for evaluation in sorted_eval.values()]
+ unsorted_output = [evaluation.value for evaluation in unsorted_eval.values()]
+
+ # Check if output from sort is sorted in the right order
+ assert sorted(sorted_output, reverse=True) == sorted_output
+
+ # Compare manual sorting against sorted output
+ test_output = sorted(unsorted_output, reverse=True)
+ assert test_output[:size] == sorted_output
+
+ @pytest.mark.test_server()
+ def test_list_evaluation_measures(self):
+ measures = openml.evaluations.list_evaluation_measures()
+ assert isinstance(measures, list) is True
+ assert all(isinstance(s, str) for s in measures) is True
+
+ @pytest.mark.production_server()
+ def test_list_evaluations_setups_filter_flow(self):
+ self.use_production_server()
+ flow_id = [405]
+ size = 100
+ evals = self._check_list_evaluation_setups(flows=flow_id, size=size)
+ # check if parameters in separate columns works
+ evals_cols = openml.evaluations.list_evaluations_setups(
+ "predictive_accuracy",
+ flows=flow_id,
+ size=size,
+ sort_order="desc",
+ parameters_in_separate_columns=True,
+ )
+ columns = list(evals_cols.columns)
+ keys = list(evals["parameters"].values[0].keys())
+ assert all(elem in columns for elem in keys)
+
+ @pytest.mark.production_server()
+ @pytest.mark.xfail(reason="failures_issue_1544", strict=False)
+ def test_list_evaluations_setups_filter_task(self):
+ self.use_production_server()
+ task_id = [6]
+ size = 121
+ self._check_list_evaluation_setups(tasks=task_id, size=size)
diff --git a/tests/test_evaluations/test_evaluations_example.py b/tests/test_evaluations/test_evaluations_example.py
new file mode 100644
index 000000000..a9ad7e8c1
--- /dev/null
+++ b/tests/test_evaluations/test_evaluations_example.py
@@ -0,0 +1,47 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+import unittest
+
+from openml.config import overwrite_config_context
+
+
+class TestEvaluationsExample(unittest.TestCase):
+ def test_example_python_paper(self):
+ # Example script which will appear in the upcoming OpenML-Python paper
+ # This test ensures that the example will keep running!
+ with overwrite_config_context(
+ {
+ "server": "https://www.openml.org/api/v1/xml",
+ "apikey": None,
+ }
+ ):
+ import matplotlib.pyplot as plt
+ import numpy as np
+ import openml
+
+ df = openml.evaluations.list_evaluations_setups(
+ "predictive_accuracy",
+ flows=[8353],
+ tasks=[6],
+ parameters_in_separate_columns=True,
+ ) # Choose an SVM flow, for example 8353, and a task.
+
+ assert len(df) > 0, (
+ "No evaluation found for flow 8353 on task 6, could "
+ "be that this task is not available on the test server."
+ )
+
+ hp_names = ["sklearn.svm.classes.SVC(16)_C", "sklearn.svm.classes.SVC(16)_gamma"]
+ df[hp_names] = df[hp_names].astype(float).apply(np.log)
+ C, gamma, score = df[hp_names[0]], df[hp_names[1]], df["value"]
+
+ cntr = plt.tricontourf(C, gamma, score, levels=12, cmap="RdBu_r")
+ plt.colorbar(cntr, label="accuracy")
+ plt.xlim((min(C), max(C)))
+ plt.ylim((min(gamma), max(gamma)))
+ plt.xlabel("C (log10)", size=16)
+ plt.ylabel("gamma (log10)", size=16)
+ plt.title("SVM performance landscape", size=20)
+
+ plt.tight_layout()
diff --git a/tests/examples/__init__.py b/tests/test_extensions/__init__.py
similarity index 100%
rename from tests/examples/__init__.py
rename to tests/test_extensions/__init__.py
diff --git a/tests/test_extensions/test_functions.py b/tests/test_extensions/test_functions.py
new file mode 100644
index 000000000..90fbaa9f1
--- /dev/null
+++ b/tests/test_extensions/test_functions.py
@@ -0,0 +1,238 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+from collections import OrderedDict
+
+import inspect
+import numpy as np
+import pytest
+from unittest.mock import patch
+import openml.testing
+from openml.extensions import Extension, get_extension_by_flow, get_extension_by_model, register_extension
+
+
+class DummyFlow:
+ external_version = "DummyFlow==0.1"
+ name = "Dummy Flow"
+ flow_id = 1
+ dependencies = None
+
+
+class DummyModel:
+ pass
+
+
+class DummyExtension1:
+ @staticmethod
+ def can_handle_flow(flow):
+ return inspect.stack()[2].filename.endswith("test_functions.py")
+
+ @staticmethod
+ def can_handle_model(model):
+ return inspect.stack()[2].filename.endswith("test_functions.py")
+
+
+class DummyExtension2:
+ @staticmethod
+ def can_handle_flow(flow):
+ return False
+
+ @staticmethod
+ def can_handle_model(model):
+ return False
+
+
+class DummyExtension(Extension):
+ @classmethod
+ def can_handle_flow(cls, flow):
+ return isinstance(flow, DummyFlow)
+
+ @classmethod
+ def can_handle_model(cls, model):
+ return isinstance(model, DummyModel)
+
+ def flow_to_model(
+ self,
+ flow,
+ initialize_with_defaults=False,
+ strict_version=True,
+ ):
+ if not isinstance(flow, DummyFlow):
+ raise ValueError("Invalid flow")
+
+ model = DummyModel()
+ model.defaults = initialize_with_defaults
+ model.strict_version = strict_version
+ return model
+
+ def model_to_flow(self, model):
+ if not isinstance(model, DummyModel):
+ raise ValueError("Invalid model")
+ return DummyFlow()
+
+ def get_version_information(self):
+ return ["dummy==1.0"]
+
+ def create_setup_string(self, model):
+ return "DummyModel()"
+
+ def is_estimator(self, model):
+ return isinstance(model, DummyModel)
+
+ def seed_model(self, model, seed):
+ model.seed = seed
+ return model
+
+ def _run_model_on_fold(
+ self,
+ model,
+ task,
+ X_train,
+ rep_no,
+ fold_no,
+ y_train=None,
+ X_test=None,
+ ):
+ preds = np.zeros(len(X_train))
+ probs = None
+ measures = OrderedDict()
+ trace = None
+ return preds, probs, measures, trace
+
+ def obtain_parameter_values(self, flow, model=None):
+ return []
+
+ def check_if_model_fitted(self, model):
+ return False
+
+ def instantiate_model_from_hpo_class(self, model, trace_iteration):
+ return DummyModel()
+
+
+
+class TestInit(openml.testing.TestBase):
+
+ def test_get_extension_by_flow(self):
+ # We replace the global list with a new empty list [] ONLY for this block
+ with patch("openml.extensions.extensions", []):
+ assert get_extension_by_flow(DummyFlow()) is None
+
+ with pytest.raises(ValueError, match="No extension registered which can handle flow:"):
+ get_extension_by_flow(DummyFlow(), raise_if_no_extension=True)
+
+ register_extension(DummyExtension1)
+ assert isinstance(get_extension_by_flow(DummyFlow()), DummyExtension1)
+
+ register_extension(DummyExtension2)
+ assert isinstance(get_extension_by_flow(DummyFlow()), DummyExtension1)
+
+ register_extension(DummyExtension1)
+ with pytest.raises(
+ ValueError, match="Multiple extensions registered which can handle flow:"
+ ):
+ get_extension_by_flow(DummyFlow())
+
+ def test_get_extension_by_model(self):
+ # Again, we start with a fresh empty list automatically
+ with patch("openml.extensions.extensions", []):
+ assert get_extension_by_model(DummyModel()) is None
+
+ with pytest.raises(ValueError, match="No extension registered which can handle model:"):
+ get_extension_by_model(DummyModel(), raise_if_no_extension=True)
+
+ register_extension(DummyExtension1)
+ assert isinstance(get_extension_by_model(DummyModel()), DummyExtension1)
+
+ register_extension(DummyExtension2)
+ assert isinstance(get_extension_by_model(DummyModel()), DummyExtension1)
+
+ register_extension(DummyExtension1)
+ with pytest.raises(
+ ValueError, match="Multiple extensions registered which can handle model:"
+ ):
+ get_extension_by_model(DummyModel())
+
+
+def test_flow_to_model_with_defaults():
+ """Test flow_to_model with initialize_with_defaults=True."""
+ ext = DummyExtension()
+ flow = DummyFlow()
+
+ model = ext.flow_to_model(flow, initialize_with_defaults=True)
+
+ assert isinstance(model, DummyModel)
+ assert model.defaults is True
+
+def test_flow_to_model_strict_version():
+ """Test flow_to_model with strict_version parameter."""
+ ext = DummyExtension()
+ flow = DummyFlow()
+
+ model_strict = ext.flow_to_model(flow, strict_version=True)
+ model_non_strict = ext.flow_to_model(flow, strict_version=False)
+
+ assert isinstance(model_strict, DummyModel)
+ assert model_strict.strict_version is True
+
+ assert isinstance(model_non_strict, DummyModel)
+ assert model_non_strict.strict_version is False
+
+def test_model_to_flow_conversion():
+ """Test converting a model back to flow representation."""
+ ext = DummyExtension()
+ model = DummyModel()
+
+ flow = ext.model_to_flow(model)
+
+ assert isinstance(flow, DummyFlow)
+
+
+def test_invalid_flow_raises_error():
+ """Test that invalid flow raises appropriate error."""
+ class InvalidFlow:
+ pass
+
+ ext = DummyExtension()
+ flow = InvalidFlow()
+
+ with pytest.raises(ValueError, match="Invalid flow"):
+ ext.flow_to_model(flow)
+
+
+@patch("openml.extensions.extensions", [])
+def test_extension_not_found_error_message():
+ """Test error message contains helpful information."""
+ class UnknownModel:
+ pass
+
+ with pytest.raises(ValueError, match="No extension registered"):
+ get_extension_by_model(UnknownModel(), raise_if_no_extension=True)
+
+
+def test_register_same_extension_twice():
+ """Test behavior when registering same extension twice."""
+ # Using a context manager here to isolate the list
+ with patch("openml.extensions.extensions", []):
+ register_extension(DummyExtension)
+ register_extension(DummyExtension)
+
+ matches = [
+ ext for ext in openml.extensions.extensions
+ if ext is DummyExtension
+ ]
+ assert len(matches) == 2
+
+
+@patch("openml.extensions.extensions", [])
+def test_extension_priority_order():
+ """Test that extensions are checked in registration order."""
+ class DummyExtensionA(DummyExtension):
+ pass
+ class DummyExtensionB(DummyExtension):
+ pass
+
+ register_extension(DummyExtensionA)
+ register_extension(DummyExtensionB)
+
+ assert openml.extensions.extensions[0] is DummyExtensionA
+ assert openml.extensions.extensions[1] is DummyExtensionB
\ No newline at end of file
diff --git a/tests/flows/__init__.py b/tests/test_flows/__init__.py
similarity index 100%
rename from tests/flows/__init__.py
rename to tests/test_flows/__init__.py
diff --git a/tests/runs/__init__.py b/tests/test_flows/dummy_learn/__init__.py
similarity index 100%
rename from tests/runs/__init__.py
rename to tests/test_flows/dummy_learn/__init__.py
diff --git a/tests/test_flows/dummy_learn/dummy_forest.py b/tests/test_flows/dummy_learn/dummy_forest.py
new file mode 100644
index 000000000..65e79e760
--- /dev/null
+++ b/tests/test_flows/dummy_learn/dummy_forest.py
@@ -0,0 +1,16 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+
+class DummyRegressor:
+ def fit(self, X, y):
+ return self
+
+ def predict(self, X):
+ return X[:, 0]
+
+ def get_params(self, deep=False):
+ return {}
+
+ def set_params(self, params):
+ return self
diff --git a/tests/test_flows/test_flow.py b/tests/test_flows/test_flow.py
new file mode 100644
index 000000000..b942c0ab9
--- /dev/null
+++ b/tests/test_flows/test_flow.py
@@ -0,0 +1,593 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+import collections
+import copy
+import hashlib
+import re
+import time
+from packaging.version import Version
+from unittest import mock
+
+import pytest
+import scipy.stats
+import sklearn
+import sklearn.datasets
+import sklearn.decomposition
+import sklearn.dummy
+import sklearn.ensemble
+import sklearn.feature_selection
+import sklearn.model_selection
+import sklearn.naive_bayes
+import sklearn.pipeline
+import sklearn.preprocessing
+import sklearn.tree
+import xmltodict
+
+from openml_sklearn import SklearnExtension
+
+import openml
+import openml.exceptions
+import openml.utils
+from openml._api_calls import _perform_api_call
+from openml.testing import SimpleImputer, TestBase
+
+
+
+class TestFlow(TestBase):
+ _multiprocess_can_split_ = True
+
+ def setUp(self):
+ super().setUp()
+ self.extension = SklearnExtension()
+
+ def tearDown(self):
+ super().tearDown()
+
+ @pytest.mark.production_server()
+ def test_get_flow(self):
+ # We need to use the production server here because 4024 is not the
+ # test server
+ self.use_production_server()
+
+ flow = openml.flows.get_flow(4024)
+ assert isinstance(flow, openml.OpenMLFlow)
+ assert flow.flow_id == 4024
+ assert len(flow.parameters) == 24
+ assert len(flow.components) == 1
+
+ subflow_1 = next(iter(flow.components.values()))
+ assert isinstance(subflow_1, openml.OpenMLFlow)
+ assert subflow_1.flow_id == 4025
+ assert len(subflow_1.parameters) == 14
+ assert subflow_1.parameters["E"] == "CC"
+ assert len(subflow_1.components) == 1
+
+ subflow_2 = next(iter(subflow_1.components.values()))
+ assert isinstance(subflow_2, openml.OpenMLFlow)
+ assert subflow_2.flow_id == 4026
+ assert len(subflow_2.parameters) == 13
+ assert subflow_2.parameters["I"] == "10"
+ assert len(subflow_2.components) == 1
+
+ subflow_3 = next(iter(subflow_2.components.values()))
+ assert isinstance(subflow_3, openml.OpenMLFlow)
+ assert subflow_3.flow_id == 1724
+ assert len(subflow_3.parameters) == 11
+ assert subflow_3.parameters["L"] == "-1"
+ assert len(subflow_3.components) == 0
+
+ @pytest.mark.production_server()
+ @pytest.mark.xfail(reason="failures_issue_1544", strict=False)
+ def test_get_structure(self):
+ # also responsible for testing: flow.get_subflow
+ # We need to use the production server here because 4024 is not the
+ # test server
+ self.use_production_server()
+
+ flow = openml.flows.get_flow(4024)
+ flow_structure_name = flow.get_structure("name")
+ flow_structure_id = flow.get_structure("flow_id")
+ # components: root (filteredclassifier), multisearch, loginboost,
+ # reptree
+ assert len(flow_structure_name) == 4
+ assert len(flow_structure_id) == 4
+
+ for sub_flow_name, structure in flow_structure_name.items():
+ if len(structure) > 0: # skip root element
+ subflow = flow.get_subflow(structure)
+ assert subflow.name == sub_flow_name
+
+ for sub_flow_id, structure in flow_structure_id.items():
+ if len(structure) > 0: # skip root element
+ subflow = flow.get_subflow(structure)
+ assert subflow.flow_id == sub_flow_id
+
+ @pytest.mark.test_server()
+ def test_tagging(self):
+ flows = openml.flows.list_flows(size=1)
+ flow_id = flows["id"].iloc[0]
+ flow = openml.flows.get_flow(flow_id)
+ # tags can be at most 64 alphanumeric (+ underscore) chars
+ unique_indicator = str(time.time()).replace(".", "")
+ tag = f"test_tag_TestFlow_{unique_indicator}"
+ flows = openml.flows.list_flows(tag=tag)
+ assert len(flows) == 0
+ flow.push_tag(tag)
+ flows = openml.flows.list_flows(tag=tag)
+ assert len(flows) == 1
+ assert flow_id in flows["id"]
+ flow.remove_tag(tag)
+ flows = openml.flows.list_flows(tag=tag)
+ assert len(flows) == 0
+
+ @pytest.mark.test_server()
+ def test_from_xml_to_xml(self):
+ # Get the raw xml thing
+ # TODO maybe get this via get_flow(), which would have to be refactored
+ # to allow getting only the xml dictionary
+ # TODO: no sklearn flows.
+ for flow_id in [
+ 3,
+ 5,
+ 7,
+ 9,
+ ]:
+ flow_xml = _perform_api_call("flow/%d" % flow_id, request_method="get")
+ flow_dict = xmltodict.parse(flow_xml)
+
+ flow = openml.OpenMLFlow._from_dict(flow_dict)
+ new_xml = flow._to_xml()
+
+ flow_xml = (
+ flow_xml.replace(" ", "")
+ .replace("\t", "")
+ .strip()
+ .replace("\n\n", "\n")
+ .replace(""", '"')
+ )
+ flow_xml = re.sub(r"^$", "", flow_xml)
+ new_xml = (
+ new_xml.replace(" ", "")
+ .replace("\t", "")
+ .strip()
+ .replace("\n\n", "\n")
+ .replace(""", '"')
+ )
+ new_xml = re.sub(r"^$", "", new_xml)
+
+ assert new_xml == flow_xml
+
+ @pytest.mark.sklearn()
+ def test_to_xml_from_xml(self):
+ scaler = sklearn.preprocessing.StandardScaler(with_mean=False)
+ estimator_name = (
+ "base_estimator" if Version(sklearn.__version__) < Version("1.4") else "estimator"
+ )
+ boosting = sklearn.ensemble.AdaBoostClassifier(
+ **{estimator_name: sklearn.tree.DecisionTreeClassifier()},
+ )
+ model = sklearn.pipeline.Pipeline(steps=(("scaler", scaler), ("boosting", boosting)))
+ flow = self.extension.model_to_flow(model)
+ flow.flow_id = -234
+ # end of setup
+
+ xml = flow._to_xml()
+ xml_dict = xmltodict.parse(xml)
+ new_flow = openml.flows.OpenMLFlow._from_dict(xml_dict)
+
+ # Would raise exception if they are not legal
+ openml.flows.functions.assert_flows_equal(new_flow, flow)
+ assert new_flow is not flow
+
+ @pytest.mark.sklearn()
+ @pytest.mark.test_server()
+ def test_publish_flow(self):
+ flow = openml.OpenMLFlow(
+ name="sklearn.dummy.DummyClassifier",
+ class_name="sklearn.dummy.DummyClassifier",
+ description="test description",
+ model=sklearn.dummy.DummyClassifier(),
+ components=collections.OrderedDict(),
+ parameters=collections.OrderedDict(),
+ parameters_meta_info=collections.OrderedDict(),
+ external_version=self.extension._format_external_version(
+ "sklearn",
+ sklearn.__version__,
+ ),
+ tags=[],
+ language="English",
+ dependencies=None,
+ )
+
+ flow, _ = self._add_sentinel_to_flow_name(flow, None)
+
+ flow.publish()
+ TestBase._mark_entity_for_removal("flow", flow.flow_id, flow.name)
+ TestBase.logger.info(f"collected from {__file__.split('/')[-1]}: {flow.flow_id}")
+ assert isinstance(flow.flow_id, int)
+
+ @pytest.mark.sklearn()
+ @mock.patch("openml.flows.functions.flow_exists")
+ def test_publish_existing_flow(self, flow_exists_mock):
+ clf = sklearn.tree.DecisionTreeClassifier(max_depth=2)
+ flow = self.extension.model_to_flow(clf)
+ flow_exists_mock.return_value = 1
+
+ with pytest.raises(openml.exceptions.PyOpenMLError, match="OpenMLFlow already exists"):
+ flow.publish(raise_error_if_exists=True)
+
+ TestBase._mark_entity_for_removal("flow", flow.flow_id, flow.name)
+ TestBase.logger.info(
+ f"collected from {__file__.split('/')[-1]}: {flow.flow_id}",
+ )
+
+ @pytest.mark.sklearn()
+ @pytest.mark.test_server()
+ def test_publish_flow_with_similar_components(self):
+ clf = sklearn.ensemble.VotingClassifier(
+ [("lr", sklearn.linear_model.LogisticRegression(solver="lbfgs"))],
+ )
+ flow = self.extension.model_to_flow(clf)
+ flow, _ = self._add_sentinel_to_flow_name(flow, None)
+ flow.publish()
+ TestBase._mark_entity_for_removal("flow", flow.flow_id, flow.name)
+ TestBase.logger.info(f"collected from {__file__.split('/')[-1]}: {flow.flow_id}")
+ # For a flow where both components are published together, the upload
+ # date should be equal
+ assert flow.upload_date == flow.components["lr"].upload_date, (
+ flow.name,
+ flow.flow_id,
+ flow.components["lr"].name,
+ flow.components["lr"].flow_id,
+ )
+
+ clf1 = sklearn.tree.DecisionTreeClassifier(max_depth=2)
+ flow1 = self.extension.model_to_flow(clf1)
+ flow1, sentinel = self._add_sentinel_to_flow_name(flow1, None)
+ flow1.publish()
+ TestBase._mark_entity_for_removal("flow", flow.flow_id, flow.name)
+ TestBase.logger.info(f"collected from {__file__.split('/')[-1]}: {flow1.flow_id}")
+
+ # In order to assign different upload times to the flows!
+ time.sleep(1)
+
+ clf2 = sklearn.ensemble.VotingClassifier(
+ [("dt", sklearn.tree.DecisionTreeClassifier(max_depth=2))],
+ )
+ flow2 = self.extension.model_to_flow(clf2)
+ flow2, _ = self._add_sentinel_to_flow_name(flow2, sentinel)
+ flow2.publish()
+ TestBase._mark_entity_for_removal("flow", flow2.flow_id, flow2.name)
+ TestBase.logger.info(f"collected from {__file__.split('/')[-1]}: {flow2.flow_id}")
+ # If one component was published before the other, the components in
+ # the flow should have different upload dates
+ assert flow2.upload_date != flow2.components["dt"].upload_date
+
+ clf3 = sklearn.ensemble.AdaBoostClassifier(sklearn.tree.DecisionTreeClassifier(max_depth=3))
+ flow3 = self.extension.model_to_flow(clf3)
+ flow3, _ = self._add_sentinel_to_flow_name(flow3, sentinel)
+ # Child flow has different parameter. Check for storing the flow
+ # correctly on the server should thus not check the child's parameters!
+ flow3.publish()
+ TestBase._mark_entity_for_removal("flow", flow3.flow_id, flow3.name)
+ TestBase.logger.info(f"collected from {__file__.split('/')[-1]}: {flow3.flow_id}")
+
+ @pytest.mark.sklearn()
+ @pytest.mark.test_server()
+ def test_semi_legal_flow(self):
+ # TODO: Test if parameters are set correctly!
+ # should not throw error as it contains two differentiable forms of
+ # Bagging i.e., Bagging(Bagging(J48)) and Bagging(J48)
+ estimator_name = (
+ "base_estimator" if Version(sklearn.__version__) < Version("1.4") else "estimator"
+ )
+ semi_legal = sklearn.ensemble.BaggingClassifier(
+ **{
+ estimator_name: sklearn.ensemble.BaggingClassifier(
+ **{
+ estimator_name: sklearn.tree.DecisionTreeClassifier(),
+ }
+ )
+ }
+ )
+ flow = self.extension.model_to_flow(semi_legal)
+ flow, _ = self._add_sentinel_to_flow_name(flow, None)
+
+ flow.publish()
+ TestBase._mark_entity_for_removal("flow", flow.flow_id, flow.name)
+ TestBase.logger.info(f"collected from {__file__.split('/')[-1]}: {flow.flow_id}")
+
+ @pytest.mark.sklearn()
+ @mock.patch("openml.flows.functions.get_flow")
+ @mock.patch("openml.flows.functions.flow_exists")
+ @mock.patch("openml._api_calls._perform_api_call")
+ def test_publish_error(self, api_call_mock, flow_exists_mock, get_flow_mock):
+ model = sklearn.ensemble.RandomForestClassifier()
+ flow = self.extension.model_to_flow(model)
+ api_call_mock.return_value = (
+ "\n" " 1 \n" " "
+ )
+ flow_exists_mock.return_value = False
+ get_flow_mock.return_value = flow
+
+ flow.publish()
+ # Not collecting flow_id for deletion since this is a test for failed upload
+
+ assert api_call_mock.call_count == 1
+ assert get_flow_mock.call_count == 1
+ assert flow_exists_mock.call_count == 1
+
+ flow_copy = copy.deepcopy(flow)
+ flow_copy.name = flow_copy.name[:-1]
+ get_flow_mock.return_value = flow_copy
+ flow_exists_mock.return_value = 1
+
+ if Version(sklearn.__version__) < Version("0.22"):
+ fixture = (
+ "The flow on the server is inconsistent with the local flow. "
+ "The server flow ID is 1. Please check manually and remove "
+ "the flow if necessary! Error is:\n"
+ "'Flow sklearn.ensemble.forest.RandomForestClassifier: "
+ "values for attribute 'name' differ: "
+ "'sklearn.ensemble.forest.RandomForestClassifier'"
+ "\nvs\n'sklearn.ensemble.forest.RandomForestClassifie'.'"
+ )
+ else:
+ # sklearn.ensemble.forest -> sklearn.ensemble._forest
+ fixture = (
+ "The flow on the server is inconsistent with the local flow. "
+ "The server flow ID is 1. Please check manually and remove "
+ "the flow if necessary! Error is:\n"
+ "'Flow sklearn.ensemble._forest.RandomForestClassifier: "
+ "values for attribute 'name' differ: "
+ "'sklearn.ensemble._forest.RandomForestClassifier'"
+ "\nvs\n'sklearn.ensemble._forest.RandomForestClassifie'.'"
+ )
+ with pytest.raises(ValueError, match=fixture):
+ flow.publish()
+
+ TestBase._mark_entity_for_removal("flow", flow.flow_id, flow.name)
+ TestBase.logger.info(
+ f"collected from {__file__.split('/')[-1]}: {flow.flow_id}",
+ )
+
+ assert get_flow_mock.call_count == 2
+
+ @pytest.mark.sklearn()
+ def test_illegal_flow(self):
+ # should throw error as it contains two imputers
+ illegal = sklearn.pipeline.Pipeline(
+ steps=[
+ ("imputer1", SimpleImputer()),
+ ("imputer2", SimpleImputer()),
+ ("classif", sklearn.tree.DecisionTreeClassifier()),
+ ],
+ )
+ self.assertRaises(ValueError, self.extension.model_to_flow, illegal)
+
+ @pytest.mark.test_server()
+ def test_nonexisting_flow_exists(self):
+ def get_sentinel():
+ # Create a unique prefix for the flow. Necessary because the flow
+ # is identified by its name and external version online. Having a
+ # unique name allows us to publish the same flow in each test run
+ md5 = hashlib.md5()
+ md5.update(str(time.time()).encode("utf-8"))
+ sentinel = md5.hexdigest()[:10]
+ return f"TEST{sentinel}"
+
+ name = get_sentinel() + get_sentinel()
+ version = get_sentinel()
+
+ flow_id = openml.flows.flow_exists(name, version)
+ assert not flow_id
+
+ @pytest.mark.sklearn()
+ @pytest.mark.test_server()
+ def test_existing_flow_exists(self):
+ # create a flow
+ nb = sklearn.naive_bayes.GaussianNB()
+
+ sparse = "sparse" if Version(sklearn.__version__) < Version("1.4") else "sparse_output"
+ ohe_params = {sparse: False, "handle_unknown": "ignore"}
+ if Version(sklearn.__version__) >= Version("0.20"):
+ ohe_params["categories"] = "auto"
+ steps = [
+ ("imputation", SimpleImputer(strategy="median")),
+ ("hotencoding", sklearn.preprocessing.OneHotEncoder(**ohe_params)),
+ (
+ "variencethreshold",
+ sklearn.feature_selection.VarianceThreshold(),
+ ),
+ ("classifier", sklearn.tree.DecisionTreeClassifier()),
+ ]
+ complicated = sklearn.pipeline.Pipeline(steps=steps)
+
+ for classifier in [nb, complicated]:
+ flow = self.extension.model_to_flow(classifier)
+ flow, _ = self._add_sentinel_to_flow_name(flow, None)
+ # publish the flow
+ flow = flow.publish()
+ TestBase._mark_entity_for_removal("flow", flow.flow_id, flow.name)
+ TestBase.logger.info(
+ f"collected from {__file__.split('/')[-1]}: {flow.flow_id}",
+ )
+ # redownload the flow
+ flow = openml.flows.get_flow(flow.flow_id)
+
+ # check if flow exists can find it
+ flow = openml.flows.get_flow(flow.flow_id)
+ downloaded_flow_id = openml.flows.flow_exists(
+ flow.name,
+ flow.external_version,
+ )
+ assert downloaded_flow_id == flow.flow_id
+
+ @pytest.mark.sklearn()
+ @pytest.mark.test_server()
+ def test_sklearn_to_upload_to_flow(self):
+ iris = sklearn.datasets.load_iris()
+ X = iris.data
+ y = iris.target
+
+ # Test a more complicated flow
+ ohe_params = {"handle_unknown": "ignore"}
+ if Version(sklearn.__version__) >= Version("0.20"):
+ ohe_params["categories"] = "auto"
+ ohe = sklearn.preprocessing.OneHotEncoder(**ohe_params)
+ scaler = sklearn.preprocessing.StandardScaler(with_mean=False)
+ pca = sklearn.decomposition.TruncatedSVD()
+ fs = sklearn.feature_selection.SelectPercentile(
+ score_func=sklearn.feature_selection.f_classif,
+ percentile=30,
+ )
+ fu = sklearn.pipeline.FeatureUnion(transformer_list=[("pca", pca), ("fs", fs)])
+ estimator_name = (
+ "base_estimator" if Version(sklearn.__version__) < Version("1.4") else "estimator"
+ )
+ boosting = sklearn.ensemble.AdaBoostClassifier(
+ **{estimator_name: sklearn.tree.DecisionTreeClassifier()},
+ )
+ model = sklearn.pipeline.Pipeline(
+ steps=[("ohe", ohe), ("scaler", scaler), ("fu", fu), ("boosting", boosting)],
+ )
+ parameter_grid = {
+ "boosting__n_estimators": [1, 5, 10, 100],
+ "boosting__learning_rate": scipy.stats.uniform(0.01, 0.99),
+ f"boosting__{estimator_name}__max_depth": scipy.stats.randint(1, 10),
+ }
+ cv = sklearn.model_selection.StratifiedKFold(n_splits=5, shuffle=True)
+ rs = sklearn.model_selection.RandomizedSearchCV(
+ estimator=model,
+ param_distributions=parameter_grid,
+ cv=cv,
+ )
+ rs.fit(X, y)
+ flow = self.extension.model_to_flow(rs)
+ # Tags may be sorted in any order (by the server). Just using one tag
+ # makes sure that the xml comparison does not fail because of that.
+ subflows = [flow]
+ while len(subflows) > 0:
+ f = subflows.pop()
+ f.tags = []
+ subflows.extend(list(f.components.values()))
+
+ flow, sentinel = self._add_sentinel_to_flow_name(flow, None)
+
+ flow.publish()
+ TestBase._mark_entity_for_removal("flow", flow.flow_id, flow.name)
+ TestBase.logger.info(f"collected from {__file__.split('/')[-1]}: {flow.flow_id}")
+ assert isinstance(flow.flow_id, int)
+
+ # Check whether we can load the flow again
+ # Remove the sentinel from the name again so that we can reinstantiate
+ # the object again
+ new_flow = openml.flows.get_flow(flow_id=flow.flow_id, reinstantiate=True)
+
+ local_xml = flow._to_xml()
+ server_xml = new_flow._to_xml()
+
+ for _i in range(10):
+ # Make sure that we replace all occurences of two newlines
+ local_xml = local_xml.replace(sentinel, "")
+ local_xml = (
+ local_xml.replace(" ", "")
+ .replace("\t", "")
+ .strip()
+ .replace("\n\n", "\n")
+ .replace(""", '"')
+ )
+ local_xml = re.sub(r"(^$)", "", local_xml)
+ server_xml = server_xml.replace(sentinel, "")
+ server_xml = (
+ server_xml.replace(" ", "")
+ .replace("\t", "")
+ .strip()
+ .replace("\n\n", "\n")
+ .replace(""", '"')
+ )
+ server_xml = re.sub(r"^$", "", server_xml)
+
+ assert server_xml == local_xml
+
+ # Would raise exception if they are not equal!
+ openml.flows.functions.assert_flows_equal(new_flow, flow)
+ assert new_flow is not flow
+
+ # OneHotEncoder was moved to _encoders module in 0.20
+ module_name_encoder = (
+ "_encoders" if Version(sklearn.__version__) >= Version("0.20") else "data"
+ )
+ if Version(sklearn.__version__) < Version("0.22"):
+ fixture_name = (
+ f"{sentinel}sklearn.model_selection._search.RandomizedSearchCV("
+ "estimator=sklearn.pipeline.Pipeline("
+ f"ohe=sklearn.preprocessing.{module_name_encoder}.OneHotEncoder,"
+ "scaler=sklearn.preprocessing.data.StandardScaler,"
+ "fu=sklearn.pipeline.FeatureUnion("
+ "pca=sklearn.decomposition.truncated_svd.TruncatedSVD,"
+ "fs="
+ "sklearn.feature_selection.univariate_selection.SelectPercentile),"
+ "boosting=sklearn.ensemble.weight_boosting.AdaBoostClassifier("
+ "base_estimator=sklearn.tree.tree.DecisionTreeClassifier)))"
+ )
+ else:
+ # sklearn.sklearn.preprocessing.data -> sklearn.sklearn.preprocessing._data
+ # sklearn.sklearn.decomposition.truncated_svd -> sklearn.decomposition._truncated_svd
+ # sklearn.feature_selection.univariate_selection ->
+ # sklearn.feature_selection._univariate_selection
+ # sklearn.ensemble.weight_boosting -> sklearn.ensemble._weight_boosting
+ # sklearn.tree.tree.DecisionTree... -> sklearn.tree._classes.DecisionTree...
+ fixture_name = (
+ f"{sentinel}sklearn.model_selection._search.RandomizedSearchCV("
+ "estimator=sklearn.pipeline.Pipeline("
+ f"ohe=sklearn.preprocessing.{module_name_encoder}.OneHotEncoder,"
+ "scaler=sklearn.preprocessing._data.StandardScaler,"
+ "fu=sklearn.pipeline.FeatureUnion("
+ "pca=sklearn.decomposition._truncated_svd.TruncatedSVD,"
+ "fs="
+ "sklearn.feature_selection._univariate_selection.SelectPercentile),"
+ "boosting=sklearn.ensemble._weight_boosting.AdaBoostClassifier("
+ f"{estimator_name}=sklearn.tree._classes.DecisionTreeClassifier)))"
+ )
+ assert new_flow.name == fixture_name
+ new_flow.model.fit(X, y)
+
+ def test_extract_tags(self):
+ flow_xml = "study_14 "
+ flow_dict = xmltodict.parse(flow_xml)
+ tags = openml.utils.extract_xml_tags("oml:tag", flow_dict)
+ assert tags == ["study_14"]
+
+ flow_xml = "OpenmlWeka \n" "weka "
+ flow_dict = xmltodict.parse(flow_xml)
+ tags = openml.utils.extract_xml_tags("oml:tag", flow_dict["oml:flow"])
+ assert tags == ["OpenmlWeka", "weka"]
+
+ @pytest.mark.production_server()
+ def test_download_non_scikit_learn_flows(self):
+ self.use_production_server()
+
+ flow = openml.flows.get_flow(6742)
+ assert isinstance(flow, openml.OpenMLFlow)
+ assert flow.flow_id == 6742
+ assert len(flow.parameters) == 19
+ assert len(flow.components) == 1
+ assert flow.model is None
+
+ subflow_1 = next(iter(flow.components.values()))
+ assert isinstance(subflow_1, openml.OpenMLFlow)
+ assert subflow_1.flow_id == 6743
+ assert len(subflow_1.parameters) == 8
+ assert subflow_1.parameters["U"] == "0"
+ assert len(subflow_1.components) == 1
+ assert subflow_1.model is None
+
+ subflow_2 = next(iter(subflow_1.components.values()))
+ assert isinstance(subflow_2, openml.OpenMLFlow)
+ assert subflow_2.flow_id == 5888
+ assert len(subflow_2.parameters) == 4
+ assert subflow_2.parameters["batch-size"] is None
+ assert len(subflow_2.components) == 0
+ assert subflow_2.model is None
diff --git a/tests/test_flows/test_flow_functions.py b/tests/test_flows/test_flow_functions.py
new file mode 100644
index 000000000..ce0d5e782
--- /dev/null
+++ b/tests/test_flows/test_flow_functions.py
@@ -0,0 +1,544 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+import copy
+import functools
+import unittest
+from collections import OrderedDict
+from multiprocessing.managers import Value
+
+from openml_sklearn import SklearnExtension
+from packaging.version import Version
+from unittest import mock
+from unittest.mock import patch
+
+import pandas as pd
+import pytest
+import requests
+import sklearn
+from sklearn import ensemble
+
+import openml
+from openml.exceptions import OpenMLNotAuthorizedError, OpenMLServerException
+from openml.testing import TestBase, create_request_response
+
+
+@pytest.mark.usefixtures("long_version")
+class TestFlowFunctions(TestBase):
+ _multiprocess_can_split_ = True
+
+ def setUp(self):
+ super().setUp()
+
+ def tearDown(self):
+ super().tearDown()
+
+ def _check_flow(self, flow):
+ assert type(flow) == dict
+ assert len(flow) == 6
+ assert isinstance(flow["id"], int)
+ assert isinstance(flow["name"], str)
+ assert isinstance(flow["full_name"], str)
+ assert isinstance(flow["version"], str)
+ # There are some runs on openml.org that can have an empty external version
+ ext_version = flow["external_version"]
+ ext_version_str_or_none = (
+ isinstance(ext_version, str) or ext_version is None or pd.isna(ext_version)
+ )
+ assert ext_version_str_or_none
+
+ @pytest.mark.production_server()
+ def test_list_flows(self):
+ self.use_production_server()
+ # We can only perform a smoke test here because we test on dynamic
+ # data from the internet...
+ flows = openml.flows.list_flows()
+ # 3000 as the number of flows on openml.org
+ assert len(flows) >= 1500
+ for flow in flows.to_dict(orient="index").values():
+ self._check_flow(flow)
+
+ @pytest.mark.production_server()
+ def test_list_flows_output_format(self):
+ self.use_production_server()
+ # We can only perform a smoke test here because we test on dynamic
+ # data from the internet...
+ flows = openml.flows.list_flows()
+ assert isinstance(flows, pd.DataFrame)
+ assert len(flows) >= 1500
+
+ @pytest.mark.production_server()
+ def test_list_flows_empty(self):
+ self.use_production_server()
+ flows = openml.flows.list_flows(tag="NoOneEverUsesThisTag123")
+ assert flows.empty
+
+ @pytest.mark.production_server()
+ def test_list_flows_by_tag(self):
+ self.use_production_server()
+ flows = openml.flows.list_flows(tag="weka")
+ assert len(flows) >= 5
+ for flow in flows.to_dict(orient="index").values():
+ self._check_flow(flow)
+
+ @pytest.mark.production_server()
+ def test_list_flows_paginate(self):
+ self.use_production_server()
+ size = 10
+ maximum = 100
+ for i in range(0, maximum, size):
+ flows = openml.flows.list_flows(offset=i, size=size)
+ assert size >= len(flows)
+ for flow in flows.to_dict(orient="index").values():
+ self._check_flow(flow)
+
+ def test_are_flows_equal(self):
+ flow = openml.flows.OpenMLFlow(
+ name="Test",
+ description="Test flow",
+ model=None,
+ components=OrderedDict(),
+ parameters=OrderedDict(),
+ parameters_meta_info=OrderedDict(),
+ external_version="1",
+ tags=["abc", "def"],
+ language="English",
+ dependencies="abc",
+ class_name="Test",
+ custom_name="Test",
+ )
+
+ # Test most important values that can be set by a user
+ openml.flows.functions.assert_flows_equal(flow, flow)
+ for attribute, new_value in [
+ ("name", "Tes"),
+ ("external_version", "2"),
+ ("language", "english"),
+ ("dependencies", "ab"),
+ ("class_name", "Tes"),
+ ("custom_name", "Tes"),
+ ]:
+ new_flow = copy.deepcopy(flow)
+ setattr(new_flow, attribute, new_value)
+ assert getattr(flow, attribute) != getattr(new_flow, attribute)
+ self.assertRaises(
+ ValueError,
+ openml.flows.functions.assert_flows_equal,
+ flow,
+ new_flow,
+ )
+
+ # Test that the API ignores several keys when comparing flows
+ openml.flows.functions.assert_flows_equal(flow, flow)
+ for attribute, new_value in [
+ ("flow_id", 1),
+ ("uploader", 1),
+ ("version", 1),
+ ("upload_date", "18.12.1988"),
+ ("binary_url", "openml.org"),
+ ("binary_format", "gzip"),
+ ("binary_md5", "12345"),
+ ("model", []),
+ ("tags", ["abc", "de"]),
+ ]:
+ new_flow = copy.deepcopy(flow)
+ setattr(new_flow, attribute, new_value)
+ assert getattr(flow, attribute) != getattr(new_flow, attribute)
+ openml.flows.functions.assert_flows_equal(flow, new_flow)
+
+ # Now test for parameters
+ flow.parameters["abc"] = 1.0
+ flow.parameters["def"] = 2.0
+ openml.flows.functions.assert_flows_equal(flow, flow)
+ new_flow = copy.deepcopy(flow)
+ new_flow.parameters["abc"] = 3.0
+ self.assertRaises(ValueError, openml.flows.functions.assert_flows_equal, flow, new_flow)
+
+ # Now test for components (subflows)
+ parent_flow = copy.deepcopy(flow)
+ subflow = copy.deepcopy(flow)
+ parent_flow.components["subflow"] = subflow
+ openml.flows.functions.assert_flows_equal(parent_flow, parent_flow)
+ self.assertRaises(
+ ValueError,
+ openml.flows.functions.assert_flows_equal,
+ parent_flow,
+ subflow,
+ )
+ new_flow = copy.deepcopy(parent_flow)
+ new_flow.components["subflow"].name = "Subflow name"
+ self.assertRaises(
+ ValueError,
+ openml.flows.functions.assert_flows_equal,
+ parent_flow,
+ new_flow,
+ )
+
+ def test_are_flows_equal_ignore_parameter_values(self):
+ paramaters = OrderedDict((("a", 5), ("b", 6)))
+ parameters_meta_info = OrderedDict((("a", None), ("b", None)))
+
+ flow = openml.flows.OpenMLFlow(
+ name="Test",
+ description="Test flow",
+ model=None,
+ components=OrderedDict(),
+ parameters=paramaters,
+ parameters_meta_info=parameters_meta_info,
+ external_version="1",
+ tags=["abc", "def"],
+ language="English",
+ dependencies="abc",
+ class_name="Test",
+ custom_name="Test",
+ )
+
+ openml.flows.functions.assert_flows_equal(flow, flow)
+ openml.flows.functions.assert_flows_equal(flow, flow, ignore_parameter_values=True)
+
+ new_flow = copy.deepcopy(flow)
+ new_flow.parameters["a"] = 7
+ with pytest.raises(ValueError) as excinfo:
+ openml.flows.functions.assert_flows_equal(flow, new_flow)
+ assert str(paramaters) in str(excinfo.value) and str(new_flow.parameters) in str(
+ excinfo.value
+ )
+
+ openml.flows.functions.assert_flows_equal(flow, new_flow, ignore_parameter_values=True)
+
+ del new_flow.parameters["a"]
+ with pytest.raises(ValueError) as excinfo:
+ openml.flows.functions.assert_flows_equal(flow, new_flow)
+ assert str(paramaters) in str(excinfo.value) and str(new_flow.parameters) in str(
+ excinfo.value
+ )
+
+ self.assertRaisesRegex(
+ ValueError,
+ r"Flow Test: parameter set of flow differs from the parameters "
+ r"stored on the server.",
+ openml.flows.functions.assert_flows_equal,
+ flow,
+ new_flow,
+ ignore_parameter_values=True,
+ )
+
+ def test_are_flows_equal_ignore_if_older(self):
+ paramaters = OrderedDict((("a", 5), ("b", 6)))
+ parameters_meta_info = OrderedDict((("a", None), ("b", None)))
+ flow_upload_date = "2017-01-31T12-01-01"
+ assert_flows_equal = openml.flows.functions.assert_flows_equal
+
+ flow = openml.flows.OpenMLFlow(
+ name="Test",
+ description="Test flow",
+ model=None,
+ components=OrderedDict(),
+ parameters=paramaters,
+ parameters_meta_info=parameters_meta_info,
+ external_version="1",
+ tags=["abc", "def"],
+ language="English",
+ dependencies="abc",
+ class_name="Test",
+ custom_name="Test",
+ upload_date=flow_upload_date,
+ )
+
+ assert_flows_equal(flow, flow, ignore_parameter_values_on_older_children=flow_upload_date)
+ assert_flows_equal(flow, flow, ignore_parameter_values_on_older_children=None)
+ new_flow = copy.deepcopy(flow)
+ new_flow.parameters["a"] = 7
+ self.assertRaises(
+ ValueError,
+ assert_flows_equal,
+ flow,
+ new_flow,
+ ignore_parameter_values_on_older_children=flow_upload_date,
+ )
+ self.assertRaises(
+ ValueError,
+ assert_flows_equal,
+ flow,
+ new_flow,
+ ignore_parameter_values_on_older_children=None,
+ )
+
+ new_flow.upload_date = "2016-01-31T12-01-01"
+ self.assertRaises(
+ ValueError,
+ assert_flows_equal,
+ flow,
+ new_flow,
+ ignore_parameter_values_on_older_children=flow_upload_date,
+ )
+ assert_flows_equal(flow, flow, ignore_parameter_values_on_older_children=None)
+
+ @pytest.mark.sklearn()
+ @unittest.skipIf(
+ Version(sklearn.__version__) < Version("0.20"),
+ reason="OrdinalEncoder introduced in 0.20. "
+ "No known models with list of lists parameters in older versions.",
+ )
+ @pytest.mark.test_server()
+ @pytest.mark.xfail(reason="failures_issue_1544", strict=False)
+ def test_sklearn_to_flow_list_of_lists(self):
+ from sklearn.preprocessing import OrdinalEncoder
+
+ ordinal_encoder = OrdinalEncoder(categories=[[0, 1], [0, 1]])
+ extension = SklearnExtension()
+
+ # Test serialization works
+ flow = extension.model_to_flow(ordinal_encoder)
+
+ # Test flow is accepted by server
+ self._add_sentinel_to_flow_name(flow)
+ flow.publish()
+ TestBase._mark_entity_for_removal("flow", flow.flow_id, flow.name)
+ TestBase.logger.info(f"collected from {__file__.split('/')[-1]}: {flow.flow_id}")
+ # Test deserialization works
+ server_flow = openml.flows.get_flow(flow.flow_id, reinstantiate=True)
+ assert server_flow.parameters["categories"] == "[[0, 1], [0, 1]]"
+ assert server_flow.model.categories == flow.model.categories
+
+ @pytest.mark.production_server()
+ def test_get_flow1(self):
+ # Regression test for issue #305
+ # Basically, this checks that a flow without an external version can be loaded
+ self.use_production_server()
+ flow = openml.flows.get_flow(1)
+ assert flow.external_version is None
+
+ @pytest.mark.sklearn()
+ @pytest.mark.test_server()
+ def test_get_flow_reinstantiate_model(self):
+ model = ensemble.RandomForestClassifier(n_estimators=33)
+ extension = openml.extensions.get_extension_by_model(model)
+ flow = extension.model_to_flow(model)
+ flow.publish(raise_error_if_exists=False)
+ TestBase._mark_entity_for_removal("flow", flow.flow_id, flow.name)
+ TestBase.logger.info(f"collected from {__file__.split('/')[-1]}: {flow.flow_id}")
+
+ downloaded_flow = openml.flows.get_flow(flow.flow_id, reinstantiate=True)
+ assert isinstance(downloaded_flow.model, sklearn.ensemble.RandomForestClassifier)
+
+ @pytest.mark.test_server()
+ def test_get_flow_reinstantiate_model_no_extension(self):
+ # Flow 10 is a WEKA flow
+ self.assertRaisesRegex(
+ ValueError,
+ ".* flow: 10 \(weka.SMO\). ",
+ openml.flows.get_flow,
+ flow_id=10,
+ reinstantiate=True,
+ )
+
+ @pytest.mark.sklearn()
+ @unittest.skipIf(
+ Version(sklearn.__version__) == Version("0.19.1"),
+ reason="Requires scikit-learn!=0.19.1, because target flow is from that version.",
+ )
+ @pytest.mark.production_server()
+ def test_get_flow_with_reinstantiate_strict_with_wrong_version_raises_exception(self):
+ self.use_production_server()
+ flow = 8175
+ expected = "Trying to deserialize a model with dependency sklearn==0.19.1 not satisfied."
+ self.assertRaisesRegex(
+ ValueError,
+ expected,
+ openml.flows.get_flow,
+ flow_id=flow,
+ reinstantiate=True,
+ strict_version=True,
+ )
+
+ @pytest.mark.sklearn()
+ @unittest.skipIf(
+ Version(sklearn.__version__) >= Version("1.0.0"),
+ reason="Requires scikit-learn < 1.0.0.",
+ # Because scikit-learn dropped min_impurity_split hyperparameter in 1.0,
+ # and the requested flow is from 1.0.0 exactly.
+ )
+ @pytest.mark.production_server()
+ def test_get_flow_reinstantiate_flow_not_strict_post_1(self):
+ self.use_production_server()
+ flow = openml.flows.get_flow(flow_id=19190, reinstantiate=True, strict_version=False)
+ assert flow.flow_id is None
+ assert "sklearn==1.0.0" not in flow.dependencies
+
+ @pytest.mark.sklearn()
+ @unittest.skipIf(
+ (Version(sklearn.__version__) < Version("0.23.2"))
+ or (Version(sklearn.__version__) >= Version("1.0")),
+ reason="Requires scikit-learn 0.23.2 or ~0.24.",
+ # Because these still have min_impurity_split, but with new scikit-learn module structure."
+ )
+ @pytest.mark.production_server()
+ def test_get_flow_reinstantiate_flow_not_strict_023_and_024(self):
+ self.use_production_server()
+ flow = openml.flows.get_flow(flow_id=18587, reinstantiate=True, strict_version=False)
+ assert flow.flow_id is None
+ assert "sklearn==0.23.1" not in flow.dependencies
+
+ @pytest.mark.sklearn()
+ @unittest.skipIf(
+ Version(sklearn.__version__) > Version("0.23"),
+ reason="Requires scikit-learn<=0.23, because the scikit-learn module structure changed.",
+ )
+ @pytest.mark.production_server()
+ def test_get_flow_reinstantiate_flow_not_strict_pre_023(self):
+ self.use_production_server()
+ flow = openml.flows.get_flow(flow_id=8175, reinstantiate=True, strict_version=False)
+ assert flow.flow_id is None
+ assert "sklearn==0.19.1" not in flow.dependencies
+
+ @pytest.mark.sklearn()
+ @pytest.mark.test_server()
+ def test_get_flow_id(self):
+ if self.long_version:
+ list_all = openml.utils._list_all
+ else:
+ list_all = functools.lru_cache()(openml.utils._list_all)
+ with patch("openml.utils._list_all", list_all):
+ clf = sklearn.tree.DecisionTreeClassifier()
+ flow = openml.extensions.get_extension_by_model(clf).model_to_flow(clf).publish()
+ TestBase._mark_entity_for_removal("flow", flow.flow_id, flow.name)
+ TestBase.logger.info(
+ f"collected from {__file__.split('/')[-1]}: {flow.flow_id}",
+ )
+
+ assert openml.flows.get_flow_id(model=clf, exact_version=True) == flow.flow_id
+ flow_ids = openml.flows.get_flow_id(model=clf, exact_version=False)
+ assert flow.flow_id in flow_ids
+ assert len(flow_ids) > 0
+
+ # Check that the output of get_flow_id is identical if only the name is given, no matter
+ # whether exact_version is set to True or False.
+ flow_ids_exact_version_True = openml.flows.get_flow_id(
+ name=flow.name,
+ exact_version=True,
+ )
+ flow_ids_exact_version_False = openml.flows.get_flow_id(
+ name=flow.name,
+ exact_version=False,
+ )
+ assert flow.flow_id in flow_ids_exact_version_True
+ assert set(flow_ids_exact_version_True).issubset(set(flow_ids_exact_version_False))
+ # instead of the assertion above, the assertion below used to be used.
+ pytest.skip(reason="Not sure why there should only be one version of this flow.")
+ assert flow_ids_exact_version_True == flow_ids_exact_version_False
+
+ @pytest.mark.test_server()
+ def test_delete_flow(self):
+ flow = openml.OpenMLFlow(
+ name="sklearn.dummy.DummyClassifier",
+ class_name="sklearn.dummy.DummyClassifier",
+ description="test description",
+ model=sklearn.dummy.DummyClassifier(),
+ components=OrderedDict(),
+ parameters=OrderedDict(),
+ parameters_meta_info=OrderedDict(),
+ external_version="1",
+ tags=[],
+ language="English",
+ dependencies=None,
+ )
+
+ flow, _ = self._add_sentinel_to_flow_name(flow, None)
+
+ flow.publish()
+ _flow_id = flow.flow_id
+ assert openml.flows.delete_flow(_flow_id)
+
+
+@mock.patch.object(requests.Session, "delete")
+def test_delete_flow_not_owned(mock_delete, test_files_directory, test_api_key):
+ content_file = test_files_directory / "mock_responses" / "flows" / "flow_delete_not_owned.xml"
+ mock_delete.return_value = create_request_response(
+ status_code=412,
+ content_filepath=content_file,
+ )
+
+ with pytest.raises(
+ OpenMLNotAuthorizedError,
+ match="The flow can not be deleted because it was not uploaded by you.",
+ ):
+ openml.flows.delete_flow(40_000)
+
+ flow_url = f"{openml.config.TEST_SERVER_URL}/api/v1/xml/flow/40000"
+ assert flow_url == mock_delete.call_args.args[0]
+ assert test_api_key == mock_delete.call_args.kwargs.get("params", {}).get("api_key")
+
+
+@mock.patch.object(requests.Session, "delete")
+def test_delete_flow_with_run(mock_delete, test_files_directory, test_api_key):
+ content_file = test_files_directory / "mock_responses" / "flows" / "flow_delete_has_runs.xml"
+ mock_delete.return_value = create_request_response(
+ status_code=412,
+ content_filepath=content_file,
+ )
+
+ with pytest.raises(
+ OpenMLNotAuthorizedError,
+ match="The flow can not be deleted because it still has associated entities:",
+ ):
+ openml.flows.delete_flow(40_000)
+
+ flow_url = f"{openml.config.TEST_SERVER_URL}/api/v1/xml/flow/40000"
+ assert flow_url == mock_delete.call_args.args[0]
+ assert test_api_key == mock_delete.call_args.kwargs.get("params", {}).get("api_key")
+
+
+@mock.patch.object(requests.Session, "delete")
+def test_delete_subflow(mock_delete, test_files_directory, test_api_key):
+ content_file = test_files_directory / "mock_responses" / "flows" / "flow_delete_is_subflow.xml"
+ mock_delete.return_value = create_request_response(
+ status_code=412,
+ content_filepath=content_file,
+ )
+
+ with pytest.raises(
+ OpenMLNotAuthorizedError,
+ match="The flow can not be deleted because it still has associated entities:",
+ ):
+ openml.flows.delete_flow(40_000)
+
+ flow_url = f"{openml.config.TEST_SERVER_URL}/api/v1/xml/flow/40000"
+ assert flow_url == mock_delete.call_args.args[0]
+ assert test_api_key == mock_delete.call_args.kwargs.get("params", {}).get("api_key")
+
+
+@mock.patch.object(requests.Session, "delete")
+def test_delete_flow_success(mock_delete, test_files_directory, test_api_key):
+ content_file = test_files_directory / "mock_responses" / "flows" / "flow_delete_successful.xml"
+ mock_delete.return_value = create_request_response(
+ status_code=200,
+ content_filepath=content_file,
+ )
+
+ success = openml.flows.delete_flow(33364)
+ assert success
+
+ flow_url = f"{openml.config.TEST_SERVER_URL}/api/v1/xml/flow/33364"
+ assert flow_url == mock_delete.call_args.args[0]
+ assert test_api_key == mock_delete.call_args.kwargs.get("params", {}).get("api_key")
+
+
+@mock.patch.object(requests.Session, "delete")
+@pytest.mark.xfail(reason="failures_issue_1544", strict=False)
+def test_delete_unknown_flow(mock_delete, test_files_directory, test_api_key):
+ content_file = test_files_directory / "mock_responses" / "flows" / "flow_delete_not_exist.xml"
+ mock_delete.return_value = create_request_response(
+ status_code=412,
+ content_filepath=content_file,
+ )
+
+ with pytest.raises(
+ OpenMLServerException,
+ match="flow does not exist",
+ ):
+ openml.flows.delete_flow(9_999_999)
+
+ flow_url = f"{openml.config.TEST_SERVER_URL}/api/v1/xml/flow/9999999"
+ assert flow_url == mock_delete.call_args.args[0]
+ assert test_api_key == mock_delete.call_args.kwargs.get("params", {}).get("api_key")
diff --git a/tests/tasks/__init__.py b/tests/test_openml/__init__.py
similarity index 100%
rename from tests/tasks/__init__.py
rename to tests/test_openml/__init__.py
diff --git a/tests/test_openml/test_api_calls.py b/tests/test_openml/test_api_calls.py
new file mode 100644
index 000000000..3f30f38ba
--- /dev/null
+++ b/tests/test_openml/test_api_calls.py
@@ -0,0 +1,128 @@
+from __future__ import annotations
+
+import unittest.mock
+from pathlib import Path
+from typing import NamedTuple, Iterable, Iterator
+from unittest import mock
+
+import minio
+import pytest
+
+import openml
+from openml.config import ConfigurationForExamples
+import openml.testing
+from openml._api_calls import _download_minio_bucket, API_TOKEN_HELP_LINK
+
+
+class TestConfig(openml.testing.TestBase):
+ @pytest.mark.test_server()
+ def test_too_long_uri(self):
+ with pytest.raises(openml.exceptions.OpenMLServerError, match="URI too long!"):
+ openml.datasets.list_datasets(data_id=list(range(10000)))
+
+ @unittest.mock.patch("time.sleep")
+ @unittest.mock.patch("requests.Session")
+ @pytest.mark.test_server()
+ def test_retry_on_database_error(self, Session_class_mock, _):
+ response_mock = unittest.mock.Mock()
+ response_mock.text = (
+ "\n"
+ "107 "
+ "Database connection error. "
+ "Usually due to high server load. "
+ "Please wait for N seconds and try again. \n"
+ " "
+ )
+ Session_class_mock.return_value.__enter__.return_value.get.return_value = response_mock
+ with pytest.raises(openml.exceptions.OpenMLServerException, match="/abc returned code 107"):
+ openml._api_calls._send_request("get", "/abc", {})
+
+ assert Session_class_mock.return_value.__enter__.return_value.get.call_count == 20
+
+
+class FakeObject(NamedTuple):
+ object_name: str
+ etag: str
+ """We use the etag of a Minio object as the name of a marker if we already downloaded it."""
+
+
+class FakeMinio:
+ def __init__(self, objects: Iterable[FakeObject] | None = None):
+ self._objects = objects or []
+
+ def list_objects(self, *args, **kwargs) -> Iterator[FakeObject]:
+ yield from self._objects
+
+ def fget_object(self, object_name: str, file_path: str, *args, **kwargs) -> None:
+ if object_name in [obj.object_name for obj in self._objects]:
+ Path(file_path).write_text("foo")
+ return
+ raise FileNotFoundError
+
+
+@mock.patch.object(minio, "Minio")
+def test_download_all_files_observes_cache(mock_minio, tmp_path: Path) -> None:
+ some_prefix, some_filename = "some/prefix", "dataset.arff"
+ some_object_path = f"{some_prefix}/{some_filename}"
+ some_url = f"https://not.real.com/bucket/{some_object_path}"
+ mock_minio.return_value = FakeMinio(
+ objects=[
+ FakeObject(object_name=some_object_path, etag=str(hash(some_object_path))),
+ ],
+ )
+
+ _download_minio_bucket(source=some_url, destination=tmp_path)
+ time_created = (tmp_path / "dataset.arff").stat().st_ctime
+
+ _download_minio_bucket(source=some_url, destination=tmp_path)
+ time_modified = (tmp_path / some_filename).stat().st_mtime
+
+ assert time_created == time_modified
+
+
+@mock.patch.object(minio, "Minio")
+def test_download_minio_failure(mock_minio, tmp_path: Path) -> None:
+ some_prefix, some_filename = "some/prefix", "dataset.arff"
+ some_object_path = f"{some_prefix}/{some_filename}"
+ some_url = f"https://not.real.com/bucket/{some_object_path}"
+ mock_minio.return_value = FakeMinio(
+ objects=[
+ FakeObject(object_name=None, etag="tmp"),
+ ],
+ )
+
+ with pytest.raises(ValueError):
+ _download_minio_bucket(source=some_url, destination=tmp_path)
+
+ mock_minio.return_value = FakeMinio(
+ objects=[
+ FakeObject(object_name="tmp", etag=None),
+ ],
+ )
+
+ with pytest.raises(ValueError):
+ _download_minio_bucket(source=some_url, destination=tmp_path)
+
+
+@pytest.mark.parametrize(
+ "endpoint, method",
+ [
+ # https://github.com/openml/OpenML/blob/develop/openml_OS/views/pages/api_new/v1/xml/pre.php
+ ("flow/exists", "post"), # 102
+ ("dataset", "post"), # 137
+ ("dataset/42", "delete"), # 350
+ # ("flow/owned", "post"), # 310 - Couldn't find what would trigger this
+ ("flow/42", "delete"), # 320
+ ("run/42", "delete"), # 400
+ ("task/42", "delete"), # 460
+ ],
+)
+@pytest.mark.test_server()
+def test_authentication_endpoints_requiring_api_key_show_relevant_help_link(
+ endpoint: str,
+ method: str,
+) -> None:
+ # We need to temporarily disable the API key to test the error message
+ with openml.config.overwrite_config_context({"apikey": None}):
+ with pytest.raises(openml.exceptions.OpenMLAuthenticationError, match=API_TOKEN_HELP_LINK):
+ openml._api_calls._perform_api_call(call=endpoint, request_method=method, data=None)
diff --git a/tests/test_openml/test_cli.py b/tests/test_openml/test_cli.py
new file mode 100644
index 000000000..eb213b561
--- /dev/null
+++ b/tests/test_openml/test_cli.py
@@ -0,0 +1,44 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+import shutil
+import subprocess
+import sys
+
+import openml
+import pytest
+
+
+def test_cli_version_prints_package_version():
+ # Invoke the CLI via module to avoid relying on console script installation
+ result = subprocess.run(
+ [sys.executable, "-m", "openml.cli", "--version"],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ text=True,
+ check=False,
+ )
+
+ # Ensure successful exit and version present in stdout only
+ assert result.returncode == 0
+ assert result.stderr == ""
+ assert openml.__version__ in result.stdout
+
+
+def test_console_script_version_prints_package_version():
+ # Try to locate the console script; skip if not installed in PATH
+ console = shutil.which("openml")
+ if console is None:
+ pytest.skip("'openml' console script not found in PATH")
+
+ result = subprocess.run(
+ [console, "--version"],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ text=True,
+ check=False,
+ )
+
+ assert result.returncode == 0
+ assert result.stderr == ""
+ assert openml.__version__ in result.stdout
diff --git a/tests/test_openml/test_config.py b/tests/test_openml/test_config.py
new file mode 100644
index 000000000..13b06223a
--- /dev/null
+++ b/tests/test_openml/test_config.py
@@ -0,0 +1,195 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+from contextlib import contextmanager
+import os
+import tempfile
+import unittest.mock
+from copy import copy
+from typing import Any, Iterator
+from pathlib import Path
+import platform
+
+import pytest
+
+import openml.config
+import openml.testing
+from openml.testing import TestBase
+
+
+@contextmanager
+def safe_environ_patcher(key: str, value: Any) -> Iterator[None]:
+ """Context manager to temporarily set an environment variable.
+
+ Safe to errors happening in the yielded to function.
+ """
+ _prev = os.environ.get(key)
+ os.environ[key] = value
+ try:
+ yield
+ except Exception as e:
+ raise e
+ finally:
+ os.environ.pop(key)
+ if _prev is not None:
+ os.environ[key] = _prev
+
+
+class TestConfig(openml.testing.TestBase):
+ @unittest.mock.patch("openml.config.openml_logger.warning")
+ @unittest.mock.patch("openml.config._create_log_handlers")
+ @unittest.skipIf(os.name == "nt", "https://github.com/openml/openml-python/issues/1033")
+ @unittest.skipIf(
+ platform.uname().release.endswith(("-Microsoft", "microsoft-standard-WSL2")),
+ "WSL does nto support chmod as we would need here, see https://github.com/microsoft/WSL/issues/81",
+ )
+ def test_non_writable_home(self, log_handler_mock, warnings_mock):
+ with tempfile.TemporaryDirectory(dir=self.workdir) as td:
+ os.chmod(td, 0o444)
+ _dd = copy(openml.config._defaults)
+ _dd["cachedir"] = Path(td) / "something-else"
+ openml.config._setup(_dd)
+
+ assert warnings_mock.call_count == 1
+ assert log_handler_mock.call_count == 1
+ assert not log_handler_mock.call_args_list[0][1]["create_file_handler"]
+ assert openml.config._root_cache_directory == Path(td) / "something-else"
+
+ @unittest.skipIf(platform.system() != "Linux", "XDG only exists for Linux systems.")
+ def test_XDG_directories_do_not_exist(self):
+ with tempfile.TemporaryDirectory(dir=self.workdir) as td:
+ # Save previous state
+ path = Path(td) / "fake_xdg_cache_home"
+ with safe_environ_patcher("XDG_CONFIG_HOME", str(path)):
+ expected_config_dir = path / "openml"
+ expected_determined_config_file_path = expected_config_dir / "config"
+
+ # Ensure that it correctly determines the path to the config file
+ determined_config_file_path = openml.config.determine_config_file_path()
+ assert determined_config_file_path == expected_determined_config_file_path
+
+ # Ensure that setup will create the config folder as the configuration
+ # will be written to that location.
+ openml.config._setup()
+ assert expected_config_dir.exists()
+
+ def test_get_config_as_dict(self):
+ """Checks if the current configuration is returned accurately as a dict."""
+ config = openml.config.get_config_as_dict()
+ _config = {}
+ _config["apikey"] = TestBase.user_key
+ _config["server"] = f"{openml.config.TEST_SERVER_URL}/api/v1/xml"
+ _config["cachedir"] = self.workdir
+ _config["avoid_duplicate_runs"] = False
+ _config["connection_n_retries"] = 20
+ _config["retry_policy"] = "robot"
+ _config["show_progress"] = False
+ assert isinstance(config, dict)
+ assert len(config) == 7
+ self.assertDictEqual(config, _config)
+
+ def test_setup_with_config(self):
+ """Checks if the OpenML configuration can be updated using _setup()."""
+ _config = {}
+ _config["apikey"] = TestBase.user_key
+ _config["server"] = "https://www.openml.org/api/v1/xml"
+ _config["cachedir"] = self.workdir
+ _config["avoid_duplicate_runs"] = True
+ _config["retry_policy"] = "human"
+ _config["connection_n_retries"] = 100
+ _config["show_progress"] = False
+ orig_config = openml.config.get_config_as_dict()
+ openml.config._setup(_config)
+ updated_config = openml.config.get_config_as_dict()
+ openml.config._setup(orig_config) # important to not affect other unit tests
+ self.assertDictEqual(_config, updated_config)
+
+
+class TestConfigurationForExamples(openml.testing.TestBase):
+ @pytest.mark.production_server()
+ def test_switch_to_example_configuration(self):
+ """Verifies the test configuration is loaded properly."""
+ # Below is the default test key which would be used anyway, but just for clarity:
+ openml.config.apikey = "any-api-key"
+ openml.config.server = self.production_server
+
+ openml.config.start_using_configuration_for_example()
+
+ assert openml.config.apikey == TestBase.user_key
+ assert openml.config.server == self.test_server
+
+ @pytest.mark.production_server()
+ def test_switch_from_example_configuration(self):
+ """Verifies the previous configuration is loaded after stopping."""
+ # Below is the default test key which would be used anyway, but just for clarity:
+ openml.config.apikey = TestBase.user_key
+ openml.config.server = self.production_server
+
+ openml.config.start_using_configuration_for_example()
+ openml.config.stop_using_configuration_for_example()
+
+ assert openml.config.apikey == TestBase.user_key
+ assert openml.config.server == self.production_server
+
+ def test_example_configuration_stop_before_start(self):
+ """Verifies an error is raised if `stop_...` is called before `start_...`."""
+ error_regex = ".*stop_use_example_configuration.*start_use_example_configuration.*first"
+ # Tests do not reset the state of this class. Thus, we ensure it is in
+ # the original state before the test.
+ openml.config.ConfigurationForExamples._start_last_called = False
+ self.assertRaisesRegex(
+ RuntimeError,
+ error_regex,
+ openml.config.stop_using_configuration_for_example,
+ )
+
+ @pytest.mark.production_server()
+ def test_example_configuration_start_twice(self):
+ """Checks that the original config can be returned to if `start..` is called twice."""
+ openml.config.apikey = TestBase.user_key
+ openml.config.server = self.production_server
+
+ openml.config.start_using_configuration_for_example()
+ openml.config.start_using_configuration_for_example()
+ openml.config.stop_using_configuration_for_example()
+
+ assert openml.config.apikey == TestBase.user_key
+ assert openml.config.server == self.production_server
+
+
+def test_configuration_file_not_overwritten_on_load():
+ """Regression test for #1337"""
+ config_file_content = "apikey = abcd"
+ with tempfile.TemporaryDirectory() as tmpdir:
+ config_file_path = Path(tmpdir) / "config"
+ with config_file_path.open("w") as config_file:
+ config_file.write(config_file_content)
+
+ read_config = openml.config._parse_config(config_file_path)
+
+ with config_file_path.open("r") as config_file:
+ new_file_content = config_file.read()
+
+ assert config_file_content == new_file_content
+ assert "abcd" == read_config["apikey"]
+
+
+def test_configuration_loads_booleans(tmp_path):
+ config_file_content = "avoid_duplicate_runs=true\nshow_progress=false"
+ tmp_file = tmp_path / "config"
+ with tmp_file.open("w") as config_file:
+ config_file.write(config_file_content)
+ read_config = openml.config._parse_config(tmp_file)
+
+ # Explicit test to avoid truthy/falsy modes of other types
+ assert read_config["avoid_duplicate_runs"] is True
+ assert read_config["show_progress"] is False
+
+
+def test_openml_cache_dir_env_var(tmp_path: Path) -> None:
+ expected_path = tmp_path / "test-cache"
+
+ with safe_environ_patcher("OPENML_CACHE_DIR", str(expected_path)):
+ openml.config._setup()
+ assert openml.config._root_cache_directory == expected_path
+ assert openml.config.get_cache_directory() == str(expected_path / "org" / "openml" / "www")
diff --git a/tests/test_openml/test_openml.py b/tests/test_openml/test_openml.py
new file mode 100644
index 000000000..998046726
--- /dev/null
+++ b/tests/test_openml/test_openml.py
@@ -0,0 +1,43 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+from unittest import mock
+
+import openml
+from openml.testing import TestBase
+
+
+class TestInit(TestBase):
+ # Splitting not helpful, these test's don't rely on the server and take less
+ # than 1 seconds
+
+ @mock.patch("openml.tasks.functions.get_task")
+ @mock.patch("openml.datasets.functions.get_dataset")
+ @mock.patch("openml.flows.functions.get_flow")
+ @mock.patch("openml.runs.functions.get_run")
+ def test_populate_cache(
+ self,
+ run_mock,
+ flow_mock,
+ dataset_mock,
+ task_mock,
+ ):
+ openml.populate_cache(task_ids=[1, 2], dataset_ids=[3, 4], flow_ids=[5, 6], run_ids=[7, 8])
+ assert run_mock.call_count == 2
+ for argument, fixture in zip(run_mock.call_args_list, [(7,), (8,)]):
+ assert argument[0] == fixture
+
+ assert flow_mock.call_count == 2
+ for argument, fixture in zip(flow_mock.call_args_list, [(5,), (6,)]):
+ assert argument[0] == fixture
+
+ assert dataset_mock.call_count == 2
+ for argument, fixture in zip(
+ dataset_mock.call_args_list,
+ [(3,), (4,)],
+ ):
+ assert argument[0] == fixture
+
+ assert task_mock.call_count == 2
+ for argument, fixture in zip(task_mock.call_args_list, [(1,), (2,)]):
+ assert argument[0] == fixture
diff --git a/tests/test_runs/__init__.py b/tests/test_runs/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/test_runs/test_run.py b/tests/test_runs/test_run.py
new file mode 100644
index 000000000..17349fca8
--- /dev/null
+++ b/tests/test_runs/test_run.py
@@ -0,0 +1,399 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+import os
+import random
+from time import time
+
+import numpy as np
+import pytest
+import xmltodict
+from openml_sklearn import SklearnExtension
+from sklearn.base import clone
+from sklearn.dummy import DummyClassifier
+from sklearn.linear_model import LinearRegression
+from sklearn.model_selection import GridSearchCV
+from sklearn.pipeline import Pipeline
+from sklearn.tree import DecisionTreeClassifier
+
+import openml
+from openml import OpenMLRun
+from openml.testing import SimpleImputer, TestBase
+
+
+class TestRun(TestBase):
+ # Splitting not helpful, these test's don't rely on the server and take
+ # less than 1 seconds
+
+ @pytest.mark.test_server()
+ def test_tagging(self):
+ runs = openml.runs.list_runs(size=1)
+ assert not runs.empty, "Test server state is incorrect"
+ run_id = runs["run_id"].iloc[0]
+ run = openml.runs.get_run(run_id)
+ # tags can be at most 64 alphanumeric (+ underscore) chars
+ unique_indicator = str(time()).replace(".", "")
+ tag = f"test_tag_TestRun_{unique_indicator}"
+ runs = openml.runs.list_runs(tag=tag)
+ assert len(runs) == 0
+ run.push_tag(tag)
+ runs = openml.runs.list_runs(tag=tag)
+ assert len(runs) == 1
+ assert run_id in runs["run_id"]
+ run.remove_tag(tag)
+ runs = openml.runs.list_runs(tag=tag)
+ assert len(runs) == 0
+
+ @staticmethod
+ def _test_prediction_data_equal(run, run_prime):
+ # Determine which attributes are numeric and which not
+ num_cols = np.array(
+ [d_type == "NUMERIC" for _, d_type in run._generate_arff_dict()["attributes"]],
+ )
+ # Get run data consistently
+ # (For run from server, .data_content does not exist)
+ run_data_content = run.predictions.values
+ run_prime_data_content = run_prime.predictions.values
+
+ # Assert numeric and string parts separately
+ numeric_part = np.array(run_data_content[:, num_cols], dtype=float)
+ numeric_part_prime = np.array(run_prime_data_content[:, num_cols], dtype=float)
+ string_part = run_data_content[:, ~num_cols]
+ string_part_prime = run_prime_data_content[:, ~num_cols]
+ np.testing.assert_array_almost_equal(numeric_part, numeric_part_prime)
+ np.testing.assert_array_equal(string_part, string_part_prime)
+
+ def _test_run_obj_equals(self, run, run_prime):
+ for dictionary in ["evaluations", "fold_evaluations", "sample_evaluations"]:
+ if getattr(run, dictionary) is not None:
+ self.assertDictEqual(getattr(run, dictionary), getattr(run_prime, dictionary))
+ else:
+ # should be none or empty
+ other = getattr(run_prime, dictionary)
+ if other is not None:
+ self.assertDictEqual(other, {})
+ assert run._to_xml() == run_prime._to_xml()
+ self._test_prediction_data_equal(run, run_prime)
+
+ # Test trace
+ run_trace_content = run.trace.trace_to_arff()["data"] if run.trace is not None else None
+
+ if run_prime.trace is not None:
+ run_prime_trace_content = run_prime.trace.trace_to_arff()["data"]
+ else:
+ run_prime_trace_content = None
+
+ if run_trace_content is not None:
+
+ def _check_array(array, type_):
+ for line in array:
+ for entry in line:
+ assert isinstance(entry, type_)
+
+ int_part = [line[:3] for line in run_trace_content]
+ _check_array(int_part, int)
+ int_part_prime = [line[:3] for line in run_prime_trace_content]
+ _check_array(int_part_prime, int)
+
+ float_part = np.array(
+ np.array(run_trace_content)[:, 3:4],
+ dtype=float,
+ )
+ float_part_prime = np.array(
+ np.array(run_prime_trace_content)[:, 3:4],
+ dtype=float,
+ )
+ bool_part = [line[4] for line in run_trace_content]
+ bool_part_prime = [line[4] for line in run_prime_trace_content]
+ for bp, bpp in zip(bool_part, bool_part_prime):
+ assert bp in ["true", "false"]
+ assert bpp in ["true", "false"]
+ string_part = np.array(run_trace_content)[:, 5:]
+ string_part_prime = np.array(run_prime_trace_content)[:, 5:]
+
+ np.testing.assert_array_almost_equal(int_part, int_part_prime)
+ np.testing.assert_array_almost_equal(float_part, float_part_prime)
+ assert bool_part == bool_part_prime
+ np.testing.assert_array_equal(string_part, string_part_prime)
+ else:
+ assert run_prime_trace_content is None
+
+ @pytest.mark.sklearn()
+ @pytest.mark.test_server()
+ def test_to_from_filesystem_vanilla(self):
+ model = Pipeline(
+ [
+ ("imputer", SimpleImputer(strategy="mean")),
+ ("classifier", DecisionTreeClassifier(max_depth=1)),
+ ],
+ )
+ task = openml.tasks.get_task(119) # diabetes; crossvalidation
+ run = openml.runs.run_model_on_task(
+ model=model,
+ task=task,
+ add_local_measures=False,
+ upload_flow=True,
+ )
+
+ cache_path = os.path.join(
+ self.workdir,
+ "runs",
+ str(random.getrandbits(128)),
+ )
+ run.to_filesystem(cache_path)
+
+ run_prime = openml.runs.OpenMLRun.from_filesystem(cache_path)
+ # The flow has been uploaded to server, so only the reference flow_id should be present
+ assert run_prime.flow_id is not None
+ assert run_prime.flow is None
+ self._test_run_obj_equals(run, run_prime)
+ run_prime.publish()
+ TestBase._mark_entity_for_removal("run", run_prime.run_id)
+ TestBase.logger.info(
+ f"collected from {__file__.split('/')[-1]}: {run_prime.run_id}",
+ )
+
+ @pytest.mark.sklearn()
+ @pytest.mark.flaky()
+ @pytest.mark.test_server()
+ def test_to_from_filesystem_search(self):
+ model = Pipeline(
+ [
+ ("imputer", SimpleImputer(strategy="mean")),
+ ("classifier", DecisionTreeClassifier(max_depth=1)),
+ ],
+ )
+ model = GridSearchCV(
+ estimator=model,
+ param_grid={
+ "classifier__max_depth": [1, 2, 3, 4, 5],
+ "imputer__strategy": ["mean", "median"],
+ },
+ )
+
+ task = openml.tasks.get_task(119) # diabetes; crossvalidation
+ run = openml.runs.run_model_on_task(
+ model=model,
+ task=task,
+ add_local_measures=False,
+ )
+
+ cache_path = os.path.join(self.workdir, "runs", str(random.getrandbits(128)))
+ run.to_filesystem(cache_path)
+
+ run_prime = openml.runs.OpenMLRun.from_filesystem(cache_path)
+ self._test_run_obj_equals(run, run_prime)
+ run_prime.publish()
+ TestBase._mark_entity_for_removal("run", run_prime.run_id)
+ TestBase.logger.info(
+ f"collected from {__file__.split('/')[-1]}: {run_prime.run_id}",
+ )
+
+ @pytest.mark.sklearn()
+ @pytest.mark.test_server()
+ def test_to_from_filesystem_no_model(self):
+ model = Pipeline(
+ [("imputer", SimpleImputer(strategy="mean")), ("classifier", DummyClassifier())],
+ )
+ task = openml.tasks.get_task(119) # diabetes; crossvalidation
+ run = openml.runs.run_model_on_task(model=model, task=task, add_local_measures=False)
+
+ cache_path = os.path.join(self.workdir, "runs", str(random.getrandbits(128)))
+ run.to_filesystem(cache_path, store_model=False)
+ # obtain run from filesystem
+ openml.runs.OpenMLRun.from_filesystem(cache_path, expect_model=False)
+ # assert default behaviour is throwing an error
+ with self.assertRaises(ValueError, msg="Could not find model.pkl"):
+ openml.runs.OpenMLRun.from_filesystem(cache_path)
+
+ @staticmethod
+ def _cat_col_selector(X):
+ return X.select_dtypes(include=["object", "category"]).columns
+
+ @staticmethod
+ def _get_models_tasks_for_tests():
+ from sklearn.compose import ColumnTransformer
+ from sklearn.preprocessing import OneHotEncoder
+
+ basic_preprocessing = [
+ (
+ "cat_handling",
+ ColumnTransformer(
+ transformers=[
+ (
+ "cat",
+ OneHotEncoder(handle_unknown="ignore"),
+ TestRun._cat_col_selector,
+ )
+ ],
+ remainder="passthrough",
+ ),
+ ),
+ ("imp", SimpleImputer()),
+ ]
+ model_clf = Pipeline(
+ [
+ *basic_preprocessing,
+ ("classifier", DummyClassifier(strategy="prior")),
+ ],
+ )
+ model_reg = Pipeline(
+ [
+ *basic_preprocessing,
+ (
+ "regressor",
+ # LR because dummy does not produce enough float-like values
+ LinearRegression(),
+ ),
+ ],
+ )
+
+ task_clf = openml.tasks.get_task(119) # diabetes; hold out validation
+ task_reg = openml.tasks.get_task(733) # quake; crossvalidation
+
+ return [(model_clf, task_clf), (model_reg, task_reg)]
+
+ @staticmethod
+ def assert_run_prediction_data(task, run, model):
+ # -- Get y_pred and y_true as it should be stored in the run
+ n_repeats, n_folds, n_samples = task.get_split_dimensions()
+ if (n_repeats > 1) or (n_samples > 1):
+ raise ValueError("Test does not support this task type's split dimensions.")
+
+ X, y = task.get_X_and_y()
+
+ # Check correctness of y_true and y_pred in run
+ for fold_id in range(n_folds):
+ # Get data for fold
+ _, test_indices = task.get_train_test_split_indices(repeat=0, fold=fold_id, sample=0)
+ train_mask = np.full(len(X), True)
+ train_mask[test_indices] = False
+
+ # Get train / test
+ X_train = X[train_mask]
+ y_train = y[train_mask]
+ X_test = X[~train_mask]
+ y_test = y[~train_mask]
+
+ # Get y_pred
+ y_pred = model.fit(X_train, y_train).predict(X_test)
+
+ # Get stored data for fold
+ saved_fold_data = run.predictions[run.predictions["fold"] == fold_id].sort_values(
+ by="row_id",
+ )
+ saved_y_pred = saved_fold_data["prediction"].values
+ gt_key = "truth" if "truth" in list(saved_fold_data) else "correct"
+ saved_y_test = saved_fold_data[gt_key].values
+
+ assert_method = np.testing.assert_array_almost_equal
+ if task.task_type == "Supervised Classification":
+ assert_method = np.testing.assert_array_equal
+ y_test = y_test.values
+
+ # Assert correctness
+ assert_method(y_pred, saved_y_pred)
+ assert_method(y_test, saved_y_test)
+
+ @pytest.mark.sklearn()
+ @pytest.mark.test_server()
+ def test_publish_with_local_loaded_flow(self):
+ """
+ Publish a run tied to a local flow after it has first been saved to
+ and loaded from disk.
+ """
+ extension = SklearnExtension()
+
+ for model, task in self._get_models_tasks_for_tests():
+ # Make sure the flow does not exist on the server yet.
+ flow = extension.model_to_flow(model)
+ self._add_sentinel_to_flow_name(flow)
+ assert not openml.flows.flow_exists(flow.name, flow.external_version)
+
+ run = openml.runs.run_flow_on_task(
+ flow=flow,
+ task=task,
+ add_local_measures=False,
+ upload_flow=False,
+ )
+
+ # Make sure that the flow has not been uploaded as requested.
+ assert not openml.flows.flow_exists(flow.name, flow.external_version)
+
+ # Make sure that the prediction data stored in the run is correct.
+ self.assert_run_prediction_data(task, run, clone(model))
+
+ cache_path = os.path.join(self.workdir, "runs", str(random.getrandbits(128)))
+ run.to_filesystem(cache_path)
+ # obtain run from filesystem
+ loaded_run = openml.runs.OpenMLRun.from_filesystem(cache_path)
+ loaded_run.publish()
+
+ # Clean up
+ TestBase._mark_entity_for_removal("run", loaded_run.run_id)
+ TestBase.logger.info(
+ f"collected from {__file__.split('/')[-1]}: {loaded_run.run_id}",
+ )
+
+ # make sure the flow is published as part of publishing the run.
+ assert openml.flows.flow_exists(flow.name, flow.external_version)
+ openml.runs.get_run(loaded_run.run_id)
+
+ @pytest.mark.sklearn()
+ @pytest.mark.test_server()
+ def test_offline_and_online_run_identical(self):
+ extension = SklearnExtension()
+
+ for model, task in self._get_models_tasks_for_tests():
+ # Make sure the flow does not exist on the server yet.
+ flow = extension.model_to_flow(model)
+ self._add_sentinel_to_flow_name(flow)
+ assert not openml.flows.flow_exists(flow.name, flow.external_version)
+
+ run = openml.runs.run_flow_on_task(
+ flow=flow,
+ task=task,
+ add_local_measures=False,
+ upload_flow=False,
+ )
+
+ # Make sure that the flow has not been uploaded as requested.
+ assert not openml.flows.flow_exists(flow.name, flow.external_version)
+
+ # Load from filesystem
+ cache_path = os.path.join(self.workdir, "runs", str(random.getrandbits(128)))
+ run.to_filesystem(cache_path)
+ loaded_run = openml.runs.OpenMLRun.from_filesystem(cache_path)
+
+ # Assert identical for offline - offline
+ self._test_run_obj_equals(run, loaded_run)
+
+ # Publish and test for offline - online
+ run.publish()
+ assert openml.flows.flow_exists(flow.name, flow.external_version)
+
+ try:
+ online_run = openml.runs.get_run(run.run_id, ignore_cache=True)
+ self._test_prediction_data_equal(run, online_run)
+ finally:
+ # Clean up
+ TestBase._mark_entity_for_removal("run", run.run_id)
+ TestBase.logger.info(
+ f"collected from {__file__.split('/')[-1]}: {loaded_run.run_id}",
+ )
+
+ def test_run_setup_string_included_in_xml(self):
+ SETUP_STRING = "setup-string"
+ run = OpenMLRun(
+ task_id=0,
+ flow_id=None, # if not none, flow parameters are required.
+ dataset_id=0,
+ setup_string=SETUP_STRING,
+ )
+ xml = run._to_xml()
+ run_dict = xmltodict.parse(xml)["oml:run"]
+ assert "oml:setup_string" in run_dict
+ assert run_dict["oml:setup_string"] == SETUP_STRING
+
+ recreated_run = openml.runs.functions._create_run_from_xml(xml, from_server=False)
+ assert recreated_run.setup_string == SETUP_STRING
diff --git a/tests/test_runs/test_run_functions.py b/tests/test_runs/test_run_functions.py
new file mode 100644
index 000000000..9bc8d74fa
--- /dev/null
+++ b/tests/test_runs/test_run_functions.py
@@ -0,0 +1,2035 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+import ast
+import os
+import random
+import time
+import unittest
+import warnings
+
+from openml_sklearn import SklearnExtension, cat, cont
+from packaging.version import Version
+from unittest import mock
+
+import arff
+import joblib
+import numpy as np
+import pandas as pd
+import pytest
+import requests
+import sklearn
+from joblib import parallel_backend
+from sklearn.dummy import DummyClassifier
+from sklearn.ensemble import BaggingClassifier, RandomForestClassifier
+from sklearn.feature_selection import VarianceThreshold
+from sklearn.linear_model import LinearRegression, LogisticRegression, SGDClassifier
+from sklearn.model_selection import GridSearchCV, RandomizedSearchCV, StratifiedKFold
+from sklearn.model_selection._search import BaseSearchCV
+from sklearn.naive_bayes import GaussianNB
+from sklearn.pipeline import Pipeline, make_pipeline
+from sklearn.preprocessing import OneHotEncoder, StandardScaler
+from sklearn.svm import SVC
+from sklearn.tree import DecisionTreeClassifier
+from sklearn.compose import ColumnTransformer
+
+import openml
+import openml._api_calls
+import openml.exceptions
+from openml.exceptions import (
+ OpenMLNotAuthorizedError,
+ OpenMLServerException,
+)
+#from openml.extensions.sklearn import cat, cont
+from openml.runs.functions import (
+ _run_task_get_arffcontent,
+ delete_run,
+ format_prediction,
+ run_exists,
+)
+from openml.runs.trace import OpenMLRunTrace
+from openml.tasks import TaskType
+from openml.testing import (
+ CustomImputer,
+ SimpleImputer,
+ TestBase,
+ check_task_existence,
+ create_request_response,
+)
+
+
+class TestRun(TestBase):
+ _multiprocess_can_split_ = True
+ TEST_SERVER_TASK_MISSING_VALS = {
+ "task_id": 96,
+ "n_missing_vals": 67,
+ "n_test_obs": 227,
+ "nominal_indices": [0, 3, 4, 5, 6, 8, 9, 11, 12],
+ "numeric_indices": [1, 2, 7, 10, 13, 14],
+ "task_meta_data": {
+ "task_type": TaskType.SUPERVISED_CLASSIFICATION,
+ "dataset_id": 16, # credit-a
+ "estimation_procedure_id": 6,
+ "target_name": "class",
+ },
+ }
+ TEST_SERVER_TASK_SIMPLE = {
+ "task_id": 119,
+ "n_missing_vals": 0,
+ "n_test_obs": 253,
+ "nominal_indices": [],
+ "numeric_indices": [*range(8)],
+ "task_meta_data": {
+ "task_type": TaskType.SUPERVISED_CLASSIFICATION,
+ "dataset_id": 20, # diabetes
+ "estimation_procedure_id": 5,
+ "target_name": "class",
+ },
+ }
+ TEST_SERVER_TASK_REGRESSION = {
+ "task_id": 1605,
+ "n_missing_vals": 0,
+ "n_test_obs": 2178,
+ "nominal_indices": [],
+ "numeric_indices": [*range(8)],
+ "task_meta_data": {
+ "task_type": TaskType.SUPERVISED_REGRESSION,
+ "dataset_id": 123, # quake
+ "estimation_procedure_id": 7,
+ "target_name": "richter",
+ },
+ }
+
+ # Suppress warnings to facilitate testing
+ hide_warnings = True
+ if hide_warnings:
+ warnings.filterwarnings("ignore", category=DeprecationWarning)
+ warnings.filterwarnings("ignore", category=FutureWarning)
+ warnings.filterwarnings("ignore", category=UserWarning)
+
+ def setUp(self):
+ super().setUp()
+ self.extension = SklearnExtension()
+
+ def _wait_for_processed_run(self, run_id, max_waiting_time_seconds):
+ # it can take a while for a run to be processed on the OpenML (test)
+ # server however, sometimes it is good to wait (a bit) for this, to
+ # properly test a function. In this case, we wait for max_waiting_time_
+ # seconds on this to happen, probing the server every 10 seconds to
+ # speed up the process
+
+ # time.time() works in seconds
+ start_time = time.time()
+ while time.time() - start_time < max_waiting_time_seconds:
+ try:
+ openml.runs.get_run_trace(run_id)
+ except openml.exceptions.OpenMLServerException:
+ time.sleep(10)
+ continue
+
+ run = openml.runs.get_run(run_id, ignore_cache=True)
+ if run.evaluations is None:
+ time.sleep(10)
+ continue
+
+ assert len(run.evaluations) > 0, (
+ "Expect not-None evaluations to always contain elements."
+ )
+ return
+
+ raise RuntimeError(
+ f"Could not find any evaluations! Please check whether run {run_id} was "
+ "evaluated correctly on the server",
+ )
+
+ def _assert_predictions_equal(self, predictions, predictions_prime):
+ assert np.array(predictions_prime["data"]).shape == np.array(predictions["data"]).shape
+
+ # The original search model does not submit confidence
+ # bounds, so we can not compare the arff line
+ compare_slice = [0, 1, 2, -1, -2]
+ for idx in range(len(predictions["data"])):
+ # depends on the assumption "predictions are in same order"
+ # that does not necessarily hold.
+ # But with the current code base, it holds.
+ for col_idx in compare_slice:
+ val_1 = predictions["data"][idx][col_idx]
+ val_2 = predictions_prime["data"][idx][col_idx]
+ if isinstance(val_1, float) or isinstance(val_2, float):
+ self.assertAlmostEqual(
+ float(val_1),
+ float(val_2),
+ places=6,
+ )
+ else:
+ assert val_1 == val_2
+
+ def _rerun_model_and_compare_predictions(self, run_id, model_prime, seed, create_task_obj):
+ run = openml.runs.get_run(run_id)
+
+ # TODO: assert holdout task
+
+ # downloads the predictions of the old task
+ file_id = run.output_files["predictions"]
+ predictions_url = openml._api_calls._file_id_to_url(file_id)
+ response = openml._api_calls._download_text_file(predictions_url)
+ predictions = arff.loads(response)
+
+ # if create_task_obj=False, task argument in run_model_on_task is specified task_id
+ if create_task_obj:
+ task = openml.tasks.get_task(run.task_id)
+ run_prime = openml.runs.run_model_on_task(
+ model=model_prime,
+ task=task,
+ seed=seed,
+ )
+ else:
+ run_prime = openml.runs.run_model_on_task(
+ model=model_prime,
+ task=run.task_id,
+ seed=seed,
+ )
+
+ predictions_prime = run_prime._generate_arff_dict()
+
+ self._assert_predictions_equal(predictions, predictions_prime)
+ pd.testing.assert_frame_equal(
+ run.predictions,
+ run_prime.predictions,
+ check_dtype=False, # Loaded ARFF reads NUMERIC as float, even if integer.
+ )
+
+ def _perform_run(
+ self,
+ task_id,
+ num_instances,
+ n_missing_vals,
+ clf,
+ flow_expected_rsv=None,
+ seed=1,
+ check_setup=True,
+ sentinel=None,
+ ):
+ """
+ Runs a classifier on a task, and performs some basic checks.
+ Also uploads the run.
+
+ Parameters
+ ----------
+ task_id : int
+
+ num_instances: int
+ The expected length of the prediction file (number of test
+ instances in original dataset)
+
+ n_missing_values: int
+
+ clf: sklearn.base.BaseEstimator
+ The classifier to run
+
+ flow_expected_rsv: str
+ The expected random state value for the flow (check by hand,
+ depends on seed parameter)
+
+ seed: int
+ The seed with which the RSV for runs will be initialized
+
+ check_setup: bool
+ If set to True, the flow will be downloaded again and
+ reinstantiated, for consistency with original flow.
+
+ sentinel: optional, str
+ in case the sentinel should be user specified
+
+ Returns
+ -------
+ run: OpenMLRun
+ The performed run (with run id)
+ """
+ classes_without_random_state = [
+ "sklearn.model_selection._search.GridSearchCV",
+ "sklearn.pipeline.Pipeline",
+ ]
+ if Version(sklearn.__version__) < Version("0.22"):
+ classes_without_random_state.append("sklearn.linear_model.base.LinearRegression")
+ else:
+ classes_without_random_state.append("sklearn.linear_model._base.LinearRegression")
+
+ def _remove_random_state(flow):
+ if "random_state" in flow.parameters:
+ del flow.parameters["random_state"]
+ for component in flow.components.values():
+ _remove_random_state(component)
+
+ flow = self.extension.model_to_flow(clf)
+ flow, _ = self._add_sentinel_to_flow_name(flow, sentinel)
+ if not openml.flows.flow_exists(flow.name, flow.external_version):
+ flow.publish()
+ TestBase._mark_entity_for_removal("flow", flow.flow_id, flow.name)
+ TestBase.logger.info(f"collected from test_run_functions: {flow.flow_id}")
+
+ task = openml.tasks.get_task(task_id)
+
+ X, y = task.get_X_and_y()
+ assert X.isna().sum().sum() == n_missing_vals
+ run = openml.runs.run_flow_on_task(
+ flow=flow,
+ task=task,
+ seed=seed,
+ )
+ run_ = run.publish()
+ TestBase._mark_entity_for_removal("run", run.run_id)
+ TestBase.logger.info(f"collected from test_run_functions: {run.run_id}")
+ assert run_ == run
+ assert isinstance(run.dataset_id, int)
+
+ # This is only a smoke check right now
+ # TODO add a few asserts here
+ run._to_xml()
+ if run.trace is not None:
+ # This is only a smoke check right now
+ # TODO add a few asserts here
+ run.trace.trace_to_arff()
+
+ # check arff output
+ assert len(run.data_content) == num_instances
+
+ if check_setup:
+ # test the initialize setup function
+ run_id = run_.run_id
+ run_server = openml.runs.get_run(run_id)
+ clf_server = openml.setups.initialize_model(
+ setup_id=run_server.setup_id,
+ )
+ flow_local = self.extension.model_to_flow(clf)
+ flow_server = self.extension.model_to_flow(clf_server)
+
+ if flow.class_name not in classes_without_random_state:
+ error_msg = "Flow class %s (id=%d) does not have a random state parameter" % (
+ flow.class_name,
+ flow.flow_id,
+ )
+ assert "random_state" in flow.parameters, error_msg
+ # If the flow is initialized from a model without a random
+ # state, the flow is on the server without any random state
+ assert flow.parameters["random_state"] == "null"
+ # As soon as a flow is run, a random state is set in the model.
+ # If a flow is re-instantiated
+ assert flow_local.parameters["random_state"] == flow_expected_rsv
+ assert flow_server.parameters["random_state"] == flow_expected_rsv
+ _remove_random_state(flow_local)
+ _remove_random_state(flow_server)
+ openml.flows.assert_flows_equal(flow_local, flow_server)
+
+ # and test the initialize setup from run function
+ clf_server2 = openml.runs.initialize_model_from_run(
+ run_id=run_server.run_id,
+ )
+ flow_server2 = self.extension.model_to_flow(clf_server2)
+ if flow.class_name not in classes_without_random_state:
+ assert flow_server2.parameters["random_state"] == flow_expected_rsv
+
+ _remove_random_state(flow_server2)
+ openml.flows.assert_flows_equal(flow_local, flow_server2)
+
+ # self.assertEqual(clf.get_params(), clf_prime.get_params())
+ # self.assertEqual(clf, clf_prime)
+
+ downloaded = openml.runs.get_run(run_.run_id)
+ assert "openml-python" in downloaded.tags
+
+ # TODO make sure that these attributes are instantiated when
+ # downloading a run? Or make sure that the trace object is created when
+ # running a flow on a task (and not only the arff object is created,
+ # so that the two objects can actually be compared):
+ # downloaded_run_trace = downloaded._generate_trace_arff_dict()
+ # self.assertEqual(run_trace, downloaded_run_trace)
+ return run
+
+ def _check_sample_evaluations(
+ self,
+ sample_evaluations,
+ num_repeats,
+ num_folds,
+ num_samples,
+ max_time_allowed=60000,
+ ):
+ """
+ Checks whether the right timing measures are attached to the run
+ (before upload). Test is only performed for versions >= Python3.3
+
+ In case of check_n_jobs(clf) == false, please do not perform this
+ check (check this condition outside of this function. )
+ default max_time_allowed (per fold, in milli seconds) = 1 minute,
+ quite pessimistic
+ """
+ # a dict mapping from openml measure to a tuple with the minimum and
+ # maximum allowed value
+ check_measures = {
+ # should take at least one millisecond (?)
+ "usercpu_time_millis_testing": (0, max_time_allowed),
+ "usercpu_time_millis_training": (0, max_time_allowed),
+ "usercpu_time_millis": (0, max_time_allowed),
+ "wall_clock_time_millis_training": (0, max_time_allowed),
+ "wall_clock_time_millis_testing": (0, max_time_allowed),
+ "wall_clock_time_millis": (0, max_time_allowed),
+ "predictive_accuracy": (0, 1),
+ }
+
+ assert isinstance(sample_evaluations, dict)
+ assert set(sample_evaluations.keys()) == set(check_measures.keys())
+
+ for measure in check_measures:
+ if measure in sample_evaluations:
+ num_rep_entrees = len(sample_evaluations[measure])
+ assert num_rep_entrees == num_repeats
+ for rep in range(num_rep_entrees):
+ num_fold_entrees = len(sample_evaluations[measure][rep])
+ assert num_fold_entrees == num_folds
+ for fold in range(num_fold_entrees):
+ num_sample_entrees = len(sample_evaluations[measure][rep][fold])
+ assert num_sample_entrees == num_samples
+ for sample in range(num_sample_entrees):
+ evaluation = sample_evaluations[measure][rep][fold][sample]
+ assert isinstance(evaluation, float)
+ if not (os.environ.get("CI_WINDOWS") or os.name == "nt"):
+ # Windows seems to get an eval-time of 0 sometimes.
+ assert evaluation > 0
+ assert evaluation < max_time_allowed
+
+ @pytest.mark.sklearn()
+ @pytest.mark.test_server()
+ def test_run_regression_on_classif_task(self):
+ task_id = 259 # collins; crossvalidation; has numeric targets
+
+ clf = LinearRegression()
+ task = openml.tasks.get_task(task_id)
+ # internally dataframe is loaded and targets are categorical
+ # which LinearRegression() cannot handle
+ with pytest.raises(
+ AttributeError, match="'LinearRegression' object has no attribute 'classes_'"
+ ):
+ openml.runs.run_model_on_task(
+ model=clf,
+ task=task,
+ )
+
+ @pytest.mark.sklearn()
+ @pytest.mark.test_server()
+ def test_check_erronous_sklearn_flow_fails(self):
+ task_id = 115 # diabetes; crossvalidation
+ task = openml.tasks.get_task(task_id)
+
+ # Invalid parameter values
+ clf = LogisticRegression(C="abc", solver="lbfgs")
+ # The exact error message depends on scikit-learn version.
+ # Because the sklearn-extension module is to be separated,
+ # I will simply relax specifics of the raised Error.
+ # old: r"Penalty term must be positive; got \(C=u?'abc'\)"
+ # new: sklearn.utils._param_validation.InvalidParameterError:
+ # The 'C' parameter of LogisticRegression must be a float in the range (0, inf]. Got 'abc' instead. # noqa: E501
+ try:
+ from sklearn.utils._param_validation import InvalidParameterError
+
+ exceptions = (ValueError, InvalidParameterError)
+ except ImportError:
+ exceptions = (ValueError,)
+ with pytest.raises(exceptions):
+ openml.runs.run_model_on_task(
+ task=task,
+ model=clf,
+ )
+
+ ###########################################################################
+ # These unit tests are meant to test the following functions, using a
+ # variety of flows:
+ # - openml.runs.run_task()
+ # - openml.runs.OpenMLRun.publish()
+ # - openml.runs.initialize_model()
+ # - [implicitly] openml.setups.initialize_model()
+ # - openml.runs.initialize_model_from_trace()
+ # They're split among several actual functions to allow for parallel
+ # execution of the unit tests without the need to add an additional module
+ # like unittest2
+
+ def _run_and_upload(
+ self,
+ clf,
+ task_id,
+ n_missing_vals,
+ n_test_obs,
+ flow_expected_rsv,
+ num_folds=1,
+ num_iterations=5,
+ seed=1,
+ metric=sklearn.metrics.accuracy_score,
+ metric_name="predictive_accuracy",
+ task_type=TaskType.SUPERVISED_CLASSIFICATION,
+ sentinel=None,
+ ):
+ def determine_grid_size(param_grid):
+ if isinstance(param_grid, dict):
+ grid_iterations = 1
+ for param in param_grid:
+ grid_iterations *= len(param_grid[param])
+ return grid_iterations
+ elif isinstance(param_grid, list):
+ grid_iterations = 0
+ for sub_grid in param_grid:
+ grid_iterations += determine_grid_size(sub_grid)
+ return grid_iterations
+ else:
+ raise TypeError("Param Grid should be of type list (GridSearch only) or dict")
+
+ run = self._perform_run(
+ task_id,
+ n_test_obs,
+ n_missing_vals,
+ clf,
+ flow_expected_rsv=flow_expected_rsv,
+ seed=seed,
+ sentinel=sentinel,
+ )
+
+ # obtain scores using get_metric_score:
+ scores = run.get_metric_fn(metric)
+ # compare with the scores in user defined measures
+ scores_provided = []
+ for rep in run.fold_evaluations[metric_name]:
+ for fold in run.fold_evaluations[metric_name][rep]:
+ scores_provided.append(run.fold_evaluations[metric_name][rep][fold])
+ assert sum(scores_provided) == sum(scores)
+
+ if isinstance(clf, BaseSearchCV):
+ trace_content = run.trace.trace_to_arff()["data"]
+ if isinstance(clf, GridSearchCV):
+ grid_iterations = determine_grid_size(clf.param_grid)
+ assert len(trace_content) == grid_iterations * num_folds
+ else:
+ assert len(trace_content) == num_iterations * num_folds
+
+ # downloads the best model based on the optimization trace
+ # suboptimal (slow), and not guaranteed to work if evaluation
+ # engine is behind.
+ # TODO: mock this? We have the arff already on the server
+ self._wait_for_processed_run(run.run_id, 600)
+ try:
+ model_prime = openml.runs.initialize_model_from_trace(
+ run_id=run.run_id,
+ repeat=0,
+ fold=0,
+ )
+ except openml.exceptions.OpenMLServerException as e:
+ e.message = "%s; run_id %d" % (e.message, run.run_id)
+ raise e
+
+ self._rerun_model_and_compare_predictions(
+ run.run_id,
+ model_prime,
+ seed,
+ create_task_obj=True,
+ )
+ self._rerun_model_and_compare_predictions(
+ run.run_id,
+ model_prime,
+ seed,
+ create_task_obj=False,
+ )
+ else:
+ run_downloaded = openml.runs.get_run(run.run_id)
+ sid = run_downloaded.setup_id
+ model_prime = openml.setups.initialize_model(sid)
+ self._rerun_model_and_compare_predictions(
+ run.run_id,
+ model_prime,
+ seed,
+ create_task_obj=True,
+ )
+ self._rerun_model_and_compare_predictions(
+ run.run_id,
+ model_prime,
+ seed,
+ create_task_obj=False,
+ )
+
+ # todo: check if runtime is present
+ self._check_fold_timing_evaluations(
+ fold_evaluations=run.fold_evaluations,
+ num_repeats=1,
+ num_folds=num_folds,
+ task_type=task_type,
+ )
+
+ # Check if run string and print representation do not run into an error
+ # The above check already verifies that all columns needed for supported
+ # representations are present.
+ # Supported: SUPERVISED_CLASSIFICATION, LEARNING_CURVE, SUPERVISED_REGRESSION
+ str(run)
+ self.logger.info(run)
+
+ return run
+
+ def _run_and_upload_classification(
+ self,
+ clf,
+ task_id,
+ n_missing_vals,
+ n_test_obs,
+ flow_expected_rsv,
+ sentinel=None,
+ ):
+ num_folds = 1 # because of holdout
+ num_iterations = 5 # for base search algorithms
+ metric = sklearn.metrics.accuracy_score # metric class
+ metric_name = "predictive_accuracy" # openml metric name
+ task_type = TaskType.SUPERVISED_CLASSIFICATION # task type
+
+ return self._run_and_upload(
+ clf=clf,
+ task_id=task_id,
+ n_missing_vals=n_missing_vals,
+ n_test_obs=n_test_obs,
+ flow_expected_rsv=flow_expected_rsv,
+ num_folds=num_folds,
+ num_iterations=num_iterations,
+ metric=metric,
+ metric_name=metric_name,
+ task_type=task_type,
+ sentinel=sentinel,
+ )
+
+ def _run_and_upload_regression(
+ self,
+ clf,
+ task_id,
+ n_missing_vals,
+ n_test_obs,
+ flow_expected_rsv,
+ sentinel=None,
+ ):
+ num_folds = 10 # because of cross-validation
+ num_iterations = 5 # for base search algorithms
+ metric = sklearn.metrics.mean_absolute_error # metric class
+ metric_name = "mean_absolute_error" # openml metric name
+ task_type = TaskType.SUPERVISED_REGRESSION # task type
+
+ return self._run_and_upload(
+ clf=clf,
+ task_id=task_id,
+ n_missing_vals=n_missing_vals,
+ n_test_obs=n_test_obs,
+ flow_expected_rsv=flow_expected_rsv,
+ num_folds=num_folds,
+ num_iterations=num_iterations,
+ metric=metric,
+ metric_name=metric_name,
+ task_type=task_type,
+ sentinel=sentinel,
+ )
+
+ @pytest.mark.sklearn()
+ @pytest.mark.test_server()
+ def test_run_and_upload_logistic_regression(self):
+ lr = LogisticRegression(solver="lbfgs", max_iter=1000)
+ task_id = self.TEST_SERVER_TASK_SIMPLE["task_id"]
+ n_missing_vals = self.TEST_SERVER_TASK_SIMPLE["n_missing_vals"]
+ n_test_obs = self.TEST_SERVER_TASK_SIMPLE["n_test_obs"]
+ self._run_and_upload_classification(lr, task_id, n_missing_vals, n_test_obs, "62501")
+
+ @pytest.mark.sklearn()
+ @pytest.mark.test_server()
+ def test_run_and_upload_linear_regression(self):
+ lr = LinearRegression()
+ task_id = self.TEST_SERVER_TASK_REGRESSION["task_id"]
+
+ task_meta_data = self.TEST_SERVER_TASK_REGRESSION["task_meta_data"]
+ _task_id = check_task_existence(**task_meta_data)
+ if _task_id is not None:
+ task_id = _task_id
+ else:
+ new_task = openml.tasks.create_task(**task_meta_data)
+ # publishes the new task
+ try:
+ new_task = new_task.publish()
+ task_id = new_task.task_id
+ except OpenMLServerException as e:
+ if e.code == 614: # Task already exists
+ # the exception message contains the task_id that was matched in the format
+ # 'Task already exists. - matched id(s): [xxxx]'
+ task_id = ast.literal_eval(e.message.split("matched id(s):")[-1].strip())[0]
+ else:
+ raise Exception(repr(e))
+ # mark to remove the uploaded task
+ TestBase._mark_entity_for_removal("task", task_id)
+ TestBase.logger.info(f"collected from test_run_functions: {task_id}")
+
+ n_missing_vals = self.TEST_SERVER_TASK_REGRESSION["n_missing_vals"]
+ n_test_obs = self.TEST_SERVER_TASK_REGRESSION["n_test_obs"]
+ self._run_and_upload_regression(lr, task_id, n_missing_vals, n_test_obs, "62501")
+
+ @pytest.mark.sklearn()
+ @pytest.mark.test_server()
+ def test_run_and_upload_pipeline_dummy_pipeline(self):
+ pipeline1 = Pipeline(
+ steps=[
+ ("scaler", StandardScaler(with_mean=False)),
+ ("dummy", DummyClassifier(strategy="prior")),
+ ],
+ )
+ task_id = self.TEST_SERVER_TASK_SIMPLE["task_id"]
+ n_missing_vals = self.TEST_SERVER_TASK_SIMPLE["n_missing_vals"]
+ n_test_obs = self.TEST_SERVER_TASK_SIMPLE["n_test_obs"]
+ self._run_and_upload_classification(pipeline1, task_id, n_missing_vals, n_test_obs, "62501")
+
+ @pytest.mark.sklearn()
+ @unittest.skipIf(
+ Version(sklearn.__version__) < Version("0.20"),
+ reason="columntransformer introduction in 0.20.0",
+ )
+ @pytest.mark.test_server()
+ def test_run_and_upload_column_transformer_pipeline(self):
+ import sklearn.compose
+ import sklearn.impute
+
+ def get_ct_cf(nominal_indices, numeric_indices):
+ inner = sklearn.compose.ColumnTransformer(
+ transformers=[
+ (
+ "numeric",
+ make_pipeline(
+ SimpleImputer(strategy="mean"),
+ sklearn.preprocessing.StandardScaler(),
+ ),
+ numeric_indices,
+ ),
+ (
+ "nominal",
+ make_pipeline(
+ CustomImputer(strategy="most_frequent"),
+ sklearn.preprocessing.OneHotEncoder(handle_unknown="ignore"),
+ ),
+ nominal_indices,
+ ),
+ ],
+ remainder="passthrough",
+ )
+ return sklearn.pipeline.Pipeline(
+ steps=[
+ ("transformer", inner),
+ ("classifier", sklearn.tree.DecisionTreeClassifier()),
+ ],
+ )
+
+ sentinel = self._get_sentinel()
+ self._run_and_upload_classification(
+ get_ct_cf(
+ self.TEST_SERVER_TASK_SIMPLE["nominal_indices"],
+ self.TEST_SERVER_TASK_SIMPLE["numeric_indices"],
+ ),
+ self.TEST_SERVER_TASK_SIMPLE["task_id"],
+ self.TEST_SERVER_TASK_SIMPLE["n_missing_vals"],
+ self.TEST_SERVER_TASK_SIMPLE["n_test_obs"],
+ "62501",
+ sentinel=sentinel,
+ )
+ # Due to #602, it is important to test this model on two tasks
+ # with different column specifications
+ self._run_and_upload_classification(
+ get_ct_cf(
+ self.TEST_SERVER_TASK_MISSING_VALS["nominal_indices"],
+ self.TEST_SERVER_TASK_MISSING_VALS["numeric_indices"],
+ ),
+ self.TEST_SERVER_TASK_MISSING_VALS["task_id"],
+ self.TEST_SERVER_TASK_MISSING_VALS["n_missing_vals"],
+ self.TEST_SERVER_TASK_MISSING_VALS["n_test_obs"],
+ "62501",
+ sentinel=sentinel,
+ )
+
+ @pytest.mark.sklearn()
+ @unittest.skip("https://github.com/openml/OpenML/issues/1180")
+ @unittest.skipIf(
+ Version(sklearn.__version__) < Version("0.20"),
+ reason="columntransformer introduction in 0.20.0",
+ )
+ @mock.patch("warnings.warn")
+ def test_run_and_upload_knn_pipeline(self, warnings_mock):
+ cat_imp = make_pipeline(
+ SimpleImputer(strategy="most_frequent"),
+ OneHotEncoder(handle_unknown="ignore"),
+ )
+ cont_imp = make_pipeline(CustomImputer(), StandardScaler())
+ from sklearn.compose import ColumnTransformer
+ from sklearn.neighbors import KNeighborsClassifier
+
+ ct = ColumnTransformer([("cat", cat_imp, cat), ("cont", cont_imp, cont)])
+ pipeline2 = Pipeline(
+ steps=[
+ ("Imputer", ct),
+ ("VarianceThreshold", VarianceThreshold()),
+ (
+ "Estimator",
+ RandomizedSearchCV(
+ KNeighborsClassifier(),
+ {"n_neighbors": list(range(2, 10))},
+ cv=3,
+ n_iter=10,
+ ),
+ ),
+ ],
+ )
+
+ task_id = self.TEST_SERVER_TASK_MISSING_VALS["task_id"]
+ n_missing_vals = self.TEST_SERVER_TASK_MISSING_VALS["n_missing_vals"]
+ n_test_obs = self.TEST_SERVER_TASK_MISSING_VALS["n_test_obs"]
+ self._run_and_upload_classification(pipeline2, task_id, n_missing_vals, n_test_obs, "62501")
+ # The warning raised is:
+ # "The total space of parameters 8 is smaller than n_iter=10.
+ # Running 8 iterations. For exhaustive searches, use GridSearchCV."
+ # It is raised three times because we once run the model to upload something and then run
+ # it again twice to compare that the predictions are reproducible.
+ warning_msg = (
+ "The total space of parameters 8 is smaller than n_iter=10. "
+ "Running 8 iterations. For exhaustive searches, use GridSearchCV."
+ )
+ call_count = 0
+ for _warnings in warnings_mock.call_args_list:
+ if _warnings[0][0] == warning_msg:
+ call_count += 1
+ assert call_count == 3
+
+ @pytest.mark.sklearn()
+ @pytest.mark.test_server()
+ def test_run_and_upload_gridsearch(self):
+ estimator_name = (
+ "base_estimator" if Version(sklearn.__version__) < Version("1.4") else "estimator"
+ )
+ gridsearch = GridSearchCV(
+ BaggingClassifier(**{estimator_name: SVC()}),
+ {f"{estimator_name}__C": [0.01, 0.1, 10], f"{estimator_name}__gamma": [0.01, 0.1, 10]},
+ cv=3,
+ )
+ task_id = self.TEST_SERVER_TASK_SIMPLE["task_id"]
+ n_missing_vals = self.TEST_SERVER_TASK_SIMPLE["n_missing_vals"]
+ n_test_obs = self.TEST_SERVER_TASK_SIMPLE["n_test_obs"]
+ run = self._run_and_upload_classification(
+ clf=gridsearch,
+ task_id=task_id,
+ n_missing_vals=n_missing_vals,
+ n_test_obs=n_test_obs,
+ flow_expected_rsv="62501",
+ )
+ assert len(run.trace.trace_iterations) == 9
+
+ @pytest.mark.sklearn()
+ @pytest.mark.test_server()
+ def test_run_and_upload_randomsearch(self):
+ randomsearch = RandomizedSearchCV(
+ RandomForestClassifier(n_estimators=5),
+ {
+ "max_depth": [3, None],
+ "max_features": [1, 2, 3, 4],
+ "min_samples_split": [2, 3, 4, 5, 6, 7, 8, 9, 10],
+ "min_samples_leaf": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
+ "bootstrap": [True, False],
+ "criterion": ["gini", "entropy"],
+ },
+ cv=StratifiedKFold(n_splits=2, shuffle=True),
+ n_iter=5,
+ )
+ # The random states for the RandomizedSearchCV is set after the
+ # random state of the RandomForestClassifier is set, therefore,
+ # it has a different value than the other examples before
+ task_id = self.TEST_SERVER_TASK_SIMPLE["task_id"]
+ n_missing_vals = self.TEST_SERVER_TASK_SIMPLE["n_missing_vals"]
+ n_test_obs = self.TEST_SERVER_TASK_SIMPLE["n_test_obs"]
+ run = self._run_and_upload_classification(
+ clf=randomsearch,
+ task_id=task_id,
+ n_missing_vals=n_missing_vals,
+ n_test_obs=n_test_obs,
+ flow_expected_rsv="12172",
+ )
+ assert len(run.trace.trace_iterations) == 5
+ trace = openml.runs.get_run_trace(run.run_id)
+ assert len(trace.trace_iterations) == 5
+
+ @pytest.mark.sklearn()
+ @pytest.mark.test_server()
+ def test_run_and_upload_maskedarrays(self):
+ # This testcase is important for 2 reasons:
+ # 1) it verifies the correct handling of masked arrays (not all
+ # parameters are active)
+ # 2) it verifies the correct handling of a 2-layered grid search
+ gridsearch = GridSearchCV(
+ RandomForestClassifier(n_estimators=5),
+ [{"max_features": [2, 4]}, {"min_samples_leaf": [1, 10]}],
+ cv=StratifiedKFold(n_splits=2, shuffle=True),
+ )
+ # The random states for the GridSearchCV is set after the
+ # random state of the RandomForestClassifier is set, therefore,
+ # it has a different value than the other examples before
+ task_id = self.TEST_SERVER_TASK_SIMPLE["task_id"]
+ n_missing_vals = self.TEST_SERVER_TASK_SIMPLE["n_missing_vals"]
+ n_test_obs = self.TEST_SERVER_TASK_SIMPLE["n_test_obs"]
+ self._run_and_upload_classification(
+ gridsearch,
+ task_id,
+ n_missing_vals,
+ n_test_obs,
+ "12172",
+ )
+
+ ##########################################################################
+
+ @pytest.mark.sklearn()
+ @pytest.mark.test_server()
+ def test_learning_curve_task_1(self):
+ task_id = 801 # diabates dataset
+ num_test_instances = 6144 # for learning curve
+ num_missing_vals = 0
+ num_repeats = 1
+ num_folds = 10
+ num_samples = 8
+
+ pipeline1 = Pipeline(
+ steps=[
+ ("scaler", StandardScaler(with_mean=False)),
+ ("dummy", DummyClassifier(strategy="prior")),
+ ],
+ )
+ run = self._perform_run(
+ task_id,
+ num_test_instances,
+ num_missing_vals,
+ pipeline1,
+ flow_expected_rsv="62501",
+ )
+ self._check_sample_evaluations(run.sample_evaluations, num_repeats, num_folds, num_samples)
+
+ @pytest.mark.sklearn()
+ @pytest.mark.test_server()
+ def test_learning_curve_task_2(self):
+ task_id = 801 # diabates dataset
+ num_test_instances = 6144 # for learning curve
+ num_missing_vals = 0
+ num_repeats = 1
+ num_folds = 10
+ num_samples = 8
+
+ pipeline2 = Pipeline(
+ steps=[
+ ("Imputer", SimpleImputer(strategy="median")),
+ ("VarianceThreshold", VarianceThreshold()),
+ (
+ "Estimator",
+ RandomizedSearchCV(
+ DecisionTreeClassifier(),
+ {
+ "min_samples_split": [2**x for x in range(1, 8)],
+ "min_samples_leaf": [2**x for x in range(7)],
+ },
+ cv=3,
+ n_iter=10,
+ ),
+ ),
+ ],
+ )
+ run = self._perform_run(
+ task_id,
+ num_test_instances,
+ num_missing_vals,
+ pipeline2,
+ flow_expected_rsv="62501",
+ )
+ self._check_sample_evaluations(run.sample_evaluations, num_repeats, num_folds, num_samples)
+
+ @pytest.mark.sklearn()
+ @unittest.skipIf(
+ Version(sklearn.__version__) < Version("0.21"),
+ reason="Pipelines don't support indexing (used for the assert check)",
+ )
+ @pytest.mark.test_server()
+ def test_initialize_cv_from_run(self):
+ randomsearch = Pipeline(
+ [
+ ("enc", OneHotEncoder(handle_unknown="ignore")),
+ (
+ "rs",
+ RandomizedSearchCV(
+ RandomForestClassifier(n_estimators=5),
+ {
+ "max_depth": [3, None],
+ "max_features": [1, 2, 3, 4],
+ "min_samples_split": [2, 3, 4, 5, 6, 7, 8, 9, 10],
+ "min_samples_leaf": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
+ "bootstrap": [True, False],
+ "criterion": ["gini", "entropy"],
+ },
+ cv=StratifiedKFold(n_splits=2, shuffle=True),
+ n_iter=2,
+ ),
+ ),
+ ],
+ )
+
+ task = openml.tasks.get_task(11) # kr-vs-kp; holdout
+ run = openml.runs.run_model_on_task(
+ model=randomsearch,
+ task=task,
+ seed=1,
+ )
+ run_ = run.publish()
+ TestBase._mark_entity_for_removal("run", run.run_id)
+ TestBase.logger.info(f"collected from test_run_functions: {run.run_id}")
+ run = openml.runs.get_run(run_.run_id)
+
+ modelR = openml.runs.initialize_model_from_run(run_id=run.run_id)
+ modelS = openml.setups.initialize_model(setup_id=run.setup_id)
+
+ assert modelS[-1].cv.random_state == 62501
+ assert modelR[-1].cv.random_state == 62501
+
+ def _test_local_evaluations(self, run):
+ # compare with the scores in user defined measures
+ accuracy_scores_provided = []
+ for rep in run.fold_evaluations["predictive_accuracy"]:
+ for fold in run.fold_evaluations["predictive_accuracy"][rep]:
+ accuracy_scores_provided.append(
+ run.fold_evaluations["predictive_accuracy"][rep][fold],
+ )
+ accuracy_scores = run.get_metric_fn(sklearn.metrics.accuracy_score)
+ np.testing.assert_array_almost_equal(accuracy_scores_provided, accuracy_scores)
+
+ # also check if we can obtain some other scores:
+ tests = [
+ (sklearn.metrics.cohen_kappa_score, {"weights": None}),
+ (sklearn.metrics.roc_auc_score, {}),
+ (sklearn.metrics.average_precision_score, {}),
+ (sklearn.metrics.precision_score, {"average": "macro"}),
+ (sklearn.metrics.brier_score_loss, {}),
+ ]
+ if Version(sklearn.__version__) < Version("0.23"):
+ tests.append((sklearn.metrics.jaccard_similarity_score, {}))
+ else:
+ tests.append((sklearn.metrics.jaccard_score, {}))
+ for _test_idx, test in enumerate(tests):
+ alt_scores = run.get_metric_fn(
+ sklearn_fn=test[0],
+ kwargs=test[1],
+ )
+ assert len(alt_scores) == 10
+ for idx in range(len(alt_scores)):
+ assert alt_scores[idx] >= 0
+ assert alt_scores[idx] <= 1
+
+ @pytest.mark.sklearn()
+ @pytest.mark.test_server()
+ def test_local_run_swapped_parameter_order_model(self):
+ clf = DecisionTreeClassifier()
+ australian_task = 595 # Australian; crossvalidation
+ task = openml.tasks.get_task(australian_task)
+
+ # task and clf are purposely in the old order
+ run = openml.runs.run_model_on_task(
+ task,
+ clf,
+ upload_flow=False,
+ )
+
+ self._test_local_evaluations(run)
+
+ @pytest.mark.sklearn()
+ @unittest.skipIf(
+ Version(sklearn.__version__) < Version("0.20"),
+ reason="SimpleImputer doesn't handle mixed type DataFrame as input",
+ )
+ @pytest.mark.test_server()
+ def test_local_run_swapped_parameter_order_flow(self):
+ # construct sci-kit learn classifier
+ clf = Pipeline(
+ steps=[
+ ("imputer", SimpleImputer(strategy="most_frequent")),
+ ("encoder", OneHotEncoder(handle_unknown="ignore")),
+ ("estimator", RandomForestClassifier(n_estimators=10)),
+ ],
+ )
+
+ flow = self.extension.model_to_flow(clf)
+ # download task
+ task = openml.tasks.get_task(7) # kr-vs-kp; crossvalidation
+
+ # invoke OpenML run
+ run = openml.runs.run_flow_on_task(
+ task,
+ flow,
+ upload_flow=False,
+ )
+
+ self._test_local_evaluations(run)
+
+ @pytest.mark.sklearn()
+ @unittest.skipIf(
+ Version(sklearn.__version__) < Version("0.20"),
+ reason="SimpleImputer doesn't handle mixed type DataFrame as input",
+ )
+ @pytest.mark.test_server()
+ def test_local_run_metric_score(self):
+ # construct sci-kit learn classifier
+ clf = Pipeline(
+ steps=[
+ ("imputer", SimpleImputer(strategy="most_frequent")),
+ ("encoder", OneHotEncoder(handle_unknown="ignore")),
+ ("estimator", RandomForestClassifier(n_estimators=10)),
+ ],
+ )
+
+ # download task
+ task = openml.tasks.get_task(7) # kr-vs-kp; crossvalidation
+
+ # invoke OpenML run
+ run = openml.runs.run_model_on_task(
+ model=clf,
+ task=task,
+ upload_flow=False,
+ )
+
+ self._test_local_evaluations(run)
+
+ @pytest.mark.production_server()
+ def test_online_run_metric_score(self):
+ self.use_production_server()
+
+ # important to use binary classification task,
+ # due to assertions
+ run = openml.runs.get_run(9864498)
+
+ self._test_local_evaluations(run)
+
+ @pytest.mark.sklearn()
+ @unittest.skipIf(
+ Version(sklearn.__version__) < Version("0.20"),
+ reason="SimpleImputer doesn't handle mixed type DataFrame as input",
+ )
+ @pytest.mark.test_server()
+ def test_initialize_model_from_run(self):
+ clf = sklearn.pipeline.Pipeline(
+ steps=[
+ ("Imputer", SimpleImputer(strategy="most_frequent")),
+ ("VarianceThreshold", VarianceThreshold(threshold=0.05)),
+ ("Estimator", GaussianNB()),
+ ],
+ )
+ task_meta_data = {
+ "task_type": TaskType.SUPERVISED_CLASSIFICATION,
+ "dataset_id": 128, # iris
+ "estimation_procedure_id": 1,
+ "target_name": "class",
+ }
+ _task_id = check_task_existence(**task_meta_data)
+ if _task_id is not None:
+ task_id = _task_id
+ else:
+ new_task = openml.tasks.create_task(**task_meta_data)
+ # publishes the new task
+ try:
+ new_task = new_task.publish()
+ task_id = new_task.task_id
+ except OpenMLServerException as e:
+ if e.code == 614: # Task already exists
+ # the exception message contains the task_id that was matched in the format
+ # 'Task already exists. - matched id(s): [xxxx]'
+ task_id = ast.literal_eval(e.message.split("matched id(s):")[-1].strip())[0]
+ else:
+ raise Exception(repr(e))
+ # mark to remove the uploaded task
+ TestBase._mark_entity_for_removal("task", task_id)
+ TestBase.logger.info(f"collected from test_run_functions: {task_id}")
+
+ task = openml.tasks.get_task(task_id)
+ run = openml.runs.run_model_on_task(
+ model=clf,
+ task=task,
+ )
+ run_ = run.publish()
+ TestBase._mark_entity_for_removal("run", run_.run_id)
+ TestBase.logger.info(f"collected from test_run_functions: {run_.run_id}")
+ run = openml.runs.get_run(run_.run_id)
+
+ modelR = openml.runs.initialize_model_from_run(run_id=run.run_id)
+ modelS = openml.setups.initialize_model(setup_id=run.setup_id)
+
+ flowR = self.extension.model_to_flow(modelR)
+ flowS = self.extension.model_to_flow(modelS)
+ flowL = self.extension.model_to_flow(clf)
+ openml.flows.assert_flows_equal(flowR, flowL)
+ openml.flows.assert_flows_equal(flowS, flowL)
+
+ assert flowS.components["Imputer"].parameters["strategy"] == '"most_frequent"'
+ assert flowS.components["VarianceThreshold"].parameters["threshold"] == "0.05"
+
+ @pytest.mark.sklearn()
+ @unittest.skipIf(
+ Version(sklearn.__version__) < Version("0.20"),
+ reason="SimpleImputer doesn't handle mixed type DataFrame as input",
+ )
+ @pytest.mark.test_server()
+ def test__run_exists(self):
+ # would be better to not sentinel these clfs,
+ # so we do not have to perform the actual runs
+ # and can just check their status on line
+ rs = 1
+ clfs = [
+ sklearn.pipeline.Pipeline(
+ steps=[
+ ("Imputer", SimpleImputer(strategy="mean")),
+ ("VarianceThreshold", VarianceThreshold(threshold=0.05)),
+ ("Estimator", DecisionTreeClassifier(max_depth=4)),
+ ],
+ ),
+ sklearn.pipeline.Pipeline(
+ steps=[
+ ("Imputer", SimpleImputer(strategy="most_frequent")),
+ ("VarianceThreshold", VarianceThreshold(threshold=0.1)),
+ ("Estimator", DecisionTreeClassifier(max_depth=4)),
+ ],
+ ),
+ ]
+
+ task = openml.tasks.get_task(115) # diabetes; crossvalidation
+
+ for clf in clfs:
+ try:
+ # first populate the server with this run.
+ # skip run if it was already performed.
+ run = openml.runs.run_model_on_task(
+ model=clf,
+ task=task,
+ seed=rs,
+ avoid_duplicate_runs=True,
+ upload_flow=True,
+ )
+ run.publish()
+ TestBase._mark_entity_for_removal("run", run.run_id)
+ TestBase.logger.info(f"collected from test_run_functions: {run.run_id}")
+ except openml.exceptions.PyOpenMLError:
+ # run already existed. Great.
+ pass
+
+ flow = self.extension.model_to_flow(clf)
+ flow_exists = openml.flows.flow_exists(flow.name, flow.external_version)
+ assert flow_exists > 0, "Server says flow from run does not exist."
+ # Do NOT use get_flow reinitialization, this potentially sets
+ # hyperparameter values wrong. Rather use the local model.
+ downloaded_flow = openml.flows.get_flow(flow_exists)
+ downloaded_flow.model = clf
+ setup_exists = openml.setups.setup_exists(downloaded_flow)
+ assert setup_exists > 0, "Server says setup of run does not exist."
+ run_ids = run_exists(task.task_id, setup_exists)
+ assert run_ids, (run_ids, clf)
+
+ @pytest.mark.sklearn()
+ @pytest.mark.test_server()
+ def test_run_with_illegal_flow_id(self):
+ # check the case where the user adds an illegal flow id to a
+ # non-existing flo
+ task = openml.tasks.get_task(115) # diabetes; crossvalidation
+ clf = DecisionTreeClassifier()
+ flow = self.extension.model_to_flow(clf)
+ flow, _ = self._add_sentinel_to_flow_name(flow, None)
+ flow.flow_id = -1
+ expected_message_regex = (
+ r"Flow does not exist on the server, but 'flow.flow_id' is not None."
+ )
+ with pytest.raises(openml.exceptions.PyOpenMLError, match=expected_message_regex):
+ openml.runs.run_flow_on_task(
+ task=task,
+ flow=flow,
+ avoid_duplicate_runs=True,
+ )
+
+ @pytest.mark.sklearn()
+ @pytest.mark.test_server()
+ def test_run_with_illegal_flow_id_after_load(self):
+ # Same as `test_run_with_illegal_flow_id`, but test this error is also
+ # caught if the run is stored to and loaded from disk first.
+ task = openml.tasks.get_task(115) # diabetes; crossvalidation
+ clf = DecisionTreeClassifier()
+ flow = self.extension.model_to_flow(clf)
+ flow, _ = self._add_sentinel_to_flow_name(flow, None)
+ flow.flow_id = -1
+ run = openml.runs.run_flow_on_task(
+ task=task,
+ flow=flow,
+ upload_flow=False,
+ )
+
+ cache_path = os.path.join(
+ self.workdir,
+ "runs",
+ str(random.getrandbits(128)),
+ )
+ run.to_filesystem(cache_path)
+ loaded_run = openml.runs.OpenMLRun.from_filesystem(cache_path)
+
+ expected_message_regex = (
+ r"Flow does not exist on the server, but 'flow.flow_id' is not None."
+ )
+ with pytest.raises(openml.exceptions.PyOpenMLError, match=expected_message_regex):
+ loaded_run.publish()
+ TestBase._mark_entity_for_removal("run", loaded_run.run_id)
+ TestBase.logger.info(f"collected from test_run_functions: {loaded_run.run_id}")
+
+ @pytest.mark.sklearn()
+ @pytest.mark.test_server()
+ def test_run_with_illegal_flow_id_1(self):
+ # Check the case where the user adds an illegal flow id to an existing
+ # flow. Comes to a different value error than the previous test
+ task = openml.tasks.get_task(115) # diabetes; crossvalidation
+ clf = DecisionTreeClassifier()
+ flow_orig = self.extension.model_to_flow(clf)
+ try:
+ flow_orig.publish() # ensures flow exist on server
+ TestBase._mark_entity_for_removal("flow", flow_orig.flow_id, flow_orig.name)
+ TestBase.logger.info(f"collected from test_run_functions: {flow_orig.flow_id}")
+ except openml.exceptions.OpenMLServerException:
+ # flow already exists
+ pass
+ flow_new = self.extension.model_to_flow(clf)
+
+ flow_new.flow_id = -1
+ expected_message_regex = "Local flow_id does not match server flow_id: '-1' vs '[0-9]+'"
+ with pytest.raises(openml.exceptions.PyOpenMLError, match=expected_message_regex):
+ openml.runs.run_flow_on_task(
+ task=task,
+ flow=flow_new,
+ avoid_duplicate_runs=True,
+ )
+
+ @pytest.mark.sklearn()
+ @pytest.mark.test_server()
+ def test_run_with_illegal_flow_id_1_after_load(self):
+ # Same as `test_run_with_illegal_flow_id_1`, but test this error is
+ # also caught if the run is stored to and loaded from disk first.
+ task = openml.tasks.get_task(115) # diabetes; crossvalidation
+ clf = DecisionTreeClassifier()
+ flow_orig = self.extension.model_to_flow(clf)
+ try:
+ flow_orig.publish() # ensures flow exist on server
+ TestBase._mark_entity_for_removal("flow", flow_orig.flow_id, flow_orig.name)
+ TestBase.logger.info(f"collected from test_run_functions: {flow_orig.flow_id}")
+ except openml.exceptions.OpenMLServerException:
+ # flow already exists
+ pass
+ flow_new = self.extension.model_to_flow(clf)
+ flow_new.flow_id = -1
+
+ run = openml.runs.run_flow_on_task(
+ task=task,
+ flow=flow_new,
+ upload_flow=False,
+ )
+
+ cache_path = os.path.join(
+ self.workdir,
+ "runs",
+ str(random.getrandbits(128)),
+ )
+ run.to_filesystem(cache_path)
+ loaded_run = openml.runs.OpenMLRun.from_filesystem(cache_path)
+
+ expected_message_regex = "Local flow_id does not match server flow_id: '-1' vs '[0-9]+'"
+ self.assertRaisesRegex(
+ openml.exceptions.PyOpenMLError,
+ expected_message_regex,
+ loaded_run.publish,
+ )
+
+ @pytest.mark.sklearn()
+ @unittest.skipIf(
+ Version(sklearn.__version__) < Version("0.20"),
+ reason="OneHotEncoder cannot handle mixed type DataFrame as input",
+ )
+ @pytest.mark.test_server()
+ def test__run_task_get_arffcontent(self):
+ task = openml.tasks.get_task(7) # kr-vs-kp; crossvalidation
+ num_instances = 3196
+ num_folds = 10
+ num_repeats = 1
+ loss = "log" if Version(sklearn.__version__) < Version("1.3") else "log_loss"
+
+ clf = make_pipeline(
+ OneHotEncoder(handle_unknown="ignore"),
+ SGDClassifier(loss=loss, random_state=1),
+ )
+ res = openml.runs.functions._run_task_get_arffcontent(
+ extension=self.extension,
+ model=clf,
+ task=task,
+ add_local_measures=True,
+ )
+ arff_datacontent, trace, fold_evaluations, _ = res
+ # predictions
+ assert isinstance(arff_datacontent, list)
+ # trace. SGD does not produce any
+ assert isinstance(trace, type(None))
+
+ task_type = TaskType.SUPERVISED_CLASSIFICATION
+ self._check_fold_timing_evaluations(
+ fold_evaluations=fold_evaluations,
+ num_repeats=num_repeats,
+ num_folds=num_folds,
+ task_type=task_type,
+ )
+
+ # 10 times 10 fold CV of 150 samples
+ assert len(arff_datacontent) == num_instances * num_repeats
+ for arff_line in arff_datacontent:
+ # check number columns
+ assert len(arff_line) == 8
+ # check repeat
+ assert arff_line[0] >= 0
+ assert arff_line[0] <= num_repeats - 1
+ # check fold
+ assert arff_line[1] >= 0
+ assert arff_line[1] <= num_folds - 1
+ # check row id
+ assert arff_line[2] >= 0
+ assert arff_line[2] <= num_instances - 1
+ # check prediction and ground truth columns
+ assert arff_line[4] in ["won", "nowin"]
+ assert arff_line[5] in ["won", "nowin"]
+ # check confidences
+ self.assertAlmostEqual(sum(arff_line[6:]), 1.0)
+
+ def test__create_trace_from_arff(self):
+ with open(self.static_cache_dir / "misc" / "trace.arff") as arff_file:
+ trace_arff = arff.load(arff_file)
+ OpenMLRunTrace.trace_from_arff(trace_arff)
+
+ @pytest.mark.production_server()
+ def test_get_run(self):
+ # this run is not available on test
+ self.use_production_server()
+ run = openml.runs.get_run(473351)
+ assert run.dataset_id == 357
+ assert run.evaluations["f_measure"] == 0.841225
+ for i, value in [
+ (0, 0.840918),
+ (1, 0.839458),
+ (2, 0.839613),
+ (3, 0.842571),
+ (4, 0.839567),
+ (5, 0.840922),
+ (6, 0.840985),
+ (7, 0.847129),
+ (8, 0.84218),
+ (9, 0.844014),
+ ]:
+ assert run.fold_evaluations["f_measure"][0][i] == value
+ assert "weka" in run.tags
+ assert "weka_3.7.12" in run.tags
+ assert run.predictions_url.endswith(
+ "/data/download/1667125/weka_generated_predictions4575715871712251329.arff"
+ )
+
+ def _check_run(self, run):
+ # This tests that the API returns seven entries for each run
+ # Check out https://openml.org/api/v1/xml/run/list/flow/1154
+ # They are run_id, task_id, task_type_id, setup_id, flow_id, uploader, upload_time
+ # error_message and run_details exist, too, but are not used so far. We need to update
+ # this check once they are used!
+ assert isinstance(run, dict)
+ assert len(run) == 8, str(run)
+
+ @pytest.mark.production_server()
+ def test_get_runs_list(self):
+ # TODO: comes from live, no such lists on test
+ self.use_production_server()
+ runs = openml.runs.list_runs(id=[2], display_errors=True)
+ assert len(runs) == 1
+ for run in runs.to_dict(orient="index").values():
+ self._check_run(run)
+
+ @pytest.mark.test_server()
+ def test_list_runs_empty(self):
+ runs = openml.runs.list_runs(task=[0])
+ assert runs.empty
+
+ @pytest.mark.production_server()
+ def test_get_runs_list_by_task(self):
+ # TODO: comes from live, no such lists on test
+ self.use_production_server()
+ task_ids = [20]
+ runs = openml.runs.list_runs(task=task_ids)
+ assert len(runs) >= 590
+ for run in runs.to_dict(orient="index").values():
+ assert run["task_id"] in task_ids
+ self._check_run(run)
+ num_runs = len(runs)
+
+ task_ids.append(21)
+ runs = openml.runs.list_runs(task=task_ids)
+ assert len(runs) >= num_runs + 1
+ for run in runs.to_dict(orient="index").values():
+ assert run["task_id"] in task_ids
+ self._check_run(run)
+
+ @pytest.mark.production_server()
+ def test_get_runs_list_by_uploader(self):
+ # TODO: comes from live, no such lists on test
+ self.use_production_server()
+ # 29 is Dominik Kirchhoff
+ uploader_ids = [29]
+
+ runs = openml.runs.list_runs(uploader=uploader_ids)
+ assert len(runs) >= 2
+ for run in runs.to_dict(orient="index").values():
+ assert run["uploader"] in uploader_ids
+ self._check_run(run)
+ num_runs = len(runs)
+
+ uploader_ids.append(274)
+
+ runs = openml.runs.list_runs(uploader=uploader_ids)
+ assert len(runs) >= num_runs + 1
+ for run in runs.to_dict(orient="index").values():
+ assert run["uploader"] in uploader_ids
+ self._check_run(run)
+
+ @pytest.mark.production_server()
+ def test_get_runs_list_by_flow(self):
+ # TODO: comes from live, no such lists on test
+ self.use_production_server()
+ flow_ids = [1154]
+ runs = openml.runs.list_runs(flow=flow_ids)
+ assert len(runs) >= 1
+ for run in runs.to_dict(orient="index").values():
+ assert run["flow_id"] in flow_ids
+ self._check_run(run)
+ num_runs = len(runs)
+
+ flow_ids.append(1069)
+ runs = openml.runs.list_runs(flow=flow_ids)
+ assert len(runs) >= num_runs + 1
+ for run in runs.to_dict(orient="index").values():
+ assert run["flow_id"] in flow_ids
+ self._check_run(run)
+
+ @pytest.mark.production_server()
+ def test_get_runs_pagination(self):
+ # TODO: comes from live, no such lists on test
+ self.use_production_server()
+ uploader_ids = [1]
+ size = 10
+ max = 100
+ for i in range(0, max, size):
+ runs = openml.runs.list_runs(offset=i, size=size, uploader=uploader_ids)
+ assert size >= len(runs)
+ for run in runs.to_dict(orient="index").values():
+ assert run["uploader"] in uploader_ids
+
+ @pytest.mark.production_server()
+ def test_get_runs_list_by_filters(self):
+ # TODO: comes from live, no such lists on test
+ self.use_production_server()
+ ids = [505212, 6100]
+ tasks = [2974, 339]
+ uploaders_1 = [1, 2]
+ uploaders_2 = [29, 274]
+ flows = [74, 1718]
+
+ """
+ Since the results are taken by batch size, the function does not
+ throw an OpenMLServerError anymore. Instead it throws a
+ TimeOutException. For the moment commented out.
+ """
+ # self.assertRaises(openml.exceptions.OpenMLServerError,
+ # openml.runs.list_runs)
+
+ runs = openml.runs.list_runs(id=ids)
+ assert len(runs) == 2
+
+ runs = openml.runs.list_runs(task=tasks)
+ assert len(runs) >= 2
+
+ runs = openml.runs.list_runs(uploader=uploaders_2)
+ assert len(runs) >= 10
+
+ runs = openml.runs.list_runs(flow=flows)
+ assert len(runs) >= 100
+
+ runs = openml.runs.list_runs(
+ id=ids,
+ task=tasks,
+ uploader=uploaders_1,
+ )
+ assert len(runs) == 2
+
+ @pytest.mark.production_server()
+ @pytest.mark.xfail(reason="failures_issue_1544", strict=False)
+ def test_get_runs_list_by_tag(self):
+ # We don't have tagged runs on the test server
+ self.use_production_server()
+ # Don't remove the size restriction: this query is too expensive without
+ runs = openml.runs.list_runs(tag="curves", size=2)
+ assert len(runs) >= 1
+
+ @pytest.mark.sklearn()
+ @unittest.skipIf(
+ Version(sklearn.__version__) < Version("0.20"),
+ reason="columntransformer introduction in 0.20.0",
+ )
+ @pytest.mark.test_server()
+ def test_run_on_dataset_with_missing_labels_dataframe(self):
+ # Check that _run_task_get_arffcontent works when one of the class
+ # labels only declared in the arff file, but is not present in the
+ # actual data
+ task = openml.tasks.get_task(2) # anneal; crossvalidation
+
+ from sklearn.compose import ColumnTransformer
+
+ cat_imp = make_pipeline(
+ SimpleImputer(strategy="most_frequent"),
+ OneHotEncoder(handle_unknown="ignore"),
+ )
+ cont_imp = make_pipeline(CustomImputer(), StandardScaler())
+ ct = ColumnTransformer([("cat", cat_imp, cat), ("cont", cont_imp, cont)])
+ model = Pipeline(
+ steps=[("preprocess", ct), ("estimator", sklearn.tree.DecisionTreeClassifier())],
+ ) # build a sklearn classifier
+
+ data_content, _, _, _ = _run_task_get_arffcontent(
+ model=model,
+ task=task,
+ extension=self.extension,
+ add_local_measures=True,
+ )
+ # 2 folds, 5 repeats; keep in mind that this task comes from the test
+ # server, the task on the live server is different
+ assert len(data_content) == 4490
+ for row in data_content:
+ # repeat, fold, row_id, 6 confidences, prediction and correct label
+ assert len(row) == 12
+
+ @pytest.mark.sklearn()
+ @unittest.skipIf(
+ Version(sklearn.__version__) < Version("0.20"),
+ reason="columntransformer introduction in 0.20.0",
+ )
+ @pytest.mark.test_server()
+ def test_run_on_dataset_with_missing_labels_array(self):
+ # Check that _run_task_get_arffcontent works when one of the class
+ # labels only declared in the arff file, but is not present in the
+ # actual data
+ task = openml.tasks.get_task(2) # anneal; crossvalidation
+ # task_id=2 on test server has 38 columns with 6 numeric columns
+ cont_idx = [3, 4, 8, 32, 33, 34]
+ cat_idx = list(set(np.arange(38)) - set(cont_idx))
+ cont = np.array([False] * 38)
+ cat = np.array([False] * 38)
+ cont[cont_idx] = True
+ cat[cat_idx] = True
+
+ from sklearn.compose import ColumnTransformer
+
+ cat_imp = make_pipeline(
+ SimpleImputer(strategy="most_frequent"),
+ OneHotEncoder(handle_unknown="ignore"),
+ )
+ cont_imp = make_pipeline(CustomImputer(), StandardScaler())
+ ct = ColumnTransformer([("cat", cat_imp, cat), ("cont", cont_imp, cont)])
+ model = Pipeline(
+ steps=[("preprocess", ct), ("estimator", sklearn.tree.DecisionTreeClassifier())],
+ ) # build a sklearn classifier
+
+ data_content, _, _, _ = _run_task_get_arffcontent(
+ model=model,
+ task=task,
+ extension=self.extension,
+ add_local_measures=True,
+ )
+ # 2 folds, 5 repeats; keep in mind that this task comes from the test
+ # server, the task on the live server is different
+ assert len(data_content) == 4490
+ for row in data_content:
+ # repeat, fold, row_id, 6 confidences, prediction and correct label
+ assert len(row) == 12
+
+ @pytest.mark.test_server()
+ def test_get_cached_run(self):
+ openml.config.set_root_cache_directory(self.static_cache_dir)
+ openml.runs.functions._get_cached_run(1)
+
+ def test_get_uncached_run(self):
+ openml.config.set_root_cache_directory(self.static_cache_dir)
+ with pytest.raises(openml.exceptions.OpenMLCacheException):
+ openml.runs.functions._get_cached_run(10)
+
+ @pytest.mark.sklearn()
+ @pytest.mark.test_server()
+ def test_run_flow_on_task_downloaded_flow(self):
+ model = sklearn.ensemble.RandomForestClassifier(n_estimators=33)
+ flow = self.extension.model_to_flow(model)
+ flow.publish(raise_error_if_exists=False)
+ TestBase._mark_entity_for_removal("flow", flow.flow_id, flow.name)
+ TestBase.logger.info(f"collected from test_run_functions: {flow.flow_id}")
+
+ downloaded_flow = openml.flows.get_flow(flow.flow_id)
+ task = openml.tasks.get_task(self.TEST_SERVER_TASK_SIMPLE["task_id"])
+ run = openml.runs.run_flow_on_task(
+ flow=downloaded_flow,
+ task=task,
+ upload_flow=False,
+ )
+
+ run.publish()
+ TestBase._mark_entity_for_removal("run", run.run_id)
+ TestBase.logger.info(f"collected from {__file__.split('/')[-1]}: {run.run_id}")
+
+ @pytest.mark.production_server()
+ def test_format_prediction_non_supervised(self):
+ # non-supervised tasks don't exist on the test server
+ self.use_production_server()
+ clustering = openml.tasks.get_task(126033, download_data=False)
+ ignored_input = [0] * 5
+ with pytest.raises(
+ NotImplementedError, match=r"Formatting for is not supported."
+ ):
+ format_prediction(clustering, *ignored_input)
+
+ @pytest.mark.test_server()
+ def test_format_prediction_classification_no_probabilities(self):
+ classification = openml.tasks.get_task(
+ self.TEST_SERVER_TASK_SIMPLE["task_id"],
+ download_data=False,
+ )
+ ignored_input = [0] * 5
+ with pytest.raises(ValueError, match="`proba` is required for classification task"):
+ format_prediction(classification, *ignored_input, proba=None)
+
+ @pytest.mark.test_server()
+ def test_format_prediction_classification_incomplete_probabilities(self):
+ classification = openml.tasks.get_task(
+ self.TEST_SERVER_TASK_SIMPLE["task_id"],
+ download_data=False,
+ )
+ ignored_input = [0] * 5
+ incomplete_probabilities = {c: 0.2 for c in classification.class_labels[1:]}
+ with pytest.raises(ValueError, match="Each class should have a predicted probability"):
+ format_prediction(classification, *ignored_input, proba=incomplete_probabilities)
+
+ @pytest.mark.test_server()
+ def test_format_prediction_task_without_classlabels_set(self):
+ classification = openml.tasks.get_task(
+ self.TEST_SERVER_TASK_SIMPLE["task_id"],
+ download_data=False,
+ )
+ classification.class_labels = None
+ ignored_input = [0] * 5
+ with pytest.raises(ValueError, match="The classification task must have class labels set"):
+ format_prediction(classification, *ignored_input, proba={})
+
+ @pytest.mark.test_server()
+ def test_format_prediction_task_learning_curve_sample_not_set(self):
+ learning_curve = openml.tasks.get_task(801, download_data=False) # diabetes;crossvalidation
+ probabilities = {c: 0.2 for c in learning_curve.class_labels}
+ ignored_input = [0] * 5
+ with pytest.raises(ValueError, match="`sample` can not be none for LearningCurveTask"):
+ format_prediction(learning_curve, *ignored_input, sample=None, proba=probabilities)
+
+ @pytest.mark.test_server()
+ def test_format_prediction_task_regression(self):
+ task_meta_data = self.TEST_SERVER_TASK_REGRESSION["task_meta_data"]
+ _task_id = check_task_existence(**task_meta_data)
+ if _task_id is not None:
+ task_id = _task_id
+ else:
+ new_task = openml.tasks.create_task(**task_meta_data)
+ # publishes the new task
+ try:
+ new_task = new_task.publish()
+ task_id = new_task.task_id
+ except OpenMLServerException as e:
+ if e.code == 614: # Task already exists
+ # the exception message contains the task_id that was matched in the format
+ # 'Task already exists. - matched id(s): [xxxx]'
+ task_id = ast.literal_eval(e.message.split("matched id(s):")[-1].strip())[0]
+ else:
+ raise Exception(repr(e))
+ # mark to remove the uploaded task
+ TestBase._mark_entity_for_removal("task", task_id)
+ TestBase.logger.info(f"collected from test_run_functions: {task_id}")
+
+ regression = openml.tasks.get_task(task_id, download_data=False)
+ ignored_input = [0] * 5
+ res = format_prediction(regression, *ignored_input)
+ self.assertListEqual(res, [0] * 5)
+
+
+
+ @unittest.skipIf(
+ Version(sklearn.__version__) < Version("0.20"),
+ reason="SimpleImputer doesn't handle mixed type DataFrame as input",
+ )
+ @pytest.mark.sklearn()
+ @pytest.mark.test_server()
+ def test_delete_run(self):
+ rs = np.random.randint(1, 2**31 - 1)
+ clf = sklearn.pipeline.Pipeline(
+ steps=[
+ (f"test_server_imputer_{rs}", SimpleImputer()),
+ ("estimator", DecisionTreeClassifier()),
+ ],
+ )
+ task = openml.tasks.get_task(32) # diabetes; crossvalidation
+
+ run = openml.runs.run_model_on_task(
+ model=clf, task=task, seed=rs,
+ )
+ run.publish()
+
+ with pytest.raises(openml.exceptions.OpenMLRunsExistError):
+ openml.runs.run_model_on_task(model=clf, task=task, seed=rs, avoid_duplicate_runs=True)
+
+ TestBase._mark_entity_for_removal("run", run.run_id)
+ TestBase.logger.info(f"collected from test_run_functions: {run.run_id}")
+
+ _run_id = run.run_id
+ assert delete_run(_run_id)
+
+ @pytest.mark.skip(reason="run id is in problematic state on test server due to PR#1454")
+ @unittest.skipIf(
+ Version(sklearn.__version__) < Version("0.20"),
+ reason="SimpleImputer doesn't handle mixed type DataFrame as input",
+ )
+ @pytest.mark.sklearn()
+ def test_initialize_model_from_run_nonstrict(self):
+ # We cannot guarantee that a run with an older version exists on the server.
+ # Thus, we test it simply with a run that we know exists that might not be loose.
+ # This tests all lines of code for OpenML but not the initialization, which we do not want to guarantee anyhow.
+ _ = openml.runs.initialize_model_from_run(run_id=1, strict_version=False)
+
+
+@mock.patch.object(requests.Session, "delete")
+def test_delete_run_not_owned(mock_delete, test_files_directory, test_api_key):
+ content_file = test_files_directory / "mock_responses" / "runs" / "run_delete_not_owned.xml"
+ mock_delete.return_value = create_request_response(
+ status_code=412,
+ content_filepath=content_file,
+ )
+
+ with pytest.raises(
+ OpenMLNotAuthorizedError,
+ match="The run can not be deleted because it was not uploaded by you.",
+ ):
+ openml.runs.delete_run(40_000)
+
+ run_url = f"{openml.config.TEST_SERVER_URL}/api/v1/xml/run/40000"
+ assert run_url == mock_delete.call_args.args[0]
+ assert test_api_key == mock_delete.call_args.kwargs.get("params", {}).get("api_key")
+
+
+@mock.patch.object(requests.Session, "delete")
+def test_delete_run_success(mock_delete, test_files_directory, test_api_key):
+ content_file = test_files_directory / "mock_responses" / "runs" / "run_delete_successful.xml"
+ mock_delete.return_value = create_request_response(
+ status_code=200,
+ content_filepath=content_file,
+ )
+
+ success = openml.runs.delete_run(10591880)
+ assert success
+
+ run_url = f"{openml.config.TEST_SERVER_URL}/api/v1/xml/run/10591880"
+ assert run_url == mock_delete.call_args.args[0]
+ assert test_api_key == mock_delete.call_args.kwargs.get("params", {}).get("api_key")
+
+
+@mock.patch.object(requests.Session, "delete")
+def test_delete_unknown_run(mock_delete, test_files_directory, test_api_key):
+ content_file = test_files_directory / "mock_responses" / "runs" / "run_delete_not_exist.xml"
+ mock_delete.return_value = create_request_response(
+ status_code=412,
+ content_filepath=content_file,
+ )
+
+ with pytest.raises(
+ OpenMLServerException,
+ match="Run does not exist",
+ ):
+ openml.runs.delete_run(9_999_999)
+
+ run_url = f"{openml.config.TEST_SERVER_URL}/api/v1/xml/run/9999999"
+ assert run_url == mock_delete.call_args.args[0]
+ assert test_api_key == mock_delete.call_args.kwargs.get("params", {}).get("api_key")
+
+
+@pytest.mark.sklearn()
+@unittest.skipIf(
+ Version(sklearn.__version__) < Version("0.21"),
+ reason="couldn't perform local tests successfully w/o bloating RAM",
+ )
+@unittest.skipIf(
+ Version(sklearn.__version__) >= Version("1.8"),
+ reason="predictions differ significantly",
+ )
+@mock.patch("openml_sklearn.SklearnExtension._prevent_optimize_n_jobs")
+@pytest.mark.test_server()
+def test__run_task_get_arffcontent_2(parallel_mock):
+ """Tests if a run executed in parallel is collated correctly."""
+ task = openml.tasks.get_task(7) # Supervised Classification on kr-vs-kp
+ x, y = task.get_X_and_y()
+ num_instances = x.shape[0]
+ line_length = 6 + len(task.class_labels)
+ loss = "log" if Version(sklearn.__version__) < Version("1.3") else "log_loss"
+ clf = sklearn.pipeline.Pipeline(
+ [
+ (
+ "cat_handling",
+ ColumnTransformer(
+ transformers=[
+ (
+ "cat",
+ OneHotEncoder(handle_unknown="ignore"),
+ x.select_dtypes(include=["object", "category"]).columns,
+ )
+ ],
+ remainder="passthrough",
+ ),
+ ),
+ ("clf", SGDClassifier(loss=loss, random_state=1)),
+ ]
+ )
+ n_jobs = 2
+ backend = "loky" if Version(joblib.__version__) > Version("0.11") else "multiprocessing"
+ from openml_sklearn import SklearnExtension
+ extension = SklearnExtension()
+ with parallel_backend(backend, n_jobs=n_jobs):
+ res = openml.runs.functions._run_task_get_arffcontent(
+ extension=extension,
+ model=clf,
+ task=task,
+ add_local_measures=True,
+ n_jobs=n_jobs,
+ )
+ # This unit test will fail if joblib is unable to distribute successfully since the
+ # function _run_model_on_fold is being mocked out. However, for a new spawned worker, it
+ # is not and the mock call_count should remain 0 while the subsequent check of actual
+ # results should also hold, only on successful distribution of tasks to workers.
+ # The _prevent_optimize_n_jobs() is a function executed within the _run_model_on_fold()
+ # block and mocking this function doesn't affect rest of the pipeline, but is adequately
+ # indicative if _run_model_on_fold() is being called or not.
+ assert parallel_mock.call_count == 0
+ assert isinstance(res[0], list)
+ assert len(res[0]) == num_instances
+ assert len(res[0][0]) == line_length
+ assert len(res[2]) == 7
+ assert len(res[3]) == 7
+ expected_scores = [
+ 0.9625,
+ 0.953125,
+ 0.965625,
+ 0.9125,
+ 0.98125,
+ 0.975,
+ 0.9247648902821317,
+ 0.9404388714733543,
+ 0.9780564263322884,
+ 0.9623824451410659,
+ ]
+ scores = [v for k, v in res[2]["predictive_accuracy"][0].items()]
+ np.testing.assert_array_almost_equal(
+ scores,
+ expected_scores,
+ decimal=2,
+ err_msg="Observed performance scores deviate from expected ones.",
+ )
+
+
+@pytest.mark.sklearn()
+@unittest.skipIf(
+ Version(sklearn.__version__) < Version("0.21"),
+ reason="couldn't perform local tests successfully w/o bloating RAM",
+ )
+@mock.patch("openml_sklearn.SklearnExtension._prevent_optimize_n_jobs")
+@pytest.mark.parametrize(
+ ("n_jobs", "backend", "call_count"),
+ [
+ # `None` picks the backend based on joblib version (loky or multiprocessing) and
+ # spawns multiple processes if n_jobs != 1, which means the mock is not applied.
+ (2, None, 0),
+ (-1, None, 0),
+ (1, None, 10), # with n_jobs=1 the mock *is* applied, since there is no new subprocess
+ (1, "sequential", 10),
+ (1, "threading", 10),
+ (-1, "threading", 10), # the threading backend does preserve mocks even with parallelizing
+ ]
+)
+@pytest.mark.test_server()
+def test_joblib_backends(parallel_mock, n_jobs, backend, call_count):
+ """Tests evaluation of a run using various joblib backends and n_jobs."""
+ if backend is None:
+ backend = (
+ "loky" if Version(joblib.__version__) > Version("0.11") else "multiprocessing"
+ )
+
+ task = openml.tasks.get_task(7) # Supervised Classification on kr-vs-kp
+ x, y = task.get_X_and_y()
+ num_instances = x.shape[0]
+ line_length = 6 + len(task.class_labels)
+
+ clf = sklearn.model_selection.RandomizedSearchCV(
+ estimator=sklearn.pipeline.Pipeline(
+ [
+ (
+ "cat_handling",
+ ColumnTransformer(
+ transformers=[
+ (
+ "cat",
+ OneHotEncoder(handle_unknown="ignore"),
+ x.select_dtypes(include=["object", "category"]).columns,
+ )
+ ],
+ remainder="passthrough",
+ ),
+ ),
+ ("clf", sklearn.ensemble.RandomForestClassifier(n_estimators=5)),
+ ]
+ ),
+ param_distributions={
+ "clf__max_depth": [3, None],
+ "clf__max_features": [1, 2, 3, 4],
+ "clf__min_samples_split": [2, 3, 4, 5, 6, 7, 8, 9, 10],
+ "clf__min_samples_leaf": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
+ "clf__bootstrap": [True, False],
+ "clf__criterion": ["gini", "entropy"],
+ },
+ random_state=1,
+ cv=sklearn.model_selection.StratifiedKFold(
+ n_splits=2,
+ shuffle=True,
+ random_state=1,
+ ),
+ n_iter=5,
+ n_jobs=n_jobs,
+ )
+ from openml_sklearn import SklearnExtension
+ extension = SklearnExtension()
+ with parallel_backend(backend, n_jobs=n_jobs):
+ res = openml.runs.functions._run_task_get_arffcontent(
+ extension=extension,
+ model=clf,
+ task=task,
+ add_local_measures=True,
+ n_jobs=n_jobs,
+ )
+ assert type(res[0]) == list
+ assert len(res[0]) == num_instances
+ assert len(res[0][0]) == line_length
+ # usercpu_time_millis_* not recorded when n_jobs > 1
+ # *_time_millis_* not recorded when n_jobs = -1
+ assert len(res[2]["predictive_accuracy"][0]) == 10
+ assert len(res[3]["predictive_accuracy"][0]) == 10
+ assert parallel_mock.call_count == call_count
diff --git a/tests/test_runs/test_trace.py b/tests/test_runs/test_trace.py
new file mode 100644
index 000000000..bdf9de42d
--- /dev/null
+++ b/tests/test_runs/test_trace.py
@@ -0,0 +1,78 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+import pytest
+
+from openml.runs import OpenMLRunTrace, OpenMLTraceIteration
+from openml.testing import TestBase
+
+
+class TestTrace(TestBase):
+ def test_get_selected_iteration(self):
+ trace_iterations = {}
+ for i in range(5):
+ for j in range(5):
+ for k in range(5):
+ t = OpenMLTraceIteration(
+ repeat=i,
+ fold=j,
+ iteration=5,
+ setup_string="parameter_%d%d%d" % (i, j, k),
+ evaluation=1.0 * i + 0.1 * j + 0.01 * k,
+ selected=(i == j and i == k and i == 2),
+ parameters=None,
+ )
+ trace_iterations[(i, j, k)] = t
+
+ trace = OpenMLRunTrace(-1, trace_iterations=trace_iterations)
+ # This next one should simply not fail
+ assert trace.get_selected_iteration(2, 2) == 2
+ with pytest.raises(
+ ValueError, match="Could not find the selected iteration for rep/fold 3/3"
+ ):
+ trace.get_selected_iteration(3, 3)
+
+ def test_initialization(self):
+ """Check all different ways to fail the initialization"""
+ with pytest.raises(ValueError, match="Trace content not available."):
+ OpenMLRunTrace.generate(attributes="foo", content=None)
+ with pytest.raises(ValueError, match="Trace attributes not available."):
+ OpenMLRunTrace.generate(attributes=None, content="foo")
+ with pytest.raises(ValueError, match="Trace content is empty."):
+ OpenMLRunTrace.generate(attributes="foo", content=[])
+ with pytest.raises(ValueError, match="Trace_attributes and trace_content not compatible:"):
+ OpenMLRunTrace.generate(attributes=["abc"], content=[[1, 2]])
+
+ def test_duplicate_name(self):
+ # Test that the user does not pass a parameter which has the same name
+ # as one of the required trace attributes
+ trace_attributes = [
+ ("repeat", "NUMERICAL"),
+ ("fold", "NUMERICAL"),
+ ("iteration", "NUMERICAL"),
+ ("evaluation", "NUMERICAL"),
+ ("selected", ["true", "false"]),
+ ("repeat", "NUMERICAL"),
+ ]
+ trace_content = [[0, 0, 0, 0.5, "true", 1], [0, 0, 0, 0.9, "false", 2]]
+ with pytest.raises(
+ ValueError,
+ match="Either `setup_string` or `parameters` needs to be passed as argument.",
+ ):
+ OpenMLRunTrace.generate(trace_attributes, trace_content)
+
+ trace_attributes = [
+ ("repeat", "NUMERICAL"),
+ ("fold", "NUMERICAL"),
+ ("iteration", "NUMERICAL"),
+ ("evaluation", "NUMERICAL"),
+ ("selected", ["true", "false"]),
+ ("sunshine", "NUMERICAL"),
+ ]
+ trace_content = [[0, 0, 0, 0.5, "true", 1], [0, 0, 0, 0.9, "false", 2]]
+ with pytest.raises(
+ ValueError,
+ match="Encountered unknown attribute sunshine that does not start with "
+ "prefix parameter_",
+ ):
+ OpenMLRunTrace.generate(trace_attributes, trace_content)
diff --git a/tests/test_setups/__init__.py b/tests/test_setups/__init__.py
new file mode 100644
index 000000000..245c252db
--- /dev/null
+++ b/tests/test_setups/__init__.py
@@ -0,0 +1,5 @@
+# License: BSD 3-Clause
+
+# Dummy to allow mock classes in the test files to have a version number for
+# their parent module
+__version__ = "0.1"
diff --git a/tests/test_setups/test_setup_functions.py b/tests/test_setups/test_setup_functions.py
new file mode 100644
index 000000000..0df3a0b3b
--- /dev/null
+++ b/tests/test_setups/test_setup_functions.py
@@ -0,0 +1,191 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+import hashlib
+import time
+import unittest.mock
+
+import pandas as pd
+import pytest
+import sklearn.base
+import sklearn.naive_bayes
+import sklearn.tree
+from openml_sklearn import SklearnExtension
+
+import openml
+import openml.exceptions
+from openml.testing import TestBase
+
+
+def get_sentinel():
+ # Create a unique prefix for the flow. Necessary because the flow is
+ # identified by its name and external version online. Having a unique
+ # name allows us to publish the same flow in each test run
+ md5 = hashlib.md5()
+ md5.update(str(time.time()).encode("utf-8"))
+ sentinel = md5.hexdigest()[:10]
+ return f"TEST{sentinel}"
+
+
+class TestSetupFunctions(TestBase):
+ _multiprocess_can_split_ = True
+
+ def setUp(self):
+ self.extension = SklearnExtension()
+ super().setUp()
+
+ @pytest.mark.sklearn()
+ @pytest.mark.test_server()
+ def test_nonexisting_setup_exists(self):
+ # first publish a non-existing flow
+ sentinel = get_sentinel()
+ # because of the sentinel, we can not use flows that contain subflows
+ dectree = sklearn.tree.DecisionTreeClassifier()
+ flow = self.extension.model_to_flow(dectree)
+ flow.name = f"TEST{sentinel}{flow.name}"
+ flow.publish()
+ TestBase._mark_entity_for_removal("flow", flow.flow_id, flow.name)
+ TestBase.logger.info(f"collected from {__file__.split('/')[-1]}: {flow.flow_id}")
+
+ # although the flow exists (created as of previous statement),
+ # we can be sure there are no setups (yet) as it was just created
+ # and hasn't been ran
+ setup_id = openml.setups.setup_exists(flow)
+ assert not setup_id
+
+ def _existing_setup_exists(self, classif):
+ flow = self.extension.model_to_flow(classif)
+ flow.name = f"TEST{get_sentinel()}{flow.name}"
+ flow.publish()
+ TestBase._mark_entity_for_removal("flow", flow.flow_id, flow.name)
+ TestBase.logger.info(f"collected from {__file__.split('/')[-1]}: {flow.flow_id}")
+
+ # although the flow exists, we can be sure there are no
+ # setups (yet) as it hasn't been ran
+ setup_id = openml.setups.setup_exists(flow)
+ assert not setup_id
+ setup_id = openml.setups.setup_exists(flow)
+ assert not setup_id
+
+ # now run the flow on an easy task:
+ task = openml.tasks.get_task(115) # diabetes; crossvalidation
+ run = openml.runs.run_flow_on_task(flow, task)
+ # spoof flow id, otherwise the sentinel is ignored
+ run.flow_id = flow.flow_id
+ run.publish()
+ TestBase._mark_entity_for_removal("run", run.run_id)
+ TestBase.logger.info(f"collected from {__file__.split('/')[-1]}: {run.run_id}")
+ # download the run, as it contains the right setup id
+ run = openml.runs.get_run(run.run_id)
+
+ # execute the function we are interested in
+ setup_id = openml.setups.setup_exists(flow)
+ assert setup_id == run.setup_id
+
+ @pytest.mark.sklearn()
+ @pytest.mark.test_server()
+ def test_existing_setup_exists_1(self):
+ def side_effect(self):
+ self.var_smoothing = 1e-9
+ self.priors = None
+
+ with unittest.mock.patch.object(
+ sklearn.naive_bayes.GaussianNB,
+ "__init__",
+ side_effect,
+ ):
+ # Check a flow with zero hyperparameters
+ nb = sklearn.naive_bayes.GaussianNB()
+ self._existing_setup_exists(nb)
+
+ @pytest.mark.sklearn()
+ @pytest.mark.test_server()
+ def test_exisiting_setup_exists_2(self):
+ # Check a flow with one hyperparameter
+ self._existing_setup_exists(sklearn.naive_bayes.GaussianNB())
+
+ @pytest.mark.sklearn()
+ @pytest.mark.test_server()
+ def test_existing_setup_exists_3(self):
+ # Check a flow with many hyperparameters
+ self._existing_setup_exists(
+ sklearn.tree.DecisionTreeClassifier(
+ max_depth=5,
+ min_samples_split=3,
+ # Not setting the random state will make this flow fail as running it
+ # will add a random random_state.
+ random_state=1,
+ ),
+ )
+
+ @pytest.mark.production_server()
+ def test_get_setup(self):
+ self.use_production_server()
+ # no setups in default test server
+ # contains all special cases, 0 params, 1 param, n params.
+ # Non scikitlearn flows.
+ setups = [18, 19, 20, 118]
+ num_params = [8, 0, 3, 1]
+
+ for idx in range(len(setups)):
+ current = openml.setups.get_setup(setups[idx])
+ assert current.flow_id > 0
+ if num_params[idx] == 0:
+ assert current.parameters is None
+ else:
+ assert len(current.parameters) == num_params[idx]
+
+ @pytest.mark.production_server()
+ def test_setup_list_filter_flow(self):
+ self.use_production_server()
+
+ flow_id = 5873
+
+ setups = openml.setups.list_setups(flow=flow_id)
+
+ assert len(setups) > 0 # TODO: please adjust 0
+ for setup_id in setups:
+ assert setups[setup_id].flow_id == flow_id
+
+ @pytest.mark.test_server()
+ def test_list_setups_empty(self):
+ setups = openml.setups.list_setups(setup=[0])
+ if len(setups) > 0:
+ raise ValueError("UnitTest Outdated, got somehow results")
+
+ assert isinstance(setups, dict)
+
+ @pytest.mark.production_server()
+ def test_list_setups_output_format(self):
+ self.use_production_server()
+ flow_id = 6794
+ setups = openml.setups.list_setups(flow=flow_id, size=10)
+ assert isinstance(setups, dict)
+ assert isinstance(setups[next(iter(setups.keys()))], openml.setups.setup.OpenMLSetup)
+ assert len(setups) == 10
+
+ setups = openml.setups.list_setups(flow=flow_id, size=10, output_format="dataframe")
+ assert isinstance(setups, pd.DataFrame)
+ assert len(setups) == 10
+
+ @pytest.mark.test_server()
+ def test_setuplist_offset(self):
+ size = 10
+ setups = openml.setups.list_setups(offset=0, size=size)
+ assert len(setups) == size
+ setups2 = openml.setups.list_setups(offset=size, size=size)
+ assert len(setups2) == size
+
+ all = set(setups.keys()).union(setups2.keys())
+
+ assert len(all) == size * 2
+
+ @pytest.mark.test_server()
+ def test_get_cached_setup(self):
+ openml.config.set_root_cache_directory(self.static_cache_dir)
+ openml.setups.functions._get_cached_setup(1)
+
+ def test_get_uncached_setup(self):
+ openml.config.set_root_cache_directory(self.static_cache_dir)
+ with pytest.raises(openml.exceptions.OpenMLCacheException):
+ openml.setups.functions._get_cached_setup(10)
diff --git a/tests/test_study/__init__.py b/tests/test_study/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/test_study/test_study_functions.py b/tests/test_study/test_study_functions.py
new file mode 100644
index 000000000..2a2d276ec
--- /dev/null
+++ b/tests/test_study/test_study_functions.py
@@ -0,0 +1,264 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+import pytest
+import unittest
+
+import openml
+import openml.study
+from openml.testing import TestBase
+
+
+class TestStudyFunctions(TestBase):
+ _multiprocess_can_split_ = True
+
+ @pytest.mark.production_server()
+ @pytest.mark.xfail(reason="failures_issue_1544", strict=False)
+ def test_get_study_old(self):
+ self.use_production_server()
+
+ study = openml.study.get_study(34)
+ assert len(study.data) == 105
+ assert len(study.tasks) == 105
+ assert len(study.flows) == 27
+ assert len(study.setups) == 30
+ assert study.runs is None
+
+ @pytest.mark.production_server()
+ def test_get_study_new(self):
+ self.use_production_server()
+
+ study = openml.study.get_study(123)
+ assert len(study.data) == 299
+ assert len(study.tasks) == 299
+ assert len(study.flows) == 5
+ assert len(study.setups) == 1253
+ assert len(study.runs) == 1693
+
+ @pytest.mark.production_server()
+ def test_get_openml100(self):
+ self.use_production_server()
+
+ study = openml.study.get_study("OpenML100", "tasks")
+ assert isinstance(study, openml.study.OpenMLBenchmarkSuite)
+ study_2 = openml.study.get_suite("OpenML100")
+ assert isinstance(study_2, openml.study.OpenMLBenchmarkSuite)
+ assert study.study_id == study_2.study_id
+
+ @pytest.mark.production_server()
+ def test_get_study_error(self):
+ self.use_production_server()
+
+ with pytest.raises(
+ ValueError, match="Unexpected entity type 'task' reported by the server, expected 'run'"
+ ):
+ openml.study.get_study(99)
+
+ @pytest.mark.production_server()
+ def test_get_suite(self):
+ self.use_production_server()
+
+ study = openml.study.get_suite(99)
+ assert len(study.data) == 72
+ assert len(study.tasks) == 72
+ assert study.flows is None
+ assert study.runs is None
+ assert study.setups is None
+
+ @pytest.mark.production_server()
+ def test_get_suite_error(self):
+ self.use_production_server()
+
+ with pytest.raises(
+ ValueError, match="Unexpected entity type 'run' reported by the server, expected 'task'"
+ ):
+ openml.study.get_suite(123)
+
+ @pytest.mark.test_server()
+ def test_publish_benchmark_suite(self):
+ fixture_alias = None
+ fixture_name = "unit tested benchmark suite"
+ fixture_descr = "bla"
+ fixture_task_ids = [1, 2, 3]
+
+ study = openml.study.create_benchmark_suite(
+ alias=fixture_alias,
+ name=fixture_name,
+ description=fixture_descr,
+ task_ids=fixture_task_ids,
+ )
+ study.publish()
+ TestBase._mark_entity_for_removal("study", study.id)
+ TestBase.logger.info(f"collected from {__file__.split('/')[-1]}: {study.id}")
+
+ assert study.id > 0
+
+ # verify main meta data
+ study_downloaded = openml.study.get_suite(study.id)
+ assert study_downloaded.alias == fixture_alias
+ assert study_downloaded.name == fixture_name
+ assert study_downloaded.description == fixture_descr
+ assert study_downloaded.main_entity_type == "task"
+ # verify resources
+ assert study_downloaded.flows is None
+ assert study_downloaded.setups is None
+ assert study_downloaded.runs is None
+ assert len(study_downloaded.data) > 0
+ assert len(study_downloaded.data) <= len(fixture_task_ids)
+ self.assertSetEqual(set(study_downloaded.tasks), set(fixture_task_ids))
+
+ # attach more tasks
+ tasks_additional = [4, 5, 6]
+ openml.study.attach_to_study(study.id, tasks_additional)
+ study_downloaded = openml.study.get_suite(study.id)
+ # verify again
+ self.assertSetEqual(set(study_downloaded.tasks), set(fixture_task_ids + tasks_additional))
+ # test detach function
+ openml.study.detach_from_study(study.id, fixture_task_ids)
+ study_downloaded = openml.study.get_suite(study.id)
+ self.assertSetEqual(set(study_downloaded.tasks), set(tasks_additional))
+
+ # test status update function
+ openml.study.update_suite_status(study.id, "deactivated")
+ study_downloaded = openml.study.get_suite(study.id)
+ assert study_downloaded.status == "deactivated"
+ # can't delete study, now it's not longer in preparation
+
+ def _test_publish_empty_study_is_allowed(self, explicit: bool):
+ runs: list[int] | None = [] if explicit else None
+ kind = "explicit" if explicit else "implicit"
+
+ study = openml.study.create_study(
+ name=f"empty-study-{kind}",
+ description=f"a study with no runs attached {kind}ly",
+ run_ids=runs,
+ )
+
+ study.publish()
+ TestBase._mark_entity_for_removal("study", study.id)
+ TestBase.logger.info(f"collected from {__file__.split('/')[-1]}: {study.id}")
+
+ assert study.id > 0
+ study_downloaded = openml.study.get_study(study.id)
+ assert study_downloaded.main_entity_type == "run"
+ assert study_downloaded.runs is None
+
+ @pytest.mark.test_server()
+ def test_publish_empty_study_explicit(self):
+ self._test_publish_empty_study_is_allowed(explicit=True)
+
+ @pytest.mark.test_server()
+ def test_publish_empty_study_implicit(self):
+ self._test_publish_empty_study_is_allowed(explicit=False)
+
+ @pytest.mark.flaky()
+ @pytest.mark.test_server()
+ def test_publish_study(self):
+ # get some random runs to attach
+ run_list = openml.evaluations.list_evaluations("predictive_accuracy", size=10)
+ assert len(run_list) == 10
+
+ fixt_alias = None
+ fixt_name = "unit tested study"
+ fixt_descr = "bla"
+ fixt_flow_ids = {evaluation.flow_id for evaluation in run_list.values()}
+ fixt_task_ids = {evaluation.task_id for evaluation in run_list.values()}
+ fixt_setup_ids = {evaluation.setup_id for evaluation in run_list.values()}
+
+ study = openml.study.create_study(
+ alias=fixt_alias,
+ benchmark_suite=None,
+ name=fixt_name,
+ description=fixt_descr,
+ run_ids=list(run_list.keys()),
+ )
+ study.publish()
+ TestBase._mark_entity_for_removal("study", study.id)
+ TestBase.logger.info(f"collected from {__file__.split('/')[-1]}: {study.id}")
+ assert study.id > 0
+ study_downloaded = openml.study.get_study(study.id)
+ assert study_downloaded.alias == fixt_alias
+ assert study_downloaded.name == fixt_name
+ assert study_downloaded.description == fixt_descr
+ assert study_downloaded.main_entity_type == "run"
+
+ self.assertSetEqual(set(study_downloaded.runs), set(run_list.keys()))
+ self.assertSetEqual(set(study_downloaded.setups), set(fixt_setup_ids))
+ self.assertSetEqual(set(study_downloaded.flows), set(fixt_flow_ids))
+ self.assertSetEqual(set(study_downloaded.tasks), set(fixt_task_ids))
+
+ # test whether the list run function also handles study data fine
+ run_ids = openml.runs.list_runs(study=study.id) # returns DF
+ self.assertSetEqual(set(run_ids["run_id"]), set(study_downloaded.runs))
+
+ # test whether the list evaluation function also handles study data fine
+ run_ids = openml.evaluations.list_evaluations( # returns list of objects
+ "predictive_accuracy",
+ size=None,
+ study=study.id,
+ output_format="object", # making the default explicit
+ )
+ self.assertSetEqual(set(run_ids), set(study_downloaded.runs))
+
+ # attach more runs, since we fetch 11 here, at least one is non-overlapping
+ run_list_additional = openml.runs.list_runs(size=11, offset=10)
+ run_list_additional = set(run_list_additional["run_id"]) - set(run_ids)
+ openml.study.attach_to_study(study.id, list(run_list_additional))
+ study_downloaded = openml.study.get_study(study.id)
+ # verify again
+ all_run_ids = run_list_additional | set(run_list.keys())
+ self.assertSetEqual(set(study_downloaded.runs), all_run_ids)
+
+ # test detach function
+ openml.study.detach_from_study(study.id, list(run_list.keys()))
+ study_downloaded = openml.study.get_study(study.id)
+ self.assertSetEqual(set(study_downloaded.runs), run_list_additional)
+
+ # test status update function
+ openml.study.update_study_status(study.id, "deactivated")
+ study_downloaded = openml.study.get_study(study.id)
+ assert study_downloaded.status == "deactivated"
+
+ res = openml.study.delete_study(study.id)
+ assert res
+
+ @pytest.mark.test_server()
+ def test_study_attach_illegal(self):
+ run_list = openml.runs.list_runs(size=10)
+ assert len(run_list) == 10
+ run_list_more = openml.runs.list_runs(size=20)
+ assert len(run_list_more) == 20
+
+ study = openml.study.create_study(
+ alias=None,
+ benchmark_suite=None,
+ name="study with illegal runs",
+ description="none",
+ run_ids=list(run_list["run_id"]),
+ )
+ study.publish()
+ TestBase._mark_entity_for_removal("study", study.id)
+ TestBase.logger.info(f"collected from {__file__.split('/')[-1]}: {study.id}")
+ study_original = openml.study.get_study(study.id)
+
+ with pytest.raises(
+ openml.exceptions.OpenMLServerException,
+ match="Problem attaching entities.",
+ ):
+ # run id does not exists
+ openml.study.attach_to_study(study.id, [0])
+
+ with pytest.raises(
+ openml.exceptions.OpenMLServerException,
+ match="Problem attaching entities.",
+ ):
+ # some runs already attached
+ openml.study.attach_to_study(study.id, list(run_list_more["run_id"]))
+ study_downloaded = openml.study.get_study(study.id)
+ self.assertListEqual(study_original.runs, study_downloaded.runs)
+
+ @unittest.skip("It is unclear when we can expect the test to pass or fail.")
+ def test_study_list(self):
+ study_list = openml.study.list_studies(status="in_preparation")
+ # might fail if server is recently reset
+ assert len(study_list) >= 2
diff --git a/tests/test_tasks/__init__.py b/tests/test_tasks/__init__.py
new file mode 100644
index 000000000..26488a8cc
--- /dev/null
+++ b/tests/test_tasks/__init__.py
@@ -0,0 +1,9 @@
+# License: BSD 3-Clause
+
+from .test_supervised_task import OpenMLSupervisedTaskTest
+from .test_task import OpenMLTaskTest
+
+__all__ = [
+ "OpenMLTaskTest",
+ "OpenMLSupervisedTaskTest",
+]
diff --git a/tests/test_tasks/test_classification_task.py b/tests/test_tasks/test_classification_task.py
new file mode 100644
index 000000000..65dcebc1d
--- /dev/null
+++ b/tests/test_tasks/test_classification_task.py
@@ -0,0 +1,43 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+import pandas as pd
+import pytest
+
+from openml.tasks import TaskType, get_task
+
+from .test_supervised_task import OpenMLSupervisedTaskTest
+
+
+class OpenMLClassificationTaskTest(OpenMLSupervisedTaskTest):
+ __test__ = True
+
+ def setUp(self, n_levels: int = 1):
+ super().setUp()
+ self.task_id = 119 # diabetes
+ self.task_type = TaskType.SUPERVISED_CLASSIFICATION
+ self.estimation_procedure = 5
+
+ @pytest.mark.test_server()
+ def test_download_task(self):
+ task = super().test_download_task()
+ assert task.task_id == self.task_id
+ assert task.task_type_id == TaskType.SUPERVISED_CLASSIFICATION
+ assert task.dataset_id == 20
+ assert task.estimation_procedure_id == self.estimation_procedure
+
+ @pytest.mark.test_server()
+ def test_class_labels(self):
+ task = get_task(self.task_id)
+ assert task.class_labels == ["tested_negative", "tested_positive"]
+
+
+@pytest.mark.test_server()
+def test_get_X_and_Y():
+ task = get_task(119)
+ X, Y = task.get_X_and_y()
+ assert X.shape == (768, 8)
+ assert isinstance(X, pd.DataFrame)
+ assert Y.shape == (768,)
+ assert isinstance(Y, pd.Series)
+ assert pd.api.types.is_categorical_dtype(Y)
diff --git a/tests/test_tasks/test_clustering_task.py b/tests/test_tasks/test_clustering_task.py
new file mode 100644
index 000000000..29f5663c4
--- /dev/null
+++ b/tests/test_tasks/test_clustering_task.py
@@ -0,0 +1,70 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+import pytest
+
+import openml
+from openml.exceptions import OpenMLServerException
+from openml.tasks import TaskType
+from openml.testing import TestBase
+
+from .test_task import OpenMLTaskTest
+
+
+class OpenMLClusteringTaskTest(OpenMLTaskTest):
+ __test__ = True
+
+ def setUp(self, n_levels: int = 1):
+ super().setUp()
+ self.task_id = 146714
+ self.task_type = TaskType.CLUSTERING
+ self.estimation_procedure = 17
+
+ @pytest.mark.production_server()
+ def test_get_dataset(self):
+ # no clustering tasks on test server
+ self.use_production_server()
+ task = openml.tasks.get_task(self.task_id)
+ task.get_dataset()
+
+ @pytest.mark.production_server()
+ @pytest.mark.test_server()
+ def test_download_task(self):
+ # no clustering tasks on test server
+ self.use_production_server()
+ task = super().test_download_task()
+ assert task.task_id == self.task_id
+ assert task.task_type_id == TaskType.CLUSTERING
+ assert task.dataset_id == 36
+
+ @pytest.mark.test_server()
+ def test_upload_task(self):
+ compatible_datasets = self._get_compatible_rand_dataset()
+ for i in range(100):
+ try:
+ dataset_id = compatible_datasets[i % len(compatible_datasets)]
+ # Upload a clustering task without a ground truth.
+ task = openml.tasks.create_task(
+ task_type=self.task_type,
+ dataset_id=dataset_id,
+ estimation_procedure_id=self.estimation_procedure,
+ )
+ task = task.publish()
+ TestBase._mark_entity_for_removal("task", task.id)
+ TestBase.logger.info(
+ f"collected from {__file__.split('/')[-1]}: {task.id}",
+ )
+ # success
+ break
+ except OpenMLServerException as e:
+ # Error code for 'task already exists'
+ # Should be 533 according to the docs
+ # (# https://www.openml.org/api_docs#!/task/post_task)
+ if e.code == 614:
+ continue
+ else:
+ raise e
+ else:
+ raise ValueError(
+ f"Could not create a valid task for task type ID {self.task_type}",
+ )
diff --git a/tests/test_tasks/test_learning_curve_task.py b/tests/test_tasks/test_learning_curve_task.py
new file mode 100644
index 000000000..465d9c0be
--- /dev/null
+++ b/tests/test_tasks/test_learning_curve_task.py
@@ -0,0 +1,40 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+import pandas as pd
+import pytest
+
+from openml.tasks import TaskType, get_task
+
+from .test_supervised_task import OpenMLSupervisedTaskTest
+
+
+class OpenMLLearningCurveTaskTest(OpenMLSupervisedTaskTest):
+ __test__ = True
+
+ def setUp(self, n_levels: int = 1):
+ super().setUp()
+ self.task_id = 801 # diabetes
+ self.task_type = TaskType.LEARNING_CURVE
+ self.estimation_procedure = 13
+
+ @pytest.mark.test_server()
+ def test_get_X_and_Y(self):
+ X, Y = super().test_get_X_and_Y()
+ assert X.shape == (768, 8)
+ assert isinstance(X, pd.DataFrame)
+ assert Y.shape == (768,)
+ assert isinstance(Y, pd.Series)
+ assert pd.api.types.is_categorical_dtype(Y)
+
+ @pytest.mark.test_server()
+ def test_download_task(self):
+ task = super().test_download_task()
+ assert task.task_id == self.task_id
+ assert task.task_type_id == TaskType.LEARNING_CURVE
+ assert task.dataset_id == 20
+
+ @pytest.mark.test_server()
+ def test_class_labels(self):
+ task = get_task(self.task_id)
+ assert task.class_labels == ["tested_negative", "tested_positive"]
diff --git a/tests/test_tasks/test_regression_task.py b/tests/test_tasks/test_regression_task.py
new file mode 100644
index 000000000..26d7dc94b
--- /dev/null
+++ b/tests/test_tasks/test_regression_task.py
@@ -0,0 +1,67 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+import ast
+
+import pandas as pd
+import pytest
+
+import openml
+from openml.exceptions import OpenMLServerException
+from openml.tasks import TaskType
+from openml.testing import TestBase, check_task_existence
+
+from .test_supervised_task import OpenMLSupervisedTaskTest
+
+
+class OpenMLRegressionTaskTest(OpenMLSupervisedTaskTest):
+ __test__ = True
+
+ def setUp(self, n_levels: int = 1):
+ super().setUp()
+ self.estimation_procedure = 9
+ task_meta_data = {
+ "task_type": TaskType.SUPERVISED_REGRESSION,
+ "dataset_id": 105, # wisconsin
+ "estimation_procedure_id": self.estimation_procedure, # non default value to test estimation procedure id
+ "target_name": "time",
+ }
+ _task_id = check_task_existence(**task_meta_data)
+ if _task_id is not None:
+ task_id = _task_id
+ else:
+ new_task = openml.tasks.create_task(**task_meta_data)
+ # publishes the new task
+ try:
+ new_task = new_task.publish()
+ task_id = new_task.task_id
+ # mark to remove the uploaded task
+ TestBase._mark_entity_for_removal("task", task_id)
+ TestBase.logger.info(f"collected from test_run_functions: {task_id}")
+ except OpenMLServerException as e:
+ if e.code == 614: # Task already exists
+ # the exception message contains the task_id that was matched in the format
+ # 'Task already exists. - matched id(s): [xxxx]'
+ task_id = ast.literal_eval(e.message.split("matched id(s):")[-1].strip())[0]
+ else:
+ raise Exception(repr(e))
+ self.task_id = task_id
+ self.task_type = TaskType.SUPERVISED_REGRESSION
+
+
+ @pytest.mark.test_server()
+ def test_get_X_and_Y(self):
+ X, Y = super().test_get_X_and_Y()
+ assert X.shape == (194, 32)
+ assert isinstance(X, pd.DataFrame)
+ assert Y.shape == (194,)
+ assert isinstance(Y, pd.Series)
+ assert pd.api.types.is_numeric_dtype(Y)
+
+ @pytest.mark.test_server()
+ def test_download_task(self):
+ task = super().test_download_task()
+ assert task.task_id == self.task_id
+ assert task.task_type_id == TaskType.SUPERVISED_REGRESSION
+ assert task.dataset_id == 105
+ assert task.estimation_procedure_id == self.estimation_procedure
diff --git a/tests/test_tasks/test_split.py b/tests/test_tasks/test_split.py
new file mode 100644
index 000000000..12cb632d9
--- /dev/null
+++ b/tests/test_tasks/test_split.py
@@ -0,0 +1,98 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+import inspect
+import os
+from pathlib import Path
+
+import numpy as np
+
+from openml import OpenMLSplit
+from openml.testing import TestBase
+
+
+class OpenMLSplitTest(TestBase):
+ # Splitting not helpful, these test's don't rely on the server and take less
+ # than 5 seconds + rebuilding the test would potentially be costly
+
+ def setUp(self):
+ __file__ = inspect.getfile(OpenMLSplitTest)
+ self.directory = os.path.dirname(__file__)
+ # This is for dataset
+ self.arff_filepath = (
+ Path(self.directory).parent
+ / "files"
+ / "org"
+ / "openml"
+ / "test"
+ / "tasks"
+ / "1882"
+ / "datasplits.arff"
+ )
+ self.pd_filename = self.arff_filepath.with_suffix(".pkl.py3")
+
+ def tearDown(self):
+ try:
+ os.remove(self.pd_filename)
+ except (OSError, FileNotFoundError):
+ # Replaced bare except. Not sure why these exceptions are acceptable.
+ pass
+
+ def test_eq(self):
+ split = OpenMLSplit._from_arff_file(self.arff_filepath)
+ assert split == split
+
+ split2 = OpenMLSplit._from_arff_file(self.arff_filepath)
+ split2.name = "a"
+ assert split != split2
+
+ split2 = OpenMLSplit._from_arff_file(self.arff_filepath)
+ split2.description = "a"
+ assert split != split2
+
+ split2 = OpenMLSplit._from_arff_file(self.arff_filepath)
+ split2.split[10] = {}
+ assert split != split2
+
+ split2 = OpenMLSplit._from_arff_file(self.arff_filepath)
+ split2.split[0][10] = {}
+ assert split != split2
+
+ def test_from_arff_file(self):
+ split = OpenMLSplit._from_arff_file(self.arff_filepath)
+ assert isinstance(split.split, dict)
+ assert isinstance(split.split[0], dict)
+ assert isinstance(split.split[0][0], dict)
+ assert isinstance(split.split[0][0][0][0], np.ndarray)
+ assert isinstance(split.split[0][0][0].train, np.ndarray)
+ assert isinstance(split.split[0][0][0].train, np.ndarray)
+ assert isinstance(split.split[0][0][0][1], np.ndarray)
+ assert isinstance(split.split[0][0][0].test, np.ndarray)
+ assert isinstance(split.split[0][0][0].test, np.ndarray)
+ for i in range(10):
+ for j in range(10):
+ assert split.split[i][j][0].train.shape[0] >= 808
+ assert split.split[i][j][0].test.shape[0] >= 89
+ assert (
+ split.split[i][j][0].train.shape[0] + split.split[i][j][0].test.shape[0] == 898
+ )
+
+ def test_get_split(self):
+ split = OpenMLSplit._from_arff_file(self.arff_filepath)
+ train_split, test_split = split.get(fold=5, repeat=2)
+ assert train_split.shape[0] == 808
+ assert test_split.shape[0] == 90
+ self.assertRaisesRegex(
+ ValueError,
+ "Repeat 10 not known",
+ split.get,
+ 10,
+ 2,
+ )
+ self.assertRaisesRegex(
+ ValueError,
+ "Fold 10 not known",
+ split.get,
+ 2,
+ 10,
+ )
diff --git a/tests/test_tasks/test_supervised_task.py b/tests/test_tasks/test_supervised_task.py
new file mode 100644
index 000000000..99df3cace
--- /dev/null
+++ b/tests/test_tasks/test_supervised_task.py
@@ -0,0 +1,35 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+import unittest
+
+import pandas as pd
+
+from openml.tasks import get_task
+import pytest
+
+from .test_task import OpenMLTaskTest
+
+
+class OpenMLSupervisedTaskTest(OpenMLTaskTest):
+ """
+ A helper class. The methods of the test case
+ are only executed in subclasses of the test case.
+ """
+
+ __test__ = False
+
+ @classmethod
+ def setUpClass(cls):
+ if cls is OpenMLSupervisedTaskTest:
+ raise unittest.SkipTest("Skip OpenMLSupervisedTaskTest tests," " it's a base class")
+ super().setUpClass()
+
+ def setUp(self, n_levels: int = 1):
+ super().setUp()
+
+ @pytest.mark.test_server()
+ def test_get_X_and_Y(self) -> tuple[pd.DataFrame, pd.Series]:
+ task = get_task(self.task_id)
+ X, Y = task.get_X_and_y()
+ return X, Y
diff --git a/tests/test_tasks/test_task.py b/tests/test_tasks/test_task.py
new file mode 100644
index 000000000..1d0df1210
--- /dev/null
+++ b/tests/test_tasks/test_task.py
@@ -0,0 +1,112 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+import unittest
+from random import randint, shuffle
+
+import pytest
+
+from openml.datasets import (
+ get_dataset,
+ list_datasets,
+)
+from openml.exceptions import OpenMLServerException
+from openml.tasks import TaskType, create_task, get_task
+from openml.testing import TestBase
+
+
+class OpenMLTaskTest(TestBase):
+ """
+ A helper class. The methods of the test case
+ are only executed in subclasses of the test case.
+ """
+
+ __test__ = False
+
+ @classmethod
+ def setUpClass(cls):
+ if cls is OpenMLTaskTest:
+ raise unittest.SkipTest("Skip OpenMLTaskTest tests," " it's a base class")
+ super().setUpClass()
+
+ def setUp(self, n_levels: int = 1):
+ super().setUp()
+
+ @pytest.mark.test_server()
+ def test_download_task(self):
+ return get_task(self.task_id)
+
+ @pytest.mark.test_server()
+ def test_upload_task(self):
+ # We don't know if the task in question already exists, so we try a few times. Checking
+ # beforehand would not be an option because a concurrent unit test could potentially
+ # create the same task and make this unit test fail (i.e. getting a dataset and creating
+ # a task for it is not atomic).
+ compatible_datasets = self._get_compatible_rand_dataset()
+ for i in range(100):
+ try:
+ dataset_id = compatible_datasets[i % len(compatible_datasets)]
+ # TODO consider implementing on the diff task types.
+ task = create_task(
+ task_type=self.task_type,
+ dataset_id=dataset_id,
+ target_name=self._get_random_feature(dataset_id),
+ estimation_procedure_id=self.estimation_procedure,
+ )
+
+ task.publish()
+ TestBase._mark_entity_for_removal("task", task.id)
+ TestBase.logger.info(
+ f"collected from {__file__.split('/')[-1]}: {task.id}",
+ )
+ # success
+ break
+ except OpenMLServerException as e:
+ # Error code for 'task already exists'
+ # Should be 533 according to the docs
+ # (# https://www.openml.org/api_docs#!/task/post_task)
+ if e.code == 614:
+ continue
+ else:
+ raise e
+ else:
+ raise ValueError(
+ f"Could not create a valid task for task type ID {self.task_type}",
+ )
+
+ def _get_compatible_rand_dataset(self) -> list:
+ active_datasets = list_datasets(status="active")
+
+ # depending on the task type, find either datasets
+ # with only symbolic features or datasets with only
+ # numerical features.
+ if self.task_type == TaskType.SUPERVISED_REGRESSION:
+ compatible_datasets = active_datasets[active_datasets["NumberOfSymbolicFeatures"] == 0]
+ elif self.task_type == TaskType.CLUSTERING:
+ compatible_datasets = active_datasets
+ else:
+ compatible_datasets = active_datasets[active_datasets["NumberOfNumericFeatures"] == 0]
+
+ compatible_datasets = list(compatible_datasets["did"])
+ # in-place shuffling
+ shuffle(compatible_datasets)
+ return compatible_datasets
+
+ # random_dataset_pos = randint(0, len(compatible_datasets) - 1)
+ #
+ # return compatible_datasets[random_dataset_pos]
+
+ def _get_random_feature(self, dataset_id: int) -> str:
+ random_dataset = get_dataset(dataset_id)
+ # necessary loop to overcome string and date type
+ # features.
+ while True:
+ random_feature_index = randint(0, len(random_dataset.features) - 1)
+ random_feature = random_dataset.features[random_feature_index]
+ if self.task_type == TaskType.SUPERVISED_REGRESSION:
+ if random_feature.data_type == "numeric":
+ break
+ else:
+ if random_feature.data_type == "nominal":
+ break
+ return random_feature.name
diff --git a/tests/test_tasks/test_task_functions.py b/tests/test_tasks/test_task_functions.py
new file mode 100644
index 000000000..df3c0a3b6
--- /dev/null
+++ b/tests/test_tasks/test_task_functions.py
@@ -0,0 +1,317 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+import os
+import unittest
+from typing import cast
+from unittest import mock
+
+import pandas as pd
+import pytest
+import requests
+
+import openml
+from openml import OpenMLSplit, OpenMLTask
+from openml.exceptions import OpenMLCacheException, OpenMLNotAuthorizedError, OpenMLServerException
+from openml.tasks import TaskType
+from openml.testing import TestBase, create_request_response
+
+
+class TestTask(TestBase):
+ _multiprocess_can_split_ = True
+
+ def setUp(self):
+ super().setUp()
+
+ def tearDown(self):
+ super().tearDown()
+
+ @pytest.mark.test_server()
+ def test__get_cached_tasks(self):
+ openml.config.set_root_cache_directory(self.static_cache_dir)
+ tasks = openml.tasks.functions._get_cached_tasks()
+ assert isinstance(tasks, dict)
+ assert len(tasks) == 3
+ assert isinstance(next(iter(tasks.values())), OpenMLTask)
+
+ @pytest.mark.test_server()
+ def test__get_cached_task(self):
+ openml.config.set_root_cache_directory(self.static_cache_dir)
+ task = openml.tasks.functions._get_cached_task(1)
+ assert isinstance(task, OpenMLTask)
+
+ def test__get_cached_task_not_cached(self):
+ openml.config.set_root_cache_directory(self.static_cache_dir)
+ self.assertRaisesRegex(
+ OpenMLCacheException,
+ "Task file for tid 2 not cached",
+ openml.tasks.functions._get_cached_task,
+ 2,
+ )
+
+ @pytest.mark.test_server()
+ def test__get_estimation_procedure_list(self):
+ estimation_procedures = openml.tasks.functions._get_estimation_procedure_list()
+ assert isinstance(estimation_procedures, list)
+ assert isinstance(estimation_procedures[0], dict)
+ assert estimation_procedures[0]["task_type_id"] == TaskType.SUPERVISED_CLASSIFICATION
+
+ @pytest.mark.production_server()
+ @pytest.mark.xfail(reason="failures_issue_1544", strict=False)
+ def test_list_clustering_task(self):
+ self.use_production_server()
+ # as shown by #383, clustering tasks can give list/dict casting problems
+ openml.tasks.list_tasks(task_type=TaskType.CLUSTERING, size=10)
+ # the expected outcome is that it doesn't crash. No assertions.
+
+ def _check_task(self, task):
+ assert type(task) == dict
+ assert len(task) >= 2
+ assert "did" in task
+ assert isinstance(task["did"], int)
+ assert "status" in task
+ assert isinstance(task["status"], str)
+ assert task["status"] in ["in_preparation", "active", "deactivated"]
+
+ @pytest.mark.test_server()
+ def test_list_tasks_by_type(self):
+ num_curves_tasks = 198 # number is flexible, check server if fails
+ ttid = TaskType.LEARNING_CURVE
+ tasks = openml.tasks.list_tasks(task_type=ttid)
+ assert len(tasks) >= num_curves_tasks
+ for task in tasks.to_dict(orient="index").values():
+ assert ttid == task["ttid"]
+ self._check_task(task)
+
+ @pytest.mark.test_server()
+ def test_list_tasks_length(self):
+ ttid = TaskType.LEARNING_CURVE
+ tasks = openml.tasks.list_tasks(task_type=ttid)
+ assert len(tasks) > 100
+
+ @pytest.mark.test_server()
+ def test_list_tasks_empty(self):
+ tasks = openml.tasks.list_tasks(tag="NoOneWillEverUseThisTag")
+ assert tasks.empty
+
+ @pytest.mark.test_server()
+ def test_list_tasks_by_tag(self):
+ # Server starts with 99 active tasks with the tag, and one 'in_preparation',
+ # so depending on the processing of the last dataset, there may be 99 or 100 matches.
+ num_basic_tasks = 99
+ tasks = openml.tasks.list_tasks(tag="OpenML100")
+ assert len(tasks) >= num_basic_tasks
+ for task in tasks.to_dict(orient="index").values():
+ self._check_task(task)
+
+ @pytest.mark.test_server()
+ def test_list_tasks(self):
+ tasks = openml.tasks.list_tasks()
+ assert len(tasks) >= 900
+ for task in tasks.to_dict(orient="index").values():
+ self._check_task(task)
+
+ @pytest.mark.test_server()
+ def test_list_tasks_paginate(self):
+ size = 10
+ max = 100
+ for i in range(0, max, size):
+ tasks = openml.tasks.list_tasks(offset=i, size=size)
+ assert size >= len(tasks)
+ for task in tasks.to_dict(orient="index").values():
+ self._check_task(task)
+
+ @pytest.mark.test_server()
+ def test_list_tasks_per_type_paginate(self):
+ size = 40
+ max = 100
+ task_types = [
+ TaskType.SUPERVISED_CLASSIFICATION,
+ TaskType.SUPERVISED_REGRESSION,
+ TaskType.LEARNING_CURVE,
+ ]
+ for j in task_types:
+ for i in range(0, max, size):
+ tasks = openml.tasks.list_tasks(task_type=j, offset=i, size=size)
+ assert size >= len(tasks)
+ for task in tasks.to_dict(orient="index").values():
+ assert j == task["ttid"]
+ self._check_task(task)
+
+ @pytest.mark.test_server()
+ def test__get_task(self):
+ openml.config.set_root_cache_directory(self.static_cache_dir)
+ openml.tasks.get_task(1882)
+
+ @unittest.skip(
+ "Please await outcome of discussion: https://github.com/openml/OpenML/issues/776",
+ )
+ @pytest.mark.production_server()
+ def test__get_task_live(self):
+ self.use_production_server()
+ # Test the following task as it used to throw an Unicode Error.
+ # https://github.com/openml/openml-python/issues/378
+ openml.tasks.get_task(34536)
+
+ @pytest.mark.test_server()
+ def test_get_task(self):
+ task = openml.tasks.get_task(1, download_data=True) # anneal; crossvalidation
+ assert isinstance(task, OpenMLTask)
+ assert os.path.exists(
+ os.path.join(openml.config.get_cache_directory(), "tasks", "1", "task.xml")
+ )
+ assert not os.path.exists(
+ os.path.join(openml.config.get_cache_directory(), "tasks", "1", "datasplits.arff")
+ )
+ assert os.path.exists(
+ os.path.join(openml.config.get_cache_directory(), "datasets", "1", "dataset_1.pq")
+ )
+
+ @pytest.mark.test_server()
+ def test_get_task_lazy(self):
+ task = openml.tasks.get_task(2, download_data=False) # anneal; crossvalidation
+ assert isinstance(task, OpenMLTask)
+ assert os.path.exists(
+ os.path.join(openml.config.get_cache_directory(), "tasks", "2", "task.xml")
+ )
+ assert task.class_labels == ["1", "2", "3", "4", "5", "U"]
+
+ assert not os.path.exists(
+ os.path.join(openml.config.get_cache_directory(), "tasks", "2", "datasplits.arff")
+ )
+ # Since the download_data=False is propagated to get_dataset
+ assert not os.path.exists(
+ os.path.join(openml.config.get_cache_directory(), "datasets", "2", "dataset.arff")
+ )
+
+ task.download_split()
+ assert os.path.exists(
+ os.path.join(openml.config.get_cache_directory(), "tasks", "2", "datasplits.arff")
+ )
+
+ @mock.patch("openml.tasks.functions.get_dataset")
+ @pytest.mark.test_server()
+ def test_removal_upon_download_failure(self, get_dataset):
+ class WeirdException(Exception):
+ pass
+
+ def assert_and_raise(*args, **kwargs):
+ # Make sure that the file was created!
+ assert os.path.join(os.getcwd(), "tasks", "1", "tasks.xml")
+ raise WeirdException()
+
+ get_dataset.side_effect = assert_and_raise
+ try:
+ openml.tasks.get_task(1) # anneal; crossvalidation
+ except WeirdException:
+ pass
+ # Now the file should no longer exist
+ assert not os.path.exists(os.path.join(os.getcwd(), "tasks", "1", "tasks.xml"))
+
+ @pytest.mark.test_server()
+ def test_get_task_with_cache(self):
+ openml.config.set_root_cache_directory(self.static_cache_dir)
+ task = openml.tasks.get_task(1)
+ assert isinstance(task, OpenMLTask)
+
+ @pytest.mark.production_server()
+ def test_get_task_different_types(self):
+ self.use_production_server()
+ # Regression task
+ openml.tasks.functions.get_task(5001)
+ # Learning curve
+ openml.tasks.functions.get_task(64)
+ # Issue 538, get_task failing with clustering task.
+ openml.tasks.functions.get_task(126033)
+
+ @pytest.mark.test_server()
+ def test_download_split(self):
+ task = openml.tasks.get_task(1) # anneal; crossvalidation
+ split = task.download_split()
+ assert type(split) == OpenMLSplit
+ assert os.path.exists(
+ os.path.join(openml.config.get_cache_directory(), "tasks", "1", "datasplits.arff")
+ )
+
+ def test_deletion_of_cache_dir(self):
+ # Simple removal
+ tid_cache_dir = openml.utils._create_cache_directory_for_id(
+ "tasks",
+ 1,
+ )
+ assert os.path.exists(tid_cache_dir)
+ openml.utils._remove_cache_dir_for_id("tasks", tid_cache_dir)
+ assert not os.path.exists(tid_cache_dir)
+
+
+@mock.patch.object(requests.Session, "delete")
+def test_delete_task_not_owned(mock_delete, test_files_directory, test_api_key):
+ content_file = test_files_directory / "mock_responses" / "tasks" / "task_delete_not_owned.xml"
+ mock_delete.return_value = create_request_response(
+ status_code=412,
+ content_filepath=content_file,
+ )
+
+ with pytest.raises(
+ OpenMLNotAuthorizedError,
+ match="The task can not be deleted because it was not uploaded by you.",
+ ):
+ openml.tasks.delete_task(1)
+
+ task_url = f"{openml.config.TEST_SERVER_URL}/api/v1/xml/task/1"
+ assert task_url == mock_delete.call_args.args[0]
+ assert test_api_key == mock_delete.call_args.kwargs.get("params", {}).get("api_key")
+
+
+@mock.patch.object(requests.Session, "delete")
+def test_delete_task_with_run(mock_delete, test_files_directory, test_api_key):
+ content_file = test_files_directory / "mock_responses" / "tasks" / "task_delete_has_runs.xml"
+ mock_delete.return_value = create_request_response(
+ status_code=412,
+ content_filepath=content_file,
+ )
+
+ with pytest.raises(
+ OpenMLNotAuthorizedError,
+ match="The task can not be deleted because it still has associated entities:",
+ ):
+ openml.tasks.delete_task(3496)
+
+ task_url = f"{openml.config.TEST_SERVER_URL}/api/v1/xml/task/3496"
+ assert task_url == mock_delete.call_args.args[0]
+ assert test_api_key == mock_delete.call_args.kwargs.get("params", {}).get("api_key")
+
+
+@mock.patch.object(requests.Session, "delete")
+def test_delete_success(mock_delete, test_files_directory, test_api_key):
+ content_file = test_files_directory / "mock_responses" / "tasks" / "task_delete_successful.xml"
+ mock_delete.return_value = create_request_response(
+ status_code=200,
+ content_filepath=content_file,
+ )
+
+ success = openml.tasks.delete_task(361323)
+ assert success
+
+ task_url = f"{openml.config.TEST_SERVER_URL}/api/v1/xml/task/361323"
+ assert task_url == mock_delete.call_args.args[0]
+ assert test_api_key == mock_delete.call_args.kwargs.get("params", {}).get("api_key")
+
+
+@mock.patch.object(requests.Session, "delete")
+def test_delete_unknown_task(mock_delete, test_files_directory, test_api_key):
+ content_file = test_files_directory / "mock_responses" / "tasks" / "task_delete_not_exist.xml"
+ mock_delete.return_value = create_request_response(
+ status_code=412,
+ content_filepath=content_file,
+ )
+
+ with pytest.raises(
+ OpenMLServerException,
+ match="Task does not exist",
+ ):
+ openml.tasks.delete_task(9_999_999)
+
+ task_url = f"{openml.config.TEST_SERVER_URL}/api/v1/xml/task/9999999"
+ assert task_url == mock_delete.call_args.args[0]
+ assert test_api_key == mock_delete.call_args.kwargs.get("params", {}).get("api_key")
diff --git a/tests/test_tasks/test_task_methods.py b/tests/test_tasks/test_task_methods.py
new file mode 100644
index 000000000..9316d0876
--- /dev/null
+++ b/tests/test_tasks/test_task_methods.py
@@ -0,0 +1,62 @@
+# License: BSD 3-Clause
+from __future__ import annotations
+
+from time import time
+
+import openml
+from openml.testing import TestBase
+import pytest
+
+
+# Common methods between tasks
+class OpenMLTaskMethodsTest(TestBase):
+ def setUp(self):
+ super().setUp()
+
+ def tearDown(self):
+ super().tearDown()
+
+ @pytest.mark.test_server()
+ def test_tagging(self):
+ task = openml.tasks.get_task(1) # anneal; crossvalidation
+ # tags can be at most 64 alphanumeric (+ underscore) chars
+ unique_indicator = str(time()).replace(".", "")
+ tag = f"test_tag_OpenMLTaskMethodsTest_{unique_indicator}"
+ tasks = openml.tasks.list_tasks(tag=tag)
+ assert len(tasks) == 0
+ task.push_tag(tag)
+ tasks = openml.tasks.list_tasks(tag=tag)
+ assert len(tasks) == 1
+ assert 1 in tasks["tid"]
+ task.remove_tag(tag)
+ tasks = openml.tasks.list_tasks(tag=tag)
+ assert len(tasks) == 0
+
+ @pytest.mark.test_server()
+ def test_get_train_and_test_split_indices(self):
+ openml.config.set_root_cache_directory(self.static_cache_dir)
+ task = openml.tasks.get_task(1882)
+ train_indices, test_indices = task.get_train_test_split_indices(0, 0)
+ assert train_indices[0] == 16
+ assert train_indices[-1] == 395
+ assert test_indices[0] == 412
+ assert test_indices[-1] == 364
+ train_indices, test_indices = task.get_train_test_split_indices(2, 2)
+ assert train_indices[0] == 237
+ assert train_indices[-1] == 681
+ assert test_indices[0] == 583
+ assert test_indices[-1] == 24
+ self.assertRaisesRegex(
+ ValueError,
+ "Fold 10 not known",
+ task.get_train_test_split_indices,
+ 10,
+ 0,
+ )
+ self.assertRaisesRegex(
+ ValueError,
+ "Repeat 10 not known",
+ task.get_train_test_split_indices,
+ 0,
+ 10,
+ )
diff --git a/tests/test_utils/__init__.py b/tests/test_utils/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/test_utils/test_utils.py b/tests/test_utils/test_utils.py
new file mode 100644
index 000000000..38e004bfb
--- /dev/null
+++ b/tests/test_utils/test_utils.py
@@ -0,0 +1,181 @@
+from __future__ import annotations
+
+import os
+import unittest.mock
+import pytest
+import openml
+from openml.testing import _check_dataset
+
+
+@pytest.fixture()
+def min_number_tasks_on_test_server() -> int:
+ """After a reset at least 1068 tasks are on the test server"""
+ return 1068
+
+
+@pytest.fixture()
+def min_number_datasets_on_test_server() -> int:
+ """After a reset at least 127 datasets are on the test server"""
+ return 127
+
+
+@pytest.fixture()
+def min_number_flows_on_test_server() -> int:
+ """After a reset at least 127 flows are on the test server"""
+ return 15
+
+
+@pytest.fixture()
+def min_number_setups_on_test_server() -> int:
+ """After a reset at least 20 setups are on the test server"""
+ return 50
+
+
+@pytest.fixture()
+def min_number_runs_on_test_server() -> int:
+ """After a reset at least 21 runs are on the test server"""
+ return 21
+
+
+@pytest.fixture()
+def min_number_evaluations_on_test_server() -> int:
+ """After a reset at least 8 evaluations are on the test server"""
+ return 8
+
+
+def _mocked_perform_api_call(call, request_method):
+ url = openml.config.server + "/" + call
+ return openml._api_calls._download_text_file(url)
+
+
+@pytest.mark.test_server()
+def test_list_all():
+ openml.utils._list_all(listing_call=openml.tasks.functions._list_tasks)
+
+
+@pytest.mark.test_server()
+def test_list_all_for_tasks(min_number_tasks_on_test_server):
+ tasks = openml.tasks.list_tasks(size=min_number_tasks_on_test_server)
+ assert min_number_tasks_on_test_server == len(tasks)
+
+
+@pytest.mark.test_server()
+def test_list_all_with_multiple_batches(min_number_tasks_on_test_server):
+ # By setting the batch size one lower than the minimum we guarantee at least two
+ # batches and at the same time do as few batches (roundtrips) as possible.
+ batch_size = min_number_tasks_on_test_server - 1
+ batches = openml.utils._list_all(
+ listing_call=openml.tasks.functions._list_tasks,
+ batch_size=batch_size,
+ )
+ assert len(batches) >= 2
+ assert min_number_tasks_on_test_server <= sum(len(batch) for batch in batches)
+
+
+@pytest.mark.test_server()
+def test_list_all_for_datasets(min_number_datasets_on_test_server):
+ datasets = openml.datasets.list_datasets(
+ size=min_number_datasets_on_test_server,
+ )
+
+ assert min_number_datasets_on_test_server == len(datasets)
+ for dataset in datasets.to_dict(orient="index").values():
+ _check_dataset(dataset)
+
+
+@pytest.mark.test_server()
+def test_list_all_for_flows(min_number_flows_on_test_server):
+ flows = openml.flows.list_flows(size=min_number_flows_on_test_server)
+ assert min_number_flows_on_test_server == len(flows)
+
+
+@pytest.mark.flaky() # Other tests might need to upload runs first
+@pytest.mark.test_server()
+def test_list_all_for_setups(min_number_setups_on_test_server):
+ # TODO apparently list_setups function does not support kwargs
+ setups = openml.setups.list_setups(size=min_number_setups_on_test_server)
+ assert min_number_setups_on_test_server == len(setups)
+
+
+@pytest.mark.flaky() # Other tests might need to upload runs first
+@pytest.mark.test_server()
+def test_list_all_for_runs(min_number_runs_on_test_server):
+ runs = openml.runs.list_runs(size=min_number_runs_on_test_server)
+ assert min_number_runs_on_test_server == len(runs)
+
+
+@pytest.mark.flaky() # Other tests might need to upload runs first
+@pytest.mark.test_server()
+def test_list_all_for_evaluations(min_number_evaluations_on_test_server):
+ # TODO apparently list_evaluations function does not support kwargs
+ evaluations = openml.evaluations.list_evaluations(
+ function="predictive_accuracy",
+ size=min_number_evaluations_on_test_server,
+ )
+ assert min_number_evaluations_on_test_server == len(evaluations)
+
+
+@unittest.mock.patch("openml._api_calls._perform_api_call", side_effect=_mocked_perform_api_call)
+@pytest.mark.test_server()
+def test_list_all_few_results_available(_perform_api_call):
+ datasets = openml.datasets.list_datasets(size=1000, data_name="iris", data_version=1)
+ assert len(datasets) == 1, "only one iris dataset version 1 should be present"
+ assert _perform_api_call.call_count == 1, "expect just one call to get one dataset"
+
+
+@unittest.skipIf(os.name == "nt", "https://github.com/openml/openml-python/issues/1033")
+@unittest.mock.patch("openml.config.get_cache_directory")
+def test__create_cache_directory(config_mock, tmp_path):
+ config_mock.return_value = tmp_path
+ openml.utils._create_cache_directory("abc")
+ assert (tmp_path / "abc").exists()
+
+ subdir = tmp_path / "def"
+ subdir.mkdir()
+ subdir.chmod(0o444)
+ config_mock.return_value = subdir
+ with pytest.raises(
+ openml.exceptions.OpenMLCacheException,
+ match="Cannot create cache directory",
+ ):
+ openml.utils._create_cache_directory("ghi")
+
+
+@pytest.mark.test_server()
+def test_correct_test_server_download_state():
+ """This test verifies that the test server downloads the data from the correct source.
+
+ If this tests fails, it is highly likely that the test server is not configured correctly.
+ Usually, this means that the test server is serving data from the task with the same ID from the production server.
+ That is, it serves parquet files wrongly associated with the test server's task.
+ """
+ task = openml.tasks.get_task(119)
+ dataset = task.get_dataset()
+ assert len(dataset.features) == dataset.get_data()[0].shape[1]
+
+@unittest.mock.patch("openml.config.get_cache_directory")
+def test_get_cache_size(config_mock,tmp_path):
+ """
+ Test that the OpenML cache size utility correctly reports the cache directory
+ size before and after fetching a dataset.
+
+ This test uses a temporary directory (tmp_path) as the cache location by
+ patching the configuration via config_mock. It verifies two conditions:
+ empty cache and after dataset fetch.
+
+ Parameters
+ ----------
+ config_mock : unittest.mock.Mock
+ A mock that overrides the configured cache directory to point to tmp_path.
+ tmp_path : pathlib.Path
+ A pytest-provided temporary directory used as an isolated cache location.
+ """
+
+ config_mock.return_value = tmp_path
+ cache_size = openml.utils.get_cache_size()
+ assert cache_size == 0
+ sub_dir = tmp_path / "subdir"
+ sub_dir.mkdir()
+ (sub_dir / "nested_file.txt").write_bytes(b"b" * 100)
+
+ assert openml.utils.get_cache_size() == 100
\ No newline at end of file
diff --git a/tox.ini b/tox.ini
deleted file mode 100755
index fbf6b6537..000000000
--- a/tox.ini
+++ /dev/null
@@ -1,16 +0,0 @@
-[tox]
-envlist = py27,py34
-
-[testenv]
-deps =
- numpy > 1.6.2
- scipy > 0.9
- pandas > 0.13.1
- xmltodict
- nose
- mock
-commands=
- python setup.py install
- python setup.py test
-
-