From 054d1b8b2a4029ce889d3722f6444c17cb2a6859 Mon Sep 17 00:00:00 2001 From: jpatacz Date: Mon, 28 Feb 2022 18:36:02 +0100 Subject: [PATCH] Adding tracking data improvements --- .github/workflows/packaging-and-releasing.yml | 38 ++ docks/requirements.txt | 5 + setup.py | 8 +- skillcorner/__init__.py | 3 + skillcorner/client.py | 329 ++++++++++++++---- skillcorner/physical_visualisation_example.py | 7 + skillcorner/tests/mocks/client_mock.py | 3 +- .../tracking_visualisation_example.ipynb | 154 ++++++++ skillcorner/visualisation_utils.py | 272 +++++++++++++++ 9 files changed, 754 insertions(+), 65 deletions(-) create mode 100644 docks/requirements.txt create mode 100644 skillcorner/__init__.py create mode 100644 skillcorner/physical_visualisation_example.py create mode 100644 skillcorner/tracking_visualisation_example.ipynb create mode 100644 skillcorner/visualisation_utils.py diff --git a/.github/workflows/packaging-and-releasing.yml b/.github/workflows/packaging-and-releasing.yml index 69c9723..a34edd0 100644 --- a/.github/workflows/packaging-and-releasing.yml +++ b/.github/workflows/packaging-and-releasing.yml @@ -16,6 +16,22 @@ jobs: - name: Edit version run: sed -r -i 's/(.*)(version=.*)([0-9]+).([0-9]+).([0-9]+)(.*)/echo "\1\2\3.\4.$((\5+1))\6"/ge' setup.py + - name: Branch protection OFF + uses: octokit/request-action@v2.x + with: + route: PUT /repos/:repository/branches/1.0.0/protection + repository: ${{ github.repository }} + required_status_checks: | + null + enforce_admins: | + null + required_pull_request_reviews: | + null + restrictions: | + null + env: + GITHUB_TOKEN: ${{ secrets.GH_ACTIONS_REPO_ADMIN_TOKEN }} + - name: Commit changes uses: EndBug/add-and-commit@v7 with: @@ -29,6 +45,28 @@ jobs: with: node-version: '14' + - name: Branch protection ON + uses: octokit/request-action@v2.x + with: + route: PUT /repos/:repository/branches/1.0.0/protection + repository: ${{ github.repository }} + mediaType: | + previews: + - luke-cage + required_status_checks: | + strict: true + contexts: + - build + enforce_admins: | + null + required_pull_request_reviews: | + dismiss_stale_reviews: true + required_approving_review_count: 1 + restrictions: | + null + env: + GITHUB_TOKEN: ${{ secrets.GH_ACTIONS_REPO_ADMIN_TOKEN }} + - name: Display setup.py run: cat setup.py diff --git a/docks/requirements.txt b/docks/requirements.txt new file mode 100644 index 0000000..a41e77f --- /dev/null +++ b/docks/requirements.txt @@ -0,0 +1,5 @@ +requests>=2.20.0 +makefun>=1.10.0 +numpy>=1.18.4, <1.19.0 +pandas +matplotlib>=3.5.0 diff --git a/setup.py b/setup.py index 37f3269..39e0f30 100644 --- a/setup.py +++ b/setup.py @@ -10,10 +10,14 @@ python_requires='>=3.6, <=3.9', install_requires=[ 'requests>=2.20.0', - 'makefun>=1.10.0' + 'makefun>=1.10.0', + 'numpy>=1.18.4, <1.19.0', + 'pandas', + 'matplotlib>=3.5.0', ], extras_require={ 'test': 'mock==4.0.3', - 'release': ['Sphinx==4.2.0', 'sphinx-rtd-theme==1.0.0'] + 'release': ['Sphinx==4.2.0', 'sphinx-rtd-theme==1.0.0'], + 'physical_visualisation': 'pandasgui', } ) diff --git a/skillcorner/__init__.py b/skillcorner/__init__.py new file mode 100644 index 0000000..cf5d7f1 --- /dev/null +++ b/skillcorner/__init__.py @@ -0,0 +1,3 @@ +from pkg_resources import get_distribution + +__version__ = get_distribution('skillcorner').version diff --git a/skillcorner/client.py b/skillcorner/client.py index 8845896..8617505 100644 --- a/skillcorner/client.py +++ b/skillcorner/client.py @@ -1,8 +1,8 @@ import json -import simplejson import logging import os import requests +import inspect from datetime import datetime, timedelta from functools import wraps from inspect import currentframe, getargvalues @@ -15,10 +15,6 @@ METHOD_DOCSTRING = 'Returns full {url} request response data in the json format. ' \ 'To learn more about endpoint go to: https://skillcorner.com/api/docs/#{docs_url_anchor}\n' -METHOD_ID_DOCSTRING = 'Returns full {url}' \ - ' request response data in the json format. ' \ - 'To learn more about endpoint go to: https://skillcorner.com/api/docs/#{docs_url_anchor}\n' - METHOD_URL_BINDING = { '_get_matches': { 'url': '/api/matches/', @@ -44,6 +40,12 @@ 'url': '/api/physical', 'paginated_request': False, 'docs_url_anchor': '/physical/physical', + 'default_output_format': 'df', + 'output_format_file_format_binding': { + 'df': ['json'], + 'python_structured_data': ['json'] + }, + 'pre_call_cb': 'verify_output_format', } } @@ -65,6 +67,14 @@ 'paginated_request': False, 'docs_url_anchor': '/match/match_tracking_list', 'id_name': 'match_id', + 'default_output_format': 'df', + 'output_format_file_format_binding': { + 'bytes': ['fifa-xml', 'fifa-data'], + 'python_structured_data': ['jsonl'], + 'df': ['jsonl'] + }, + 'df_parameterization': 'positions_per_frame', + 'pre_call_cb': 'verify_output_format', }, '_get_match_data_collection': { 'url': '/api/match/{}/data_collection', @@ -92,9 +102,40 @@ } } + logger = logging.getLogger(__name__) +def verify_output_format(output_format, params, output_format_file_format_binding): + logger.debug(f"Verifying output format in callback. Passed output_format: {output_format} and params: {params}.") + + if output_format not in output_format_file_format_binding.keys(): + type_error_text = f'Requested output format: "{output_format}" is not supported for this endpoint. ' + type_error_text += f'Supported output formats: ' + for key in output_format_file_format_binding.keys(): + type_error_text += f'"{key}", ' + type_error_text = type_error_text[:-2] + raise TypeError(type_error_text) + + file_format_param = None + if params: + if 'file_format' in params.keys(): + file_format_param = params['file_format'] + logger.debug(f'File format parameter: {file_format_param}.') + + if file_format_param and file_format_param not in output_format_file_format_binding[output_format]: + raise TypeError(f'File format: "{file_format_param}" requested in the params argument cannot be converted into ' + f'output format: "{output_format}" requested in output_format argument.') + + elif not file_format_param: + for key, value in output_format_file_format_binding.items(): + if key == output_format: + file_format_param = value[0] + break + + return file_format_param + + def _args_logging(logger): """Decorator logging arguments passed to the function @@ -108,7 +149,7 @@ def decorator(func): @wraps(func) def wrapper(*args, **kwargs): - logger.debug(f'Calling {fname} with following arguments: ') + logger.debug(f'Calling {fname} with following arguments: {args} {kwargs}') for pair in zip(argnames, args): if pair[0] == "self": continue @@ -155,6 +196,19 @@ def wrapper(*args, **kwargs): kwargs['id'] = id_value del kwargs[id_name] + output_format_file_format_binding = frozen_kwargs['output_format_file_format_binding'] + pre_call_cb = frozen_kwargs['pre_call_cb'] + if pre_call_cb: + pre_call_cb = globals()[pre_call_cb] + file_format_param = pre_call_cb(passed_kwargs['output_format'], + passed_kwargs['params'], + output_format_file_format_binding) + if not kwargs['params']: + kwargs['params'] = dict() + if 'file_format' not in kwargs['params'].keys(): + kwargs['params']['file_format'] = None + kwargs['params']['file_format'] = file_format_param + kwargs.update(frozen_kwargs) return func(*args, **kwargs) @@ -174,17 +228,32 @@ class _MethodsGenerator(type): proper and valid URL (e. g. 'match_id' for 'get_matches' method). """ - def _generate_signature(cls, func_name, filepath=None, id_name=None): + def _generate_signature(cls, func_name, filepath=None, id_name=None, output_format=None): + frame = inspect.currentframe() + args, _, _, values = inspect.getargvalues(frame) + args.remove('cls') + args.remove('func_name') + func = getattr(cls, func_name) public_func_name = func_name.strip("_") - if filepath and not id_name: - public_func_sig = f"{public_func_name}(self, filepath, params=None)" - elif id_name and not filepath: - public_func_sig = f"{public_func_name}(self, {id_name}, params=None)" - elif id_name and filepath: - public_func_sig = f"{public_func_name}(self, {id_name}, filepath, params=None)" - else: - public_func_sig = f"{public_func_name}(self, params=None)" + + public_func_sig = f"{public_func_name}(self, " + for arg in args: + if values[arg]: + if arg == 'id_name': + public_func_sig += values[arg] + public_func_sig += ', ' + else: + if type(values[arg]) == type(str()): + if values[arg] == 'None': + public_func_sig += f"{arg}={values[arg]}, " + else: + public_func_sig += f"{arg}='{values[arg]}', " + else: + public_func_sig += arg + public_func_sig += ', ' + public_func_sig += 'params=None)' + public_func_gen = create_function(public_func_sig, func) setattr(cls, public_func_name, public_func_gen) public_func = getattr(cls, public_func_name) @@ -194,68 +263,124 @@ def __new__(cls, classname, supers, cls_dict): skcr_client = super().__new__(cls, classname, supers, cls_dict) for key, value in METHOD_URL_BINDING.items(): + default_output_format = value.get('default_output_format', False) + output_format_file_format_binding = value.get('output_format_file_format_binding', False) + pre_call_cb = value.get('pre_call_cb', False) timeout = value.get('timeout', DEFAULT_TIMEOUT) + setattr(skcr_client, key, _freeze_args(skcr_client._get_data, url=value['url'], paginated_request=value['paginated_request'], - timeout=timeout)) - cls_dict[key.strip("_")] = skcr_client._generate_signature(key) + timeout=timeout, + output_format_file_format_binding=output_format_file_format_binding, + pre_call_cb=pre_call_cb)) + cls_dict[key.strip("_")] = skcr_client._generate_signature(key, + output_format=default_output_format) docs_url_anchor = value.get('docs_url_anchor', False) - if docs_url_anchor: + if docs_url_anchor and not default_output_format: docstring = METHOD_DOCSTRING.format(url=value['url'], docs_url_anchor=docs_url_anchor) + elif docs_url_anchor and default_output_format: + docstring = f'Returns full {value["url"]} request response data in the chosen format ({list(output_format_file_format_binding.keys())}). ' + docstring +=f'To learn more about endpoint go to: https://skillcorner.com/api/docs/#{docs_url_anchor}\n' + elif not docs_url_anchor and default_output_format: + docstring = f'Returns full {value["url"]} request response data in the chosen format ({list(output_format_file_format_binding.keys())}). ' else: - docstring = 'Returns full {url} request response data in the json format.'.format(url=value['url']) + docstring = f'Returns full {value["url"]} request response data in the json format.' + cls_dict[key.strip("_")].__doc__ = docstring - get_and_save_func_name = key.replace('_get_', '_get_and_save_') + get_and_save_method_name = key.replace('_get_', '_get_and_save_') + if default_output_format == 'df': + default_output_format = 'csv' + write_output_format_file_format_binding = None + if output_format_file_format_binding: + write_output_format_file_format_binding = output_format_file_format_binding.copy() + if write_output_format_file_format_binding: + if 'df' in write_output_format_file_format_binding.keys(): + write_output_format_file_format_binding['csv'] = write_output_format_file_format_binding.pop('df') setattr(skcr_client, - get_and_save_func_name, + get_and_save_method_name, _freeze_args(skcr_client._get_and_write_data, url=value['url'], paginated_request=value['paginated_request'], - timeout=timeout)) - cls_dict[get_and_save_func_name.strip("_")] = skcr_client._generate_signature(get_and_save_func_name, - filepath=True) - + timeout=timeout, + output_format_file_format_binding=write_output_format_file_format_binding, + pre_call_cb=pre_call_cb)) + cls_dict[get_and_save_method_name.strip("_")] = skcr_client._generate_signature(get_and_save_method_name, + filepath=True, + output_format=default_output_format) get_and_save_docstring = docstring.split(" in the ")[0] + " and saves in the file using " + \ docstring.split(" in the ")[1] - cls_dict[get_and_save_func_name.strip("_")].__doc__ = get_and_save_docstring + cls_dict[get_and_save_method_name.strip("_")].__doc__ = get_and_save_docstring for key, value in METHOD_URL_ID_BINDING.items(): + default_output_format = value.get('default_output_format', False) + output_format_file_format_binding = value.get('output_format_file_format_binding', False) + pre_call_cb = value.get('pre_call_cb', False) + df_parameterization = value.get('df_parameterization', False) + timeout = value.get('timeout', DEFAULT_TIMEOUT) setattr(skcr_client, key, _freeze_args(skcr_client._get_data_with_id, id_name=value["id_name"], url=value['url'], paginated_request=value['paginated_request'], - timeout=timeout)) - cls_dict[key.strip("_")] = skcr_client._generate_signature(key, id_name=value['id_name']) + timeout=timeout, + output_format_file_format_binding=output_format_file_format_binding, + pre_call_cb=pre_call_cb, + df_parameterization=df_parameterization)) + + cls_dict[key.strip("_")] = skcr_client._generate_signature(key, + id_name=value['id_name'], + output_format=default_output_format) docks_url = value['url'].split('{}')[0] + '{' + value['id_name'] + '}' + value['url'].split('{}')[1] docs_url_anchor = value.get('docs_url_anchor', False) - if docs_url_anchor: - docstring = METHOD_ID_DOCSTRING.format(url=value['url'], + if docs_url_anchor and not default_output_format: + docstring = METHOD_DOCSTRING.format(url=value['url'], docs_url_anchor=docs_url_anchor) + elif docs_url_anchor and default_output_format: + docstring = f'Returns full {value["url"]} request response data in the chosen format ({list(output_format_file_format_binding.keys())}). ' + if key == '_get_match_tracking_data': + docstring += 'If format of the response is not specified by file_format passed to the method ' + docstring += 'through params argument, it will be automatically adapted to the passed output_format. ' + docstring += 'If format of the response requested with file_format parameter passed to the method through ' + docstring += 'params argument is in conflict with request output_format, TypeError will be raised.' + docstring += f'To learn more about endpoint go to: https://skillcorner.com/api/docs/#{docs_url_anchor}\n' + elif not docs_url_anchor and default_output_format: + docstring = f'Returns full {value["url"]} request response data in the chosen format ({list(output_format_file_format_binding.keys())}). ' else: - docstring = 'Returns full {url} request response data in the json format.'.format(url=value['url']) + docstring = f'Returns full {value["url"]} request response data in the json format.' + cls_dict[key.strip("_")].__doc__ = docstring - timeout = value.get('timeout', DEFAULT_TIMEOUT) get_and_save_method_name = key.replace('_get_', '_get_and_save_') + if default_output_format == 'df': + default_output_format = 'csv' + write_output_format_file_format_binding = None + if output_format_file_format_binding: + write_output_format_file_format_binding = output_format_file_format_binding.copy() + if write_output_format_file_format_binding: + if 'df' in write_output_format_file_format_binding.keys(): + write_output_format_file_format_binding['csv'] = write_output_format_file_format_binding.pop('df') setattr(skcr_client, get_and_save_method_name, _freeze_args(skcr_client._get_and_write_data_with_id, id_name=value["id_name"], url=value['url'], paginated_request=value['paginated_request'], - timeout=timeout)) + timeout=timeout, + output_format_file_format_binding=write_output_format_file_format_binding, + pre_call_cb=pre_call_cb, + df_parameterization=df_parameterization)) cls_dict[get_and_save_method_name.strip("_")] = skcr_client._generate_signature(get_and_save_method_name, filepath=True, - id_name=value['id_name']) + id_name=value['id_name'], + output_format=default_output_format) get_and_save_docstring = docstring.split(" in the ")[0] + " and saves in the file using " + \ docstring.split(" in the ")[1] @@ -294,8 +419,11 @@ def __init__(self, username=None, password=None): self.base_url = BASE_URL logger.debug(f'Base url: {self.base_url}') + from skillcorner.visualisation_utils import create_full_visualisation, create_visualisation_per_frame + @_args_logging(logger) - def _skillcorner_request(self, url, method, params, paginated_request, timeout, json_data=None, pagination_limit=300): + def _skillcorner_request(self, url, method, params, paginated_request, timeout, json_data=None, + pagination_limit=300, **kwargs): """Custom Skillcorner API request Custom request function using session object to persist parameters for Skillcorner host connection. @@ -309,6 +437,10 @@ def _skillcorner_request(self, url, method, params, paginated_request, timeout, :param int pagination_limit: indicates pagination limit :return dict: contains response from server """ + if '/video/tracking' in url: + logger.warning('WARNING: get_match_video_tracking_data method accesses non-extrapolated tracking data. ' + 'We recommend to work with extrapolated data that can be accessed with ' + 'get_match_tracking_data method.') url = '{}{}'.format(self.base_url, url) logger.info(f'Connecting to: {url}') @@ -317,6 +449,12 @@ def _skillcorner_request(self, url, method, params, paginated_request, timeout, if not params: params = {} + else: + for key, value in params.items(): + if value==True: + params[key] = 1 + elif False in params.values(): + params[key] = 2 data = {} if paginated_request: @@ -375,15 +513,39 @@ def _skillcorner_request(self, url, method, params, paginated_request, timeout, timeout=timeout) skillcorner_response.raise_for_status() - try: - data = skillcorner_response.json() - except (json.decoder.JSONDecodeError, simplejson.errors.JSONDecodeError) as ex: - data_bytes = skillcorner_response.content - data_list = data_bytes.decode('utf-8').split("\n") - data = [] - for line in data_list: - if line: - data.append(json.loads(line)) + content_type = skillcorner_response.headers['Content-Type'] + output_format = kwargs.get('output_format', False) + + if content_type == 'application/json': + logger.debug('Get json data.') + response_data = skillcorner_response.json() + + elif content_type == 'application/json-l': + from skillcorner.visualisation_utils import _get_json_list + logger.debug('Get json-l data.') + response_data = _get_json_list(skillcorner_response) + + elif content_type == 'binary/octet-stream': + logger.debug('Get binary data.') + response_data = skillcorner_response.content + + elif content_type == 'text/plain': + logger.debug('Get plain text data.') + response_data = skillcorner_response.content.decode('utf-8') + + else: + logger.warning('Unexpected response Content-Type. Returning binary content.') + response_data = skillcorner_response.content + + data = response_data + if output_format == 'df' or output_format == 'csv': + logger.debug(f'Convert response content into Data Frame.') + df_parameterization = kwargs.get('df_parameterization', False) + if isinstance(response_data, dict) or isinstance(response_data, list): + from skillcorner.visualisation_utils import _convert_response_to_df + data = _convert_response_to_df(response_data, df_parameterization) + else: + raise ValueError('Content-Type of data returned by server cannot be converted into Data Frame') end_timestamp = datetime.now() @@ -395,7 +557,8 @@ def _skillcorner_request(self, url, method, params, paginated_request, timeout, return data - def _get_data(self, *, url, paginated_request, timeout, params=None): + + def _get_data(self, *, url, paginated_request, timeout, params=None, **kwargs): """General get... function Uses skillcorner request to get response from passed url without any additional parameters. @@ -406,11 +569,13 @@ def _get_data(self, *, url, paginated_request, timeout, params=None): return self._skillcorner_request(url=url, method='GET', - params=params, paginated_request=paginated_request, - timeout=timeout) + timeout=timeout, + params=params, + **kwargs) + - def _get_and_write_data(self, filepath, *, url, paginated_request, timeout, params=None): + def _get_and_write_data(self, filepath, *, url, paginated_request, timeout, params=None, **kwargs): """General get_and_write... function Uses skillcorner request to get response from passed url without any additional parameters and save @@ -422,15 +587,37 @@ def _get_and_write_data(self, filepath, *, url, paginated_request, timeout, para data = self._skillcorner_request(url=url, method='GET', - params=params, paginated_request=paginated_request, - timeout=timeout) + timeout=timeout, + params=params, + **kwargs) logger.info(f'Writing response to the file: {filepath}') - with open(filepath, 'w') as file: - json.dump(data, file, indent=4) + import pandas as pd + if isinstance(data, dict): + logger.info('Save dict data in json file.') + with open(filepath, 'w') as file: + json.dump(data, file, indent=4) + elif isinstance(data, list): + logger.info('Save list data in json file.') + with open(filepath, 'w') as file: + for line in data: + json.dump(line, file, indent=4) + file.write('\n') + elif isinstance(data, str): + logger.info('Save str data in txt file.') + with open(filepath, 'w') as file: + file.write(data) + elif isinstance(data, pd.DataFrame): + logger.info('Save DataFrame data in csv file.') + data.to_csv(filepath) + elif isinstance(data, bytes): + logger.info('Save bytes data in binary file.') + with open(filepath, 'wb') as file: + file.write(data) - def _get_data_with_id(self, id, *, url, paginated_request, timeout, params=None): + + def _get_data_with_id(self, id, *, url, paginated_request, timeout, params=None, **kwargs): """General get...(id) function Uses skillcorner request to get response from passed url with one parameter. @@ -442,11 +629,13 @@ def _get_data_with_id(self, id, *, url, paginated_request, timeout, params=None) url = url.format(id) return self._skillcorner_request(url=url, method='GET', - params=params, paginated_request=paginated_request, - timeout=timeout) + timeout=timeout, + params=params, + **kwargs) - def _get_and_write_data_with_id(self, id, filepath, *, url, paginated_request, timeout, params=None): + + def _get_and_write_data_with_id(self, id, filepath, *, url, paginated_request, timeout, params=None, **kwargs): """General get_and_write...(id) function Uses skillcorner request to get response from passed url with one parameter and save the response in JSON file. @@ -454,18 +643,34 @@ def _get_and_write_data_with_id(self, id, filepath, *, url, paginated_request, t :return: dict containing server response """ - url = url.format(id) data = self._skillcorner_request(url=url, method='GET', - params=params, paginated_request=paginated_request, - timeout=timeout) + timeout=timeout, + params=params, + **kwargs) - logger.info(f'Writing response to the file: {filepath}') - try: + logger.info(f'Writing response to the file: {filepath}.') + import pandas as pd + if isinstance(data, dict): + logger.info('Save dict data in json file.') with open(filepath, 'w') as file: json.dump(data, file, indent=4) - except TypeError: + elif isinstance(data, list): + logger.info('Save list data in json file.') + with open(filepath, 'w') as file: + for line in data: + json.dump(line, file, indent=4) + file.write('\n') + elif isinstance(data, str): + logger.info('Save str data in txt file.') + with open(filepath, 'w') as file: + file.write(data) + elif isinstance(data, pd.DataFrame): + logger.info('Save DataFrame data in csv file.') + data.to_csv(filepath) + elif isinstance(data, bytes): + logger.info('Save bytes data in binary file.') with open(filepath, 'wb') as file: file.write(data) diff --git a/skillcorner/physical_visualisation_example.py b/skillcorner/physical_visualisation_example.py new file mode 100644 index 0000000..aabfb1d --- /dev/null +++ b/skillcorner/physical_visualisation_example.py @@ -0,0 +1,7 @@ +from pandasgui import show +from skillcorner.client import SkillcornerClient + + +skc_client = SkillcornerClient(username='PUT_YOUR_LOGIN_HERE', password='PUT_YOUR_PASSWORD_HERE') +physical_data = skc_client.get_physical(params={'season': 6, 'competition': 1}) +show(physical_data) diff --git a/skillcorner/tests/mocks/client_mock.py b/skillcorner/tests/mocks/client_mock.py index 3f91679..a8fa39a 100644 --- a/skillcorner/tests/mocks/client_mock.py +++ b/skillcorner/tests/mocks/client_mock.py @@ -29,7 +29,8 @@ def __init__(self, *args, **kwargs): super(MockSkillcornerClient, self).__init__(*args, **kwargs) logger.debug(f'Creating Skillcorner mock client instance') - def _skillcorner_request(self, url, method, params, paginated_request, timeout, pagination_limit=300): + def _skillcorner_request(self, url, method, params, paginated_request, timeout, output_format=None, + visualisation=None, json_data=None, pagination_limit=300, **kwargs): """ Mocked skillcorner_request method returning fake json response read from file. diff --git a/skillcorner/tracking_visualisation_example.ipynb b/skillcorner/tracking_visualisation_example.ipynb new file mode 100644 index 0000000..f546275 --- /dev/null +++ b/skillcorner/tracking_visualisation_example.ipynb @@ -0,0 +1,154 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "2bee4754", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "from skillcorner.client import SkillcornerClient\n", + "skc_client = SkillcornerClient('PUT_YOUR_LOGIN_HERE', 'PUT_YOUR_PASSWORD_HERE')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "f8fc6482", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "d01b9ea8e78045d18ac341645e59a626", + "version_major": 2, + "version_minor": 0 + }, + "image/png": "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ghMeHq5Fixbp3HPP1YwZM7RmzZp2qfecc85RUlKSXnrpJXm9Xv/2119/vU1NibNnz9aqVav0yiuv6PDhw6ds/q2trW3SVJqamqr09PSAptY+ffpo1apVqq6u9m9bvHix9u3bF/DY5l731atXKycnp9XnUK+kpKTJz6B+FHF9bQ6HQzabLeBK5O7du5td8SMqKqrZENtYdna2wsPD9dxzzwU8/8svv6yysjL/yGIAXRvTwAAhaMKECUpISNBNN92kn//857LZbHrttdeaBAq73a4XX3xRM2bM0MiRI3XLLbforLPOUl5ennJzc/Xhhx82Obbb7dbixYt18cUX65JLLtEnn3zSqj6EJxMeHq6HH35Yd999ty6++GLNmjVLu3fv1vz589WnT59WX72aNWuW7r//ft1///1KTExs8WpYvfLycnXv3l1XX321RowYoejoaC1btkxr167VU0895d/vtttu08KFCzVt2jTNmjVLO3bs0N/+9rcmU6lcdtllWrRoka688kpNnz5du3bt0l/+8hcNHjzY32zbWq+++qrmzp2rK6+8Un369FF5ebleeuklxcbG6tJLL5UkTZ8+XU8//bSmTZum66+/XoWFhXrhhRfUt29fbdq0KeB4o0eP1rJly/T0008rPT1dvXr10tixY5s8b0pKih544AH97ne/07Rp0/TDH/5QW7du1dy5c3XuueeeciJyAF2EWcOPAbRNS9PADBkypNn9v/jiC2PcuHGG2+020tPTjV//+tfGhx9+aEgyVqxYEbDv559/bkyePNmIiYkxoqKijOHDhwdMqdJwGph6hw8fNgYPHmx069bN+O677/z1NDcNzFtvvRXw2F27dhmSjHnz5gVsf+6554yePXsaERERxpgxY4wvvvjCGD16tDFt2rTWvESGYRjGeeedZ0gybrvttmbvV4PpV6qqqoxf/epXxogRI/znPmLECGPu3LlNHvfUU08ZGRkZRkREhHHeeecZ69ata3K+Pp/PePTRR/3nMGrUKGPx4sXGTTfdZPTs2bPFOgyj6XQsX331lXHdddcZPXr0MCIiIozU1FTjsssuM9atWxdwnJdfftno16+fERERYQwcONCYN2+e8dBDDzX5XcnLyzPOP/98w+12G5L8U8I0ft56f/7zn42BAwcaYWFhRlpamnHHHXc0mSqopd+/5s4XQHCxGQY9dQEE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vHkzIRAAYLpjx45pxIgReuGFF5q9/80339R9992nhx56SF999ZVGjBihqVOnqrCwsE37dDabYRiGac+Ok7r55ptVWlra5K+NCy+8UEOHDpXT6dTf/vY3DRs2TCtWrNDTTz+tefPmaefOnUpMTNSMGTP0xBNPKDo6WpI0f/583Xvvvfrb3/6mX/7yl9q3b58uvfRSLViwQG+99ZYeeughlZWV6cYbb9Qzzzwjh8MhSaqqqtJvfvMb/f3vf1dpaamGDh2qxx9/XBdeeKEkac+ePbrrrrv0+eefq7q6WllZWXryySd16aWX+muuqanRqlWrFBcXp6FDh8pub/lvD5/Ppy1btqikpEQJCQkn3RcAgNMxdOjQNj/GZrPp7bff1hVXXOHfNnbsWJ177rn685//LKnuOywzM1N33323/uM//qPV+3Q2vlm7qFdffVXh4eH64osv9Je//EWSZLfb9dxzzyk3N1evvvqqPv74Y/36178OeNzx48f13HPP6Y033tCSJUu0cuVKXXnllXr//ff1/vvv67XXXtP//M//aOHChf7H3HXXXcrJydEbb7yhTZs26ZprrtG0adP03XffSZLuvPNOVVVV6dNPP9XmzZv1+OOP+0NnvYKCAvl8Pg0YMOCUgc5ut2vAgAEyDENVVVXt8XIBANDuqqurtX79emVnZ/u32e12ZWdnKycnp9X7mIFBIEFu8eLFAWHqkksukST169dPTzzxRMC+9957r///WVlZeuSRR/TTn/5Uc+fO9W+vqanRiy++qD59+kiSrr76ar322msqKCjw98m76KKLtGLFCs2ePVt79+7VvHnztHfvXqWnp0uS7r//fi1ZskTz5s3To48+qr179+qqq67SsGHDJEm9e/cOqMswDOXn5ys5OVkRERGtOu+IiAilpKSouLhYLpeL0cEAgKBz+PBh1dbWKi0tLWB7Wlqa8vLyWr2PGQiAQe6iiy7Siy++6L8dFRWl6667TqNHj26y77Jly/THP/5ReXl5Onr0qLxerzwej44fP67IyEhJUmRkpD/8SXW/gFlZWQEhMy0tzd8vYfPmzaqtrVX//v0DnquqqkpJSUmSpJ///Oe644479NFHHyk7O1tXXXWVhg8f7t+3pqZGlZWVTfounkpKSoqKiopkGAYBEACAdkQADHJRUVHq27dvs9sb2r17ty677DLdcccd+sMf/qDExER9/vnn+vGPf6zq6mp/AGw8vYrNZmt2m8/nkyRVVFTI4XBo/fr1/j6B9epD42233aapU6fqvffe00cffaQ//vGPeuqpp3T33XdLkmprayVJTmfbft3q96ebKgAgGCUnJ8vhcKigoCBge0FBgbp169bqfcxAH8AQsX79evl8Pj311FMaN26c+vfvrwMHDpzxcUeNGqXa2loVFhaqb9++Af8a/uJmZmbqpz/9qRYtWqRf/vKXeumll/z31QdHr9fbpueu35+rfwCAYBQeHq7Ro0dr+fLl/m0+n0/Lly/X+PHjW72PGbgCGCL69u2rmpoaPf/885oxY0bA4JAz0b9/f91www2aM2eOnnrqKY0aNUpFRUVavny5hg8frunTp+vee+/VJZdcov79+6ukpEQrVqzQoEGD/McICwuT2+1WUVGRUlNTW/3cRUVFcjgcBEAAgGkqKioC5t/dtWuXNmzYoMTERPXo0UP33XefbrrpJp1zzjkaM2aM/vSnP+nYsWO65ZZb/I9pzT6djQAYIkaMGKGnn35ajz/+uB544AGdf/75+uMf/6g5c+ac8bHnzZunRx55RL/85S/9gznGjRunyy67TFJdE++dd96p/fv3KzY2VtOmTdMzzzzjf7zNZlNGRoZ27NihqqqqVg0Eqaqq0uHDh9WnTx917979jM8BAIDTsW7dOl100UX+2/fdd58k6aabbtL8+fM1e/ZsFRUV6cEHH9ShQ4c0cuRILVmyJGDQR2v26WzMA4hOcTrzAJaVlWncuHEsCwcAQDujDyA6RVhYmIYMGaKSkhJt2bKlxfn9qqqq/JNADxkyhPAHAEAH4AogOlVxcbFyc3Pl8/mUnJyslJQUOZ1Oeb1eFRUV6fDhw7Lb7RoyZIgSExPNLhcAgJBEAESnq6mpUUFBgfLz81VZWenf7na7lZGRoW7durV5yhgAANB6BECYxjAMeb1eeb1eOZ1OOZ1ORvwCANAJCIAAAAAWwyAQAAAAiyEAAgAAWAwBEAAAwGIIgAAAABZDAAQAALAYAiAAAIDFEAABAAAshgAIAABgMQRAAAAAiyEAAgAAWAwBsAu4+eabdcUVV5hdBgAACBEEQJPZbLaT/nv44Yf17LPPav78+abW2VVDaHFxsW644QbFxsYqPj5eP/7xj1VRURGwz6ZNmzRx4kS5XC5lZmbqiSeeOOVxPR6P7rzzTiUlJSk6OlpXXXWVCgoKAvbZu3evpk+frsjISKWmpupXv/qVvF7vSY9rGIYefPBBnXXWWXK73crOztZ3330XsE9WVlaT35PHHnvMlHoBAF0TAdBkBw8e9P/705/+pNjY2IBt999/v+Li4hQfH292qV3SDTfcoNzcXC1dulSLFy/Wp59+qp/85Cf++48ePaopU6aoZ8+eWr9+vZ588kk9/PDD+utf/3rS4/7iF7/Qv/71L7311lv65JNPdODAAc2cOdN/f21traZPn67q6mp9+eWXevXVVzV//nw9+OCDJz3uE088oeeee05/+ctftHr1akVFRWnq1KnyeDwB+/3Xf/1XwO/J3XffbUq9AIAuykDQmDdvnhEXF9dk+0033WRcfvnl/tsXXHCBcddddxn33HOPER8fb6Smphp//etfjYqKCuPmm282oqOjjT59+hjvv/9+wHE2b95sTJs2zYiKijJSU1ONH/3oR0ZRUZH//rfeessYOnSo4XK5jMTERGPSpElGRUWF8dBDDxmSAv6tWLHCMAzD+PWvf23069fPcLvdRq9evYzf/va3RnV1tf+YDz30kDFixAjj5ZdfNjIzM42oqCjjjjvuMLxer/H4448baWlpRkpKivHII48E1CrJmDt3rjFt2jTD5XIZvXr1Mt566602vZ7ffPONIclYu3atf9sHH3xg2Gw2Iz8/3zAMw5g7d66RkJBgVFVV+ff593//d2PAgAEtHre0tNQICwsLqOfbb781JBk5OTmGYRjG+++/b9jtduPQoUP+fV588UUjNjY24Lka8vl8Rrdu3Ywnn3wy4LkiIiKMv//97/5tPXv2NJ555plWvgodVy8AoOviCmAX9eqrryo5OVlr1qzR3XffrTvuuEPXXHONJkyYoK+++kpTpkzRjTfeqOPHj0uSSktLdfHFF2vUqFFat26dlixZooKCAs2aNUtS3ZXI6667Trfeequ+/fZbrVy5UjNnzpRhGLr//vs1a9YsTZs2zX/FacKECZKkmJgYzZ8/X998842effZZvfTSS3rmmWcCat2xY4c++OADLVmyRH//+9/18ssva/r06dq/f78++eQTPf744/rtb3+r1atXBzzuP//zP3XVVVdp48aNuuGGG3Tttdfq22+/9d9/4YUX6uabb27xNcrJyVF8fLzOOecc/7bs7GzZ7Xb/c+Xk5Oj8889XeHi4f5+pU6dq69atKikpkSStXLlSNptNu3fvliStX79eNTU1ys7O9j9m4MCB6tGjh3JycvzHHTZsmNLS0gKOe/ToUeXm5kqSdu/eLZvNppUrV0qSdu3apUOHDgUcNy4uTmPHjvUft95jjz2mpKQkjRo1Sk8++eRJm2rbq14AQOhwml0ATs+IESP029/+VpL0wAMP6LHHHlNycrJuv/12SdKDDz6oF198UZs2bdK4ceP05z//WaNGjdKjjz7qP8Yrr7yizMxMbdu2TRUVFfJ6vZo5c6Z69uwpSRo2bJh/X7fbraqqKnXr1i2gjvoapLq+affff7/eeOMN/frXv/Zv9/l8euWVVxQTE6PBgwfroosu0tatW/X+++/LbrdrwIABevzxx7VixQqNHTvW/7hrrrlGt912myTp97//vZYuXarnn39ec+fOlST16NFDZ511Vouv0aFDh5Samhqwzel0KjExUYcOHfLv06tXr4B96kPQoUOHlJCQoMjISA0YMEBhYWH+7eHh4U2a5dPS0gKO2zBMNT6uJIWFhWnAgAGKjIwM2N7c4+rvk6Sf//znOvvss5WYmKgvv/xSDzzwgA4ePKinn366xdehPeoFAIQOAmAXNXz4cP//HQ6HkpKSAgJb/Zd3YWGhJGnjxo1asWKFoqOjmxxrx44dmjJliiZNmqRhw4Zp6tSpmjJliq6++molJCSctI4333xTzz33nHbs2OEPkbGxsQH7ZGVlKSYmJqA2h8Mhu90esK2+1nrjx49vcnvDhg3+2wsWLDhpbe1lzJgxysvLa/fjZmRknNZx77vvPv//hw8frvDwcP3bv/2b/vjHPyoiIqI9SwQAhCiagLuo+qtR9Ww2W8A2m80mqe7qmyRVVFRoxowZ2rBhQ8C/7777Tueff74cDoeWLl2qDz74QIMHD9bzzz+vAQMGaNeuXS3WkJOToxtuuEGXXnqpFi9erK+//lq/+c1vVF1d3aZa67fV19peunXr1iRUer1eFRcX+69kduvWrclo2Prbja92NjxudXW1SktLmzzuTI/bcL/mjtucsWPHyuv1+puoO6teAEDXRQC0iLPPPlu5ubnKyspS3759A/5FRUVJqgth5513nn73u9/p66+/Vnh4uN5++21JUnh4uGprawOO+eWXX6pnz576zW9+o3POOUf9+vXTnj172q3mVatWNbk9aNCgVj9+/PjxKi0t1fr16/3bPv74Y/l8Pn9T8/jx4/Xpp5+qpqbGv8/SpUs1YMCAFq9+jh49WmFhYVq+fLl/29atW7V3717/Vcvx48dr8+bNAQF06dKlio2N1eDBg5s9bq9evdStW7eA4x49elSrV69ucjW0oQ0bNshutzdp7u7oegEAXRcB0CLuvPNOFRcX67rrrtPatWu1Y8cOffjhh7rllltUW1ur1atX69FHH9W6deu0d+9eLVq0SEVFRf7AlZWVpU2bNmnr1q06fPiwampq1K9fP+3du1dvvPGGduzYoeeee84fGNvDW2+9pVdeeUXbtm3TQw89pDVr1uiuu+7y3z9nzhw98MADLT5+0KBBmjZtmm6//XatWbNGX3zxhe666y5de+21Sk9PlyRdf/31Cg8P149//GPl5ubqzTff1LPPPhvQzLpmzRoNHDhQ+fn5kuoGZvz4xz/WfffdpxUrVmj9+vW65ZZbNH78eI0bN06SNGXKFA0ePFg33nijNm7cqA8//FC//e1vdeedd/qbafPz8zVw4ECtWbNGUl0Av/fee/XII4/o3Xff1ebNmzVnzhylp6f752DMycnRn/70J23cuFE7d+7U66+/rl/84hf60Y9+5A+sjY/bXvUCAEKI2cOQcUJbpoG55557AvZpbmoQScbbb7/tv71t2zbjyiuvNOLj4w23220MHDjQuPfeew2fz2d88803xtSpU42UlBQjIiLC6N+/v/H888/7H1tYWGhMnjzZiI6ODpgG5le/+pWRlJRkREdHG7NnzzaeeeaZgHOonwbmZOfT3DlJMl544QVj8uTJRkREhJGVlWW8+eabTR5z0003NXm9Gjpy5Ihx3XXXGdHR0UZsbKxxyy23GOXl5QH7bNy40fjBD35gREREGBkZGcZjjz0WcP+KFSsMScauXbv82yorK42f/exnRkJCghEZGWlceeWVxsGDBwMet3v3buOSSy4x3G63kZycbPzyl780ampq/Pfv2rUr4LU0jLqpYP7zP//TSEtLMyIiIoxJkyYZW7du9d+/fv16Y+zYsUZcXJzhcrmMQYMGGY8++qjh8XhOetz2qBcAEDpshmEYZgZQoDk2m01vv/12l1x9BACAYEcTMAAAgMUQAAEAACyGeQARlOiZAABAx+EKIAAAgMUQAAEAACyGAAgAAGAxBEAAAACLIQACAABYDAEQAADAYgiAAAAAFkMABAAAsBgCIAAAgMUQAAEAACyGAAgAAGAxBEAAAACLIQACAABYDAEQAADAYgiAAAAAFkMABAAAsBgCIAAAgMUQAAEAACyGAAgAAGAxBEAAAACL+f87/mFqfnG/6AAAAABJRU5ErkJggg==", + "text/html": [ + "\n", + "
\n", + "
\n", + " Skillcorner\n", + "
\n", + " \n", + "
\n", + " " + ], + "text/plain": [ + "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "skc_client.create_full_visualisation(4320)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "a5baa485", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "56068c4b63b748a3b699b5b8dac8dee6", + "version_major": 2, + "version_minor": 0 + }, + "image/png": "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ghMeHq5Fixbp3HPP1YwZM7RmzZp2qfecc85RUlKSXnrpJXm9Xv/2119/vU1NibNnz9aqVav0yiuv6PDhw6ds/q2trW3SVJqamqr09PSAptY+ffpo1apVqq6u9m9bvHix9u3bF/DY5l731atXKycnp9XnUK+kpKTJz6B+FHF9bQ6HQzabLeBK5O7du5td8SMqKqrZENtYdna2wsPD9dxzzwU8/8svv6yysjL/yGIAXRvTwAAhaMKECUpISNBNN92kn//857LZbHrttdeaBAq73a4XX3xRM2bM0MiRI3XLLbforLPOUl5ennJzc/Xhhx82Obbb7dbixYt18cUX65JLLtEnn3zSqj6EJxMeHq6HH35Yd999ty6++GLNmjVLu3fv1vz589WnT59WX72aNWuW7r//ft1///1KTExs8WpYvfLycnXv3l1XX321RowYoejoaC1btkxr167VU0895d/vtttu08KFCzVt2jTNmjVLO3bs0N/+9rcmU6lcdtllWrRoka688kpNnz5du3bt0l/+8hcNHjzY32zbWq+++qrmzp2rK6+8Un369FF5ebleeuklxcbG6tJLL5UkTZ8+XU8//bSmTZum66+/XoWFhXrhhRfUt29fbdq0KeB4o0eP1rJly/T0008rPT1dvXr10tixY5s8b0pKih544AH97ne/07Rp0/TDH/5QW7du1dy5c3XuueeeciJyAF2EWcOPAbRNS9PADBkypNn9v/jiC2PcuHGG2+020tPTjV//+tfGhx9+aEgyVqxYEbDv559/bkyePNmIiYkxoqKijOHDhwdMqdJwGph6hw8fNgYPHmx069bN+O677/z1NDcNzFtvvRXw2F27dhmSjHnz5gVsf+6554yePXsaERERxpgxY4wvvvjCGD16tDFt2rTWvESGYRjGeeedZ0gybrvttmbvV4PpV6qqqoxf/epXxogRI/znPmLECGPu3LlNHvfUU08ZGRkZRkREhHHeeecZ69ata3K+Pp/PePTRR/3nMGrUKGPx4sXGTTfdZPTs2bPFOgyj6XQsX331lXHdddcZPXr0MCIiIozU1FTjsssuM9atWxdwnJdfftno16+fERERYQwcONCYN2+e8dBDDzX5XcnLyzPOP/98w+12G5L8U8I0ft56f/7zn42BAwcaYWFhRlpamnHHHXc0mSqopd+/5s4XQHCxGQY9dQEE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vHkzIRAAYLpjx45pxIgReuGFF5q9/80339R9992nhx56SF999ZVGjBihqVOnqrCwsE37dDabYRiGac+Ok7r55ptVWlra5K+NCy+8UEOHDpXT6dTf/vY3DRs2TCtWrNDTTz+tefPmaefOnUpMTNSMGTP0xBNPKDo6WpI0f/583Xvvvfrb3/6mX/7yl9q3b58uvfRSLViwQG+99ZYeeughlZWV6cYbb9Qzzzwjh8MhSaqqqtJvfvMb/f3vf1dpaamGDh2qxx9/XBdeeKEkac+ePbrrrrv0+eefq7q6WllZWXryySd16aWX+muuqanRqlWrFBcXp6FDh8pub/lvD5/Ppy1btqikpEQJCQkn3RcAgNMxdOjQNj/GZrPp7bff1hVXXOHfNnbsWJ177rn685//LKnuOywzM1N33323/uM//qPV+3Q2vlm7qFdffVXh4eH64osv9Je//EWSZLfb9dxzzyk3N1evvvqqPv74Y/36178OeNzx48f13HPP6Y033tCSJUu0cuVKXXnllXr//ff1/vvv67XXXtP//M//aOHChf7H3HXXXcrJydEbb7yhTZs26ZprrtG0adP03XffSZLuvPNOVVVV6dNPP9XmzZv1+OOP+0NnvYKCAvl8Pg0YMOCUgc5ut2vAgAEyDENVVVXt8XIBANDuqqurtX79emVnZ/u32e12ZWdnKycnp9X7mIFBIEFu8eLFAWHqkksukST169dPTzzxRMC+9957r///WVlZeuSRR/TTn/5Uc+fO9W+vqanRiy++qD59+kiSrr76ar322msqKCjw98m76KKLtGLFCs2ePVt79+7VvHnztHfvXqWnp0uS7r//fi1ZskTz5s3To48+qr179+qqq67SsGHDJEm9e/cOqMswDOXn5ys5OVkRERGtOu+IiAilpKSouLhYLpeL0cEAgKBz+PBh1dbWKi0tLWB7Wlqa8vLyWr2PGQiAQe6iiy7Siy++6L8dFRWl6667TqNHj26y77Jly/THP/5ReXl5Onr0qLxerzwej44fP67IyEhJUmRkpD/8SXW/gFlZWQEhMy0tzd8vYfPmzaqtrVX//v0DnquqqkpJSUmSpJ///Oe644479NFHHyk7O1tXXXWVhg8f7t+3pqZGlZWVTfounkpKSoqKiopkGAYBEACAdkQADHJRUVHq27dvs9sb2r17ty677DLdcccd+sMf/qDExER9/vnn+vGPf6zq6mp/AGw8vYrNZmt2m8/nkyRVVFTI4XBo/fr1/j6B9epD42233aapU6fqvffe00cffaQ//vGPeuqpp3T33XdLkmprayVJTmfbft3q96ebKgAgGCUnJ8vhcKigoCBge0FBgbp169bqfcxAH8AQsX79evl8Pj311FMaN26c+vfvrwMHDpzxcUeNGqXa2loVFhaqb9++Af8a/uJmZmbqpz/9qRYtWqRf/vKXeumll/z31QdHr9fbpueu35+rfwCAYBQeHq7Ro0dr+fLl/m0+n0/Lly/X+PHjW72PGbgCGCL69u2rmpoaPf/885oxY0bA4JAz0b9/f91www2aM2eOnnrqKY0aNUpFRUVavny5hg8frunTp+vee+/VJZdcov79+6ukpEQrVqzQoEGD/McICwuT2+1WUVGRUlNTW/3cRUVFcjgcBEAAgGkqKioC5t/dtWuXNmzYoMTERPXo0UP33XefbrrpJp1zzjkaM2aM/vSnP+nYsWO65ZZb/I9pzT6djQAYIkaMGKGnn35ajz/+uB544AGdf/75+uMf/6g5c+ac8bHnzZunRx55RL/85S/9gznGjRunyy67TFJdE++dd96p/fv3KzY2VtOmTdMzzzzjf7zNZlNGRoZ27NihqqqqVg0Eqaqq0uHDh9WnTx917979jM8BAIDTsW7dOl100UX+2/fdd58k6aabbtL8+fM1e/ZsFRUV6cEHH9ShQ4c0cuRILVmyJGDQR2v26WzMA4hOcTrzAJaVlWncuHEsCwcAQDujDyA6RVhYmIYMGaKSkhJt2bKlxfn9qqqq/JNADxkyhPAHAEAH4AogOlVxcbFyc3Pl8/mUnJyslJQUOZ1Oeb1eFRUV6fDhw7Lb7RoyZIgSExPNLhcAgJBEAESnq6mpUUFBgfLz81VZWenf7na7lZGRoW7durV5yhgAANB6BECYxjAMeb1eeb1eOZ1OOZ1ORvwCANAJCIAAAAAWwyAQAAAAiyEAAgAAWAwBEAAAwGIIgAAAABZDAAQAALAYAiAAAIDFEAABAAAshgAIAABgMQRAAAAAiyEAAgAAWAwBsAu4+eabdcUVV5hdBgAACBEEQJPZbLaT/nv44Yf17LPPav78+abW2VVDaHFxsW644QbFxsYqPj5eP/7xj1VRURGwz6ZNmzRx4kS5XC5lZmbqiSeeOOVxPR6P7rzzTiUlJSk6OlpXXXWVCgoKAvbZu3evpk+frsjISKWmpupXv/qVvF7vSY9rGIYefPBBnXXWWXK73crOztZ3330XsE9WVlaT35PHHnvMlHoBAF0TAdBkBw8e9P/705/+pNjY2IBt999/v+Li4hQfH292qV3SDTfcoNzcXC1dulSLFy/Wp59+qp/85Cf++48ePaopU6aoZ8+eWr9+vZ588kk9/PDD+utf/3rS4/7iF7/Qv/71L7311lv65JNPdODAAc2cOdN/f21traZPn67q6mp9+eWXevXVVzV//nw9+OCDJz3uE088oeeee05/+ctftHr1akVFRWnq1KnyeDwB+/3Xf/1XwO/J3XffbUq9AIAuykDQmDdvnhEXF9dk+0033WRcfvnl/tsXXHCBcddddxn33HOPER8fb6Smphp//etfjYqKCuPmm282oqOjjT59+hjvv/9+wHE2b95sTJs2zYiKijJSU1ONH/3oR0ZRUZH//rfeessYOnSo4XK5jMTERGPSpElGRUWF8dBDDxmSAv6tWLHCMAzD+PWvf23069fPcLvdRq9evYzf/va3RnV1tf+YDz30kDFixAjj5ZdfNjIzM42oqCjjjjvuMLxer/H4448baWlpRkpKivHII48E1CrJmDt3rjFt2jTD5XIZvXr1Mt566602vZ7ffPONIclYu3atf9sHH3xg2Gw2Iz8/3zAMw5g7d66RkJBgVFVV+ff593//d2PAgAEtHre0tNQICwsLqOfbb781JBk5OTmGYRjG+++/b9jtduPQoUP+fV588UUjNjY24Lka8vl8Rrdu3Ywnn3wy4LkiIiKMv//97/5tPXv2NJ555plWvgodVy8AoOviCmAX9eqrryo5OVlr1qzR3XffrTvuuEPXXHONJkyYoK+++kpTpkzRjTfeqOPHj0uSSktLdfHFF2vUqFFat26dlixZooKCAs2aNUtS3ZXI6667Trfeequ+/fZbrVy5UjNnzpRhGLr//vs1a9YsTZs2zX/FacKECZKkmJgYzZ8/X998842effZZvfTSS3rmmWcCat2xY4c++OADLVmyRH//+9/18ssva/r06dq/f78++eQTPf744/rtb3+r1atXBzzuP//zP3XVVVdp48aNuuGGG3Tttdfq22+/9d9/4YUX6uabb27xNcrJyVF8fLzOOecc/7bs7GzZ7Xb/c+Xk5Oj8889XeHi4f5+pU6dq69atKikpkSStXLlSNptNu3fvliStX79eNTU1ys7O9j9m4MCB6tGjh3JycvzHHTZsmNLS0gKOe/ToUeXm5kqSdu/eLZvNppUrV0qSdu3apUOHDgUcNy4uTmPHjvUft95jjz2mpKQkjRo1Sk8++eRJm2rbq14AQOhwml0ATs+IESP029/+VpL0wAMP6LHHHlNycrJuv/12SdKDDz6oF198UZs2bdK4ceP05z//WaNGjdKjjz7qP8Yrr7yizMxMbdu2TRUVFfJ6vZo5c6Z69uwpSRo2bJh/X7fbraqqKnXr1i2gjvoapLq+affff7/eeOMN/frXv/Zv9/l8euWVVxQTE6PBgwfroosu0tatW/X+++/LbrdrwIABevzxx7VixQqNHTvW/7hrrrlGt912myTp97//vZYuXarnn39ec+fOlST16NFDZ511Vouv0aFDh5Samhqwzel0KjExUYcOHfLv06tXr4B96kPQoUOHlJCQoMjISA0YMEBhYWH+7eHh4U2a5dPS0gKO2zBMNT6uJIWFhWnAgAGKjIwM2N7c4+rvk6Sf//znOvvss5WYmKgvv/xSDzzwgA4ePKinn366xdehPeoFAIQOAmAXNXz4cP//HQ6HkpKSAgJb/Zd3YWGhJGnjxo1asWKFoqOjmxxrx44dmjJliiZNmqRhw4Zp6tSpmjJliq6++molJCSctI4333xTzz33nHbs2OEPkbGxsQH7ZGVlKSYmJqA2h8Mhu90esK2+1nrjx49vcnvDhg3+2wsWLDhpbe1lzJgxysvLa/fjZmRknNZx77vvPv//hw8frvDwcP3bv/2b/vjHPyoiIqI9SwQAhCiagLuo+qtR9Ww2W8A2m80mqe7qmyRVVFRoxowZ2rBhQ8C/7777Tueff74cDoeWLl2qDz74QIMHD9bzzz+vAQMGaNeuXS3WkJOToxtuuEGXXnqpFi9erK+//lq/+c1vVF1d3aZa67fV19peunXr1iRUer1eFRcX+69kduvWrclo2Prbja92NjxudXW1SktLmzzuTI/bcL/mjtucsWPHyuv1+puoO6teAEDXRQC0iLPPPlu5ubnKyspS3759A/5FRUVJqgth5513nn73u9/p66+/Vnh4uN5++21JUnh4uGprawOO+eWXX6pnz576zW9+o3POOUf9+vXTnj172q3mVatWNbk9aNCgVj9+/PjxKi0t1fr16/3bPv74Y/l8Pn9T8/jx4/Xpp5+qpqbGv8/SpUs1YMCAFq9+jh49WmFhYVq+fLl/29atW7V3717/Vcvx48dr8+bNAQF06dKlio2N1eDBg5s9bq9evdStW7eA4x49elSrV69ucjW0oQ0bNshutzdp7u7oegEAXRcB0CLuvPNOFRcX67rrrtPatWu1Y8cOffjhh7rllltUW1ur1atX69FHH9W6deu0d+9eLVq0SEVFRf7AlZWVpU2bNmnr1q06fPiwampq1K9fP+3du1dvvPGGduzYoeeee84fGNvDW2+9pVdeeUXbtm3TQw89pDVr1uiuu+7y3z9nzhw98MADLT5+0KBBmjZtmm6//XatWbNGX3zxhe666y5de+21Sk9PlyRdf/31Cg8P149//GPl5ubqzTff1LPPPhvQzLpmzRoNHDhQ+fn5kuoGZvz4xz/WfffdpxUrVmj9+vW65ZZbNH78eI0bN06SNGXKFA0ePFg33nijNm7cqA8//FC//e1vdeedd/qbafPz8zVw4ECtWbNGUl0Av/fee/XII4/o3Xff1ebNmzVnzhylp6f752DMycnRn/70J23cuFE7d+7U66+/rl/84hf60Y9+5A+sjY/bXvUCAEKI2cOQcUJbpoG55557AvZpbmoQScbbb7/tv71t2zbjyiuvNOLj4w23220MHDjQuPfeew2fz2d88803xtSpU42UlBQjIiLC6N+/v/H888/7H1tYWGhMnjzZiI6ODpgG5le/+pWRlJRkREdHG7NnzzaeeeaZgHOonwbmZOfT3DlJMl544QVj8uTJRkREhJGVlWW8+eabTR5z0003NXm9Gjpy5Ihx3XXXGdHR0UZsbKxxyy23GOXl5QH7bNy40fjBD35gREREGBkZGcZjjz0WcP+KFSsMScauXbv82yorK42f/exnRkJCghEZGWlceeWVxsGDBwMet3v3buOSSy4x3G63kZycbPzyl780ampq/Pfv2rUr4LU0jLqpYP7zP//TSEtLMyIiIoxJkyYZW7du9d+/fv16Y+zYsUZcXJzhcrmMQYMGGY8++qjh8XhOetz2qBcAEDpshmEYZgZQoDk2m01vv/12l1x9BACAYEcTMAAAgMUQAAEAACyGeQARlOiZAABAx+EKIAAAgMUQAAEAACyGAAgAAGAxBEAAAACLIQACAABYDAEQAADAYgiAAAAAFkMABAAAsBgCIAAAgMUQAAEAACyGAAgAAGAxBEAAAACLIQACAABYDAEQAADAYgiAAAAAFkMABAAAsBgCIAAAgMUQAAEAACyGAAgAAGAxBEAAAACL+f87/mFqfnG/6AAAAABJRU5ErkJggg==", + "text/html": [ + "\n", + "
\n", + "
\n", + " Skillcorner\n", + "
\n", + " \n", + "
\n", + " " + ], + "text/plain": [ + "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "tracking_data = skc_client.get_match_tracking_data(4320)\n", + "skc_client.create_full_visualisation(tracking_data)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "8dfae210", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "43a51d3d0ea642019c8473dcdd6ede7f", + "version_major": 2, + "version_minor": 0 + }, + "image/png": "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hRo/frx+9atf6YEHHpDD4dCll16qpUuX+vd5+eWXFRUVpWnTpunll1/Wyy+/rO9973utPvZ9992nW2+9VampqXr00Ue1YMECPfPMM8rOzlZtbW3AvkePHtXcuXM1duxYPfrooxo+fLh+8pOf6J133mnzOd5444265557dNppp+n3v/+9zj77bP32t78NeL9efvllTZs2TVFRUf7ap0+f3uLxHn/8cV188cWS6n+fXn75Zc2fP9//861bt+qKK67Q7NmztXDhQv9kkqefflqZmZn62c9+pkcffVQZGRm65ZZb9NRTTzV7jO3bt+vKK6/UhRdeqN/+9rc6evSoLrzwQr3yyiu6/fbb9e1vf1v333+/duzYocsuu0w+n89/302bNmny5MnavHmzfvrTn+rRRx9VTEyMLrroIr355psBjzNz5kzNnDmzzdewwf3336/Y2Fi5XC6dfvrpzYZj7N+/XwUFBZo4cWKz+06aNCkgrH7++eeKiYlpNrxk0qRJ/p8DIcUAQtitt95qNP01Pvvssw1Jxh//+Mdm+1dWVjbb9r3vfc+Ijo42PB6PYRiG4fV6jaysLCMzM9M4evRowL4+n8///2uvvdaIiYkxDMMwPvroIyM+Pt6YN2+e/ziN6zn77LP9t1esWGFIMkaMGGFUV1f7ty9cuNCQZGzYsMEwDMOorq42kpKSjNNPP92ora317/fiiy8akgKO2ZKtW7cakownn3wyYPstt9xixMbGBrwWkox7773XfzshIcG49dZbT3j8zMxM49prr222venz9Xq9Ac/TMAzj6NGjRt++fY0bbrghYHvTOl544QVDkrFr1y7DMAzjzTffNCQZn3zyyQlra/o+19TUGKNGjTJmzJgRsD0mJqbF59D0cQsKCgyn02lkZ2cbdXV1/v3+8Ic/GJKM559/PuD5SzIWL17s31ZdXW3069fPWLBgwQnrzsvLMyQZN954Y8D2O+64w5BkvP/++/5tjX//2nLvvfcakozCwsKA7ZmZmYYk49133212n5Y+K3PmzDEGDRrU4jE+/vhj/7Zly5YZkgy3223s2bPHv/2ZZ54xJBkrVqzwb5s5c6YxevTogM+Nz+czpk6dagwZMqTZY2VmZrb5fPfs2WNkZ2cbTz/9tPGvf/3LePzxx40BAwYYdrvdeOutt/z7ffLJJ83eqwZ33nmnIclf17x585o9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j3/8o9LS0jR16tQ26wKCCS2AsIypU6eqd+/euvbaa/WDH/xANptNL7/8crNAYbfb9fTTT+vCCy/UuHHjdP3116t///7asmWLNm3apGXLljU7ttvt1ltvvaUZM2bovPPO03//+992jSE8EafTqfvuu0//8z//oxkzZuiyyy7T7t279eKLL2rw4MHtbr267LLLdMcdd+iOO+5QYmJimy0tZWVlSk9P1yWXXKKxY8cqNjZWy5cv1yeffKJHH33Uv9+NN96oN954Q3PnztVll12mHTt26M9//rO/BbXBBRdcoCVLlujiiy/WvHnztGvXLv3xj3/UyJEj/d227fXSSy9p0aJFuvjiizV48GCVlZXp2WefVXx8vD88zZs3T4899pjmzp2rK6+8UgUFBXrqqad0yimn6Isvvgg43oQJE7R8+XI99thjSk1NVVZWln+iQmPJycm6++67df/992vu3Ln6xje+oa1bt2rRokU6/fTT21xsuL3Gjh2ra6+9Vn/605/8QxbWrl2rl156SRdddFGL6zp2h+zsbDmdTl144YX63ve+p/Lycj377LNKSUlpMah1xlNPPaWzzjpLo0eP1k033aRBgwbp66+/Vm5urvLz87V+/Xr/vg1LwLQ1EeSuu+7yd2OnpqZq9+7deuaZZ1RRUdFsLc+f/exnev3113Xuuefqhz/8ocrLy/XII49o9OjRARPJ0tPT9aMf/UiPPPKIamtrdfrpp+sf//iHPvzwQ73yyissAo3QY8LMY6DLtLYMzKmnntri/qtWrTImT55suN1uIzU11bjrrrv8S1Y0XprCMOqXdpk9e7YRFxdnxMTEGGPGjAlYUqWlZTgOHz5sjBw50ujXr5+xbds2fz0tLQPz+uuvB9x3165dhiTjhRdeCNj+xBNPGJmZmUZUVJQxadIkY9WqVcaECROMuXPntuclMgzDMM4888wWlxdpoEbLr1RXVxt33nmnMXbsWP9zHzt2rLFo0aJm93v00UeNtLQ0IyoqyjjzzDONTz/9tNnz9fl8xgMPPOB/DuPHjzfeeust49prr222pIfaWAbms88+M6644gpjwIABRlRUlJGSkmJccMEFxqeffhpwnOeee84YMmSIERUVZQwfPtx44YUX/EuhNLZlyxZj+vTphtvtNiT5l4Rp+rgN/vCHPxjDhw83IiMjjb59+xo333xzs6WCWvv9a+n5tqS2tta4//77jaysLCMyMtLIyMgw7r777mbLC3XVMjDz5s1r8T7/+te/jDFjxhgul8sYOHCg8dBDDxnPP/98s9eltWNIaraUUMPv+COPPBKwfceOHcY111xj9OvXz4iMjDTS0tKMCy64wHjjjTea1due1/Avf/mLMX36dCM5OdlwOBxGnz59jIsvvthYt25di/tv3LjRyM7ONqKjo41evXoZV111lXHo0KFm+9XV1fl/l51Op3Hqqacaf/7zn9usBwhGNsPooVHRALqEz+dTcnKy5s+fr2effdbscgAAIYgxgEAQ83g8zbqoFy9erKKioi6bbAIAsB5aAIEgtnLlSt1+++269NJLlZSUpM8++0zPPfecRowYoXXr1nVo3UEAABowCQQIYgMHDlRGRoaeeOIJFRUVKTExUddcc40efPBBwh8A4KTRAggAAGAxjAEEAACwGAIgAACAxRAAAQAALIYACAAAYDEEQAAAAIshAAIAAFgMARAAAMBiCIAAAAAWQwAEAACwGAIgAACAxRAAAQAALIYACAAAYDEEQAAAAIshAAIAAFgMARAAAMBiCIAAAAAWQwAEAACwGAIgAACAxRAAAQAALIYACAAAYDEEQAAAAIshAAIAAFgMARAAAMBiCIAAAAAWQwAEAACwGAIgAACAxRAAAQAALIYACAAAYDEOswsIZT6fTwcOHFBcXJxsNpvZ5QAAgHYwDENlZWVKTU2V3W7NtjACYCccOHBAGRkZZpcBAABOwr59+5Senm52GaYgAHZCXFycpPpfoPj4eJOrAQAA7VFaWqqMjAz/97gVEQA7oaHbNz4+ngAIAECIsfLwLWt2fAMAAFgYARAAAMBiCIAAAAAWQwAEAACwGAIgAACAxRAAAQAALIYACAAAYDEEQAAAAIshAAIAAFgMARAAAMBiCIAAAAAWQwAEAACwGAIgAACAxTjMLgCt+9O6P5ldAgAApvnuhO+aXULYogUQAADAYgiAAAAAFkMABAAAsBgCIAAAgMUQAAEAACyGAAgAAGAxBEAAAACLIQACAABYDAEQAADAYgiAAAAAFkMABAAAsBgCIAAAgMUQAAEAACyGAAgAAGAxBEAAAACLcZhdALqHy+GSJHl9XpMrAUKTw15/euQzBJychs+Qx+sxuRK0hAAYhlwOl64Ze43ZZQAAoMXrFxMCgxBdwAAAABZDC2AYatxltXj9YrqwEPZ8hpR/2KkKT4RiXHVK71Mju+3kj+ewO/yt6HyGgI5r/Bni8xOcCIBhzuvz8uFDWNuS71JOXoLKqo6fzuLcXs0eV6Lh6Z3vduIzBCAc0QUMIGRtyXdpSW6iyqoiAraXVUVoSW6ituS7TKoMAIIbARBASPIZUk5ewrFbTft7628vz0uQz+jRsgAgJBAAAYSkfYXOY92+rQ32s6m0yqF9hc6eLAsAQgIBEEBIKvdEtL1TB/YDACshAAIISbGuui7dDwCshAAIICRlJNcozu2V1NogP0Pxbq8ykmt6siwACAkEQAAhyW6TZo8rOXaraQisvz1rXEmn1gMEgHBFAAQQsoanezR/SpHi3IHdvPHuOs2fUtQl6wACQDhiIWgAIW14ukdD0zzaV+hUuSdCsa46ZSR37kogABDuCIAAQp7dJmWmMNYPANqLLmAAAACLIQACAABYDAEQAADAYhgDCCAo+QwxsQMAugkBEEDQ2ZLvUk5ewrFr/daLc3s1e1wJS7sAQBegCxhAUNmS79KS3ESVVQVew7esKkJLchO1Jd9lUmUAED4IgACChs+QcvISjt1q2t9bf3t5XoJ8rV39DQDQLgRAAEFjX6HzWLdva4P9bCqtcmhfobMnywKAsEMABBA0yj0Rbe/Ugf0AAC0jAAIIGrGuurZ36sB+AICWEQABBI2M5BrFub2SWhvkZyje7VVGMpd9A4DOIAACCBp2mzR7XMmxW01DYP3tWeNKWA+wi/gMaU+BU5v2urWnwHnCyTUd2RdA8GMdQABBZXi6R/OnFDVbBzDeXadZrAPYZTqy1iLrMgLhhwAIIOgMT/doaJqHK4F0k4a1FptqWGtx/pQif7DryL4AQgcBEEBQstukzBTG+nW1ttdaNJSTl6CoSJ8qPBFt7rs8L0FD0zyEcyDEEAABwEKOr7XYGpvKqhz66wfJ7Tja8XUZCetAaGESCABYSHesoci6jEDoIQACgIV0xxqKrMsIhB4CIABYSNtrLXYE6zICoYoACAAWcuK1FjuCdRmBUEYABACLaVhrMc598l238e46loABQhizgAHAgpqutRgdVad/r+19bEJHS016hqKjfJo1tkRxbtZlBEIdARAALKrpWovZ40uOLfpsKDAE1nf3zj2tmBY/IEzQBQwAkNR61zDdvUD4oQUQAODHZfgAayAAAgACcBk+IPzRBQwAAGAxBEAAAACLIQACAABYDGMAASAE+AwxMQNAlyEAAkCQ25LvUk5egsqqjp+y49xezR5XwtIsAE4KXcAAEMS25Lu0JDdRZVURAdvLqiK0JDdRW/JdJlUGIJQRAAEgSPkMKScv4ditpv299beX5yXIZ/RoWQDCAAEQABrxGdLeQmfAbbPsK3Qe6/ZtbbCfTaVVDu1rVC8AtAcBEACO2ZLv0lNL++q1D/v4t/3p3RTTulnLPRFt79SB/QCgAQEQABScY+1iXXVt79SB/QCgAQEQgOUF61i7jOQaxbm9klp7YEPxbq8ykrlsG4COIQACsLxgHWtnt0mzx5Ucu9U0BNbfnjWuhPUAAXQYARCA5Z3MWDufIe0pcGrTXrf2FDi7rXVweLpH86cUKc4d2M0b767T/ClFrAMI4KSwEDQAy+voWLueXph5eLpHQ9M8XAkEQJehBRCA5XVkrJ1Zk0XsNikzpUanDqhSZgrhD0DnEAABWF57x9pJwTlZBAA6igAIAGrfWLtgnSwCAB3FGEAAOKZhrN2homj/tpvmFshneCWxMDOA8EELIAA0YrdJAxqtq9d4rB0LMwMIFwRAAGgnFmYGEC7oAg5zDrt13+KG5+7xsk4aukbDZJEluYmqD4GNxwKauzCzzxDLxAQRl6N+NrjX5zW5EnNY+bsnVPAOhaGGE48kXTP2GhMrCQ6L1y8mBKLLNEwWaboOYLy7TrO6aR3AtvT0uoQ4MZfDxbm3EZfDpfKacrPLQBMEQADooGBamLlhXcKmGtYl5GohAFpCAAxDjbsc/rLhL5Zs/XLYHf6/wK3aBYPu1bAws5l8RlvrEhpanpegoWkeuoN7UONzzuL1iy15DnI5XLpy9JWSOAcHKwJgmPP6vHz4gDB1fF3C1hxfl9DssGpVVj0HW/E5hxpmAQNAiGJdQgAniwAIACGKdQkBnCwCIACEKNYlBHCyCIAAEKIa1iWs1zQEmrsuIYDgRgAEgBDWsC5hnDuwmzfeXccSMABaxSxgAAhxwbQuIYDQQAAEgDAQDOsSAggddAEDAABYDAEQAADAYgiAAAAAFkMABAAAsBgCIAAAgMUQAAEAACyGZWAAdCmfIdajA4AgRwAE0GW25LuUk5egsqrjp5Y4t1ezx5VwRQoACCJ0AQPoElvyXVqSm6iyqoiA7WVVEVqSm6gt+S6TKgMANEUABNBpPkPKyUs4dqtpf2/97eV5CfIZPVoWAKAVBEAAnbav0Hms27e1wX42lVY5tK/Q2ZNlAQBaQQAE0Gnlnoi2d+rAfgCA7kUABNBpsa66Lt0PANC9CIAAOi0juUZxbq+k1gb5GYp3e5WRXNOTZQEAWkEABNBpdps0e1zJsVtNQ2D97VnjSlgPEACCBAEQQJcYnu7R/ClFinMHdvPGu+s0f0oR6wACQBBhIWgAXWZ4ukdD0zxcCQQAghwBEECXstukzBTG+gFAMKMLGAAAwGIIgAAAABZDAAQAALAYAiAAAIDFEAABAAAshgAIAABgMQRAAAAAiyEAAgAAWAwBEAAAwGIIgAAAABZDAAQAALAYAiAAAIDFEAABAAAshgAIAABgMQRAAAAAi3GYXQAAoOf4DGlfoVPlngjFuuqUkVwju83sqgD0NAIgAFjElnyXcvISVFZ1/NQf5/Zq9rgSDU/3mFgZgJ5GFzAAWMCWfJeW5CaqrCoiYHtZVYSW5CZqS77LpMoAmIEACABhzmdIOXkJx2417e+tv708L0E+o0fLAmAiuoDDnMPukMPeNW9zw3E8XrqKgFCyr9AZ0O3bnE2lVQ7tK3QqM6Wmx+pC8HA56luAvT5vlxyvq7530H14h8JQVESU//9Xjr6yy4+/eP1iQiDChtvhVkpMimKdsYpxxigmMkbuSLf/59mDs1VVW6WK2gpV1FSovKZcBRUFqvJWmVh1x5R7ItreqQP7Iby4HC5dM/aabjt+VEQU3xlBiAAIwFKiIqI0sNdApcWnqW9MX8VFxUmSvL46FVdWqqymQrV1x7+svD6vYpwx6hvbVzGRMYqw14eksuoyfV3xtfaX7tfu4t2qrqs25fm0R6yrrkv3AxD6CIBhqM44fhJ/beNrqqit6PQxHXaH/y/EruoiAHqK3WbXoN6DdEriKUqPT5dNNhVUFGhX8S59vveo/vxRtbYX1Mo4NgYuJd7Q2p8NlCS9v+v9gN/5mMgYpcSkqG9sX/WN6avpmdN11oCztL9sv7YXbdfOozvlM3wmPMvWZSTXKM7tPTYBpKU1XwzFu+uXhIH1NP79Xrx+cZec42MiY3T5qMslBX4nIXgQAMNcnVFHYINlOSOcGtFnhEaljFKMM0YHyw4qd1+udh7dqSpvlX9mbL3jwajpTNnGKmortKt4l3YV75JU34U8qPcgDeo9SDOyZuiMtDO0sWCjviz8UrW+2u58eu1mt0mzx5Uce66GAkNgfeqdNa6E9QAhr8/bJd8ZhL7gRwAEEHbsNrtGp4zW+P7jFWGL0Laibfri6y9U7Cn279OembEN+51IlbdKmwo3aVPhJvVy9dKYvmM0MXWixvcfr88Pfq4NBRuCokVweLpH86cUNVsHMN5dp1msAwhYDgEQQFjJiM/QlIwpio+K16aCTco7lNfihI22Z8bWyz/sVGpS+1pEij3F+mDPB/pk/yca12+cTk87XcP6DFPuvlztK93X4efS1YanezQ0zcOVQAAQAAGEh0h7pM4acJaGJA1Rfmm+3tvxXkCLX1PtnfFacRIzY6u8VcrNz9WWw1s0NWOqzhtynrYd2aaP9n5kerew3SaWegFAAAQQ+pLcSZo1aJbckW69v+t9bS/a3uZ92jvjNaYTM2OPeo5q6balOiXxFJ014CwlxyTrPzv/oyNVR076mGbg+sFA+CEAIuw57A4mwoSxoUlDNW3ANBVVFemd7e+otLq0Xfdre2ZsvfQ+NZ2+Qsb2ou0qqCjQrEGzdNHwi/Th3g/11ZGvOnfQHsL1gzuORZARCrgUHMLe+UPOV3RktNlloBuM7TtW5ww8R9uKtumfW//Z7vAnHZ8ZW69pwjMC9usKpdWl+seWf2hb0TadM/Acje07tmsO3I24fnDHRUdG6/wh55tdBtAmAiDCntvh1jeGfYMQGGYmp0/WGelnaN2BdfpgzwcnNdO2YWZsnDuwmzfe3T1LWPgMnz7Y84HWHVinM9LP0OT0yd3yOF2B6wd3XHRktL4x7Bv+y6oBwYx2aoS9pduW6vwh5+v8Iefr31v/HdRXbED7TEmfolEpo/TR3o/0ZeGXnTpWSzNjs/p277It6w6uU5W3SmdmnCmbbMrNz+3WxzsZXD+4Y6IionT+kPNlt9n19ra3ddmpl5ldEnBCtAAi7JXXlGvpV0vldrh1/pDzFWmPNLskdMK4fuM0uu/oLgl/DRpmxp46oEqZKT0zweHLwi/10d6PNLrv6KDsDub6we0XaY/U+UPOl8vh0tKvlqq8ptzskoA2EQBhCSXVJVq6banio+KVPThbthMM+kfwyuqVpUlpk7TuwDptPrzZ7HI6bfPhzf7u4KxeWWaXE4DrB7ePTTZlD85WfFS83t72tkqqS9q+ExAECICwjKKqIr234z31j+uvCakTzC4HHZQcnaxzs87V9qLtWndwndnldJl1B9dpe9F2nZt1rpKjk80ux69hlnTzCTINDMW7vZa/fvCE1AnqH9df7+14T0VVRWaXA7QbARCWcrD8oD498KnG9xuvtLg0s8tBOzkjnMoenK3DlYf1393/NbucLvff3f/Vkcojyh6cLWeE0+xyJLVvlrTVrx+cHp+u8f3G69MDn+pg+UGzywE6hAAIy8k7lKf80nzNyJrBzOAQMTF1oiIjIrV85/KwvMh8nVGnnJ05ioyI1IT+wdM6faJZ0vOnFFl6HcDoyGidO/Bc7Svdp7xDeWaXA3QYs4BhSSt2r9CCEQt0ZsaZytmZY3Y5OIEkd5JGJo/Umvw1qqytNLucblNZW+kfD7j1yNag6U7k+sEtOzPjTPkMn1buXml2KcBJoQUQluTxerQ6f7WyemfRFRzkzhxwpoo9xdpYsNHsUrrdxoKNKvYU66wBZ5ldSgAzZkkHs7S4NGX1ztLq/NXyeK3bCorQRgCEZe04ukMHyg5oasZUZgUHqaFJQ9Uvtp9W7V0lo9XJCOHDkKFVe1epX2w/DUkcYnY5aIHdZtfUjKk6UHZAO47uMLsc4KQRAGFpH+/7WAmuBI1KGWV2KWjCGeHUGWlnaNuRbZYaYH+w/KC2F23X5PTJQTMhBMedmnyqElwJ+njfx2aXAnQKARCWVlRVpM2FmzW+/3gu4B5kTk89XRH2CK3Zv8bsUnrc6vzVirBHaGLqRLNLQSMOu0Pj+4/X5sLNQTNGEzhZBECEJJ8h7SlwatNet/YUODt1PdK8Q3lyRjg1os+IrisQneJ2uDUieYQ+P/h5WE/8aE1lbaU+P/i5RiaPlNvhNrscHDOizwg5I5w9Puu3K893QAOaPEJUw8XGvT5vs59F2ML70kxb8l3KyUsIuE5pnNur2eNKTmpZioraCm0v2q7RfUdrY8FGS4w1C3anJJ4iwzC05fAWs0sxzZbDWzQxdaJOSTxFGwo2mF2O5dlk0+i+o7XtyDZV1Fb02ON29fnODBG2iBZ7WBq2MZHGHATAEORyuHTN2Gvata/THl5jiLbku7QkN7HZ9rKqCC3JTTzptck2fL1BQ5OGamCvgdpVvKsrSkUnDOszTLuLd6u6rtrsUkxTXVetPSV7NDRpKAEwCAzsNVCxztgenY3eXee7ntD4u+fyUZefcN/F6xcTAk1AFzBChs+QcvISjt1qOmu3/vbyvIST6h45UnVEh8oPaXif4Z2qEZ2X5E5SojtRXx35yuxSTLf18FYlRScpyZ1kdimWNyJ5hA6VH9KRqiM98njdeb4DJFoAQ1Ljbt/F6xc36waOd8brklMvkaSwumrCvkJnQDdIczaVVjm0r9Cpwf18HT7+9qLtmpoxVVERUZZueTLb0KShqqytVH5pvtmlmC6/NF+VtZUamjRUufm5ZpdjWVERUUqNS9Wqvat67DE7cr7LTAm+6zE3/u55Y9MbKq0pDfi5w+7w92S1NJQJ3Y8WwBDn9Xmb/Qun0NdYuad9Yxvbu19Tu47ukk02ZfbKPKn7o/PsNruGJA3RtiPbGIup+nUBtx3ZplMST5HdxunaLJm9MmWTTbuLd/fYY3b3+a4n1Rl1LX5XwVycURAyYl3tC7bt3a+pKm+VDpUfUlavrJO6PzpvQMIAuRwuun8b+erIV3JHupURn2F2KZY1qPcgHSo/pCpvVY89Znef7wACIEJGRnKN4txeqdWWIUPxbq8ykk++O2RX8S6lx6cr0h550sfAyRvce7AKKwp11HO02c+suhTGUc9RFVYU6pTEU8wuxZIi7ZFKi0vTzqM7e/Rxe+J8B2tjDCBCht0mzR5XcmxWnKHAgdH1J8lZ40o6dZ3S/NJ8Tc2YquSYZB0oO9CZcnES+kT30Z6SPc22h8NSGJ1xqPyQMhJoATRDSkyKIuwR2l+2v0cftyfOd7A2WgARUoanezR/SpHi3IHdHvHuui5ZEqHYU6yauhqlxKR06jjoOIfdofio+GZXWGhYCqOsKnCsU8NSGFvyXT1ZpimOVB1RQlQCV6sxQXJMsqq91Sr2FPf4Y3f3+Q7WxtkEIWd4ukdD0zzaV+hUuSdCsa46ZSTXdNlfwgUVBQRAEyS6E2Wz2XSk8vgyG20vhWFoeV6ChqZ5wrolpKiqSDabTb1dvVVYWWh2OZaSEpNi6mve3ec7WBcBECHJblO3LX1QWFGoYX2Gdcuxw53P0El/USW6E+UzfAEtLaG+FEZXOVp1VD7Dp6ToJAJgD0uJSdHWw1tNraE7z3ewLgIg0MThysMaHzleLoeL1ek7oLPj9JLcSSrxlAQsYxROS2F0Rp1RpxJPiRLdza8Kge7jdrgVHRmtw5WHzS4F6HKMAQSaKK8plyTFRMaYXEno6IpxeonuxGZXWWApjOOKqoq4IkgPi3HWnwPKaspMrgToegRAoImGC703nPxxYl11yaqk6KRmE0BYCuO4I1VHaAHsYQ1/BFbUVJhcCdD1CIBAE1W1VfIZPloA2+n4OL3WBvsdH6fXGpfDJWeEs9lMy4alMOo1DYHWWgqj2FOsKEeUXI7wn/UcLKIjo+UzfAwFQVgiACIoRNiCZwyXIUOVtZW0ALZTV4zTa1jepLauttnPWAqjXsNrw1IwPSfGGaPK2sqguixhMJ0rEdo4k8BU8VHxmjdknuKi4nSw7KDe3f6uan3NQ0BPq/ZWyxnReosVjuuKcXoNX2qtXce6p5fCGNFnRMD/NxRs6J4H6oCG1yacAkBnZo33hKiIKFV7q80uQ1L9FUnmnjJX/eP6q6y6TEu3LVVpdanZZSGEEQBhqmkDpvlb2vrG9tX4/uO1dv9ak6uSfIZPdhsN5O3RME6vfgJIS9/ehuLddSccpxdhPxYAfa2HxJ5aCiM+Kl6T0yf7b09On6w9JXtM/7JteG0aXqtQFwpXd7HZbPIZPrPLkCSN7z9efWP7SqpvmZw2YJqWbltqclUIZXzDwVQJrgR/0LLJpjhnnMkV1TNkyNbqmDY01hXj9Bp+B4LhyzbOGSeb7XixNltw/F42vDbh8IdJqFzdxSZb0HT/xkfF+89JdptdCa6ENu4BnFjon0kQ0g6VHQr40i+oKDCxmuPsNntQhJFQ0dlxeg2tW8EQboqqiuT1ef23vT5vs9nJZmhPK2ko6KpZ4z0hmP4Q/Lr8a///fYZPB8sOmlgNwgFdwDDVR/s+kt1uV6I7UXtL9mpjwUazS5JUH0SC5S//UNGZcXoNYTsYJjhUeauUsyNH5w05T5KUsyNHVd4qk6tqe5xkqAilq7v4DF/QdLlvLNioGGeMBiQMUFFVkVbtW2V2SQhx5p9tYWk1dTVavnO52WU0w1VATs7JjtNraHELhhZASTpYfrDF/5spXFoAQ+nqLh6vJ2iW3TFkaHX+aq3OX212KQgTwXG2BYKI3WZXdGQ0i7/2oIaZ31GOKJMrCV4Ns9KDYZZ8Z4TS1V0qairkdriDphsY6EoEQKCJ6MhoScevCILO8RnSngKnNu11a0+Bs8WxXTV1NaqsrVRvV++eLzBEJLoTVVFToZq60L7qSShd3aWytlI2m81/TgDCCV3AQBNc/qnrdGSpjyOVR5QUzbVuW5Pkbn6pvFDUMGt8SW6i6kNg49a14Lq6S+PLQvIHIcINLYBAE3FR9Ut+cMLvnI4u9VFUVcS1bk8g0Z0YFgFQCp2ru5TXlEuqX4IFCDe0AAJNJEcnq8RTEvJdbWZqe6kPQ8vzEjQ0zeNv6SmqKtLYfmMVaY8M+XFuXS3SHqm4qDgdqTpidildpqev7nIyaupqVOIpUXJ0srYXbTe7HKBLEQCBJlJiUoJmPcJQdTJLfTSEm0R3or6u+PoE97WehpbRI5XhEwClnru6S2cUVhYqOSbZ7DKALkcXMNCI3WZXUnQSAbCTTmapj2JPsXyGj27gFiRFJ6nOV6eS6pK2d0aXKqgoUJ/oPkGzRBHQVfiNBhpJcifJYXeosLLQ7FJC2sks9eEzfDpadZSJIC1IdCf6AzJ6VkFFgRx2h5Lc/F4ivBAAgUYGJAxQTV2NDlceNruUkHayS30UVBQoPT692+sLNenx6bRKm+Rw5WHV1NUoIyHD7FKALkUABBrJ6p2lPcV7aGnppIalPuo1DYGtL/WxrWib4qPi1S+2X7fXGCr6x/ZXfFS8thVtM7sUS/IZPu0p3qNBvQeZXQrQpQiAwDEJUQlKdCdq59GdZpcSFk5mqY9D5YdUWl2qYUnDeqrMoDc0aahKq0t1qPyQ2aVY1q7iXUp0JyohKqHtnYEQwSzgEOewN38LGy4aj44Z1HuQautqlV+ab3YpYeNklvr46shXGtN3jFbtW+W/RrBVOewODeo9SHmH8swuxdL2lexTbV2tsnpn8V6chAhbRLPvqpa+u9CzeAdCUOMPzjVjrznhvoTB9huSNER7SvaozjD/GqThpKNLfXx15CtNTJ2orF5Zlu/2zOqVpciISMu/DmarM+q0p2SPhiYNJQC2U+PvnktOveSE+zrsDsv/sWcGuoAB1Q+y7+XqpS8LvzS7FMsrrynXgbIDGpo01OxSTDc0aaj2l+73X5EC5vmy8Ev1cvVSWlya2aUAXYIWwBDk8Xq0eP1iSWrxr6aYyBhdPupySVKNL7gXWQ0Wo1NG63DlYcZZBYmth7fq3KxzFeuMtWz4iXXGKi0+TSt2rTC7FKh+fOrhysMa03eM9pftN7ucoNf4u+e1ja+1eGnNht4sjzc4Lv1nNbQAhiiP1yOP1yOvz9vsH12YHZPkTlJGQobWH1pvdik4ZlfxLtXU1Wh0ymizSzHN6JTRqqmr0a7iXWaXgmPWH1qvjIQM1gTsoDqjrsXvqobvMZiDAAjLm5g6UaXVpcz+DSJen1efH/xcp6acaskrgyS6E3Vqyqn67OBnjI0KIjuP7lRpdakmpk40uxSg0wiAsLSM+Axl9srUmvw1MlpdtBhm2FCwQSWeEp2ZcabZpfS4swacpRJPiTYWbDS7FDRiyNCa/DXK7JWpjHgWhkZoIwDCsuw2u6ZkTNH+0v10swUhn+HTqn2r1D+uv4YkDjG7nB4zJHGI+sX206p9q1iQPAjtKt6l/aX7NSVjCtcHRkjjtxeWNTpltOKj4vXxvo/NLgWtOFB2QDuKduiM9DPkjHCaXU63c0Y4NTl9srYXbdeBsgNml4NWfLzvY8VHxVt6jCpCHwEQltTb1VsTUidoU8EmHfUcNbscnEBufq4i7ZGWGHc1MXWiIuwRWp2/2uxScAJHPUe1qWCTJqROUG9Xb7PLAU4KARCW47A7NGvQLJVWl2rt/rVml4M2VNZWat3BdRqZPFJ9ovuYXU636RPdRyOTR2rdgXWqrK00uxy0Ye3+tSqtLtXMQTO5qgVCEgEQlnPWgLMU64xVzo4clswJERu+3qAjlUeUPThbbofb7HK6nNvhVvbgbB2pPMLEjxBRZ9Rp+c7linPGWXKiEkIfARCWMrzPcA1NGqoP936okuoSs8tBOxkytGzHMtlk05xT5oTVJQ4jbBGac8oc2WTTsh3LmI0eQoo9xfpo70ca1meYhvcZbnY5QIcQAGEZWb2ydNaAs7SpYJO2F203uxx0UGVtpZbtWKbert46N+tc2WTrlsfxGdLeQmfA7e5ik03nZp2r3q7eenf7u3T9hqBtRdu0qWCTzhpwlrJ6ZZldDtBuBEBYQnp8umZkzdDOozuZ9RvCDlce1vu73tfAXgN15oCu73bbku/SU0v76rUPj481/NO7KdqS7+ryx5KkMwecqYG9Bur9Xe/rSNWRbnkMdL+P932sXUd3aUbWDKXHp5tdDtAuBECEvX6x/ZQ9OFv5pflasWsFXWwhbk/JHn2w5wONTB6p6ZnTu6wlcEu+S0tyE1VWFdi9XFYVoSW5iV0aAm2y6ezMszUyeaQ+2POB9pTs6bJjo+cZMvT+rveVX5qv7MHZ6hfbz+ySgDYRABH2sgdn61D5IS3fuZzwFya+OvKV3t/1voYmDVX24OxOjwn0GVJOXsKxW00DZf3t5XkJXdIdHGGLUPbgbA1JGqL3d72vr4581fmDwnSGDC3fuVyHyg8pe3C22eUAbSIAIuztKd6jd7e/y4zfMLO9aLuWbV+m1LhUzRs6TzGRMSd9rH2FTpVVOdQ8/DWwqbTKoX2FnVuMOiYyRvOGzlNqXKre3f4uY1HDTJ1Rp3e3v6s9xbToIvgRABH2/rvnv1xSK0ztK92nt756S7HOWC0YuUADEgac1HHKPe1rQWzvfi0ZkDBAC0YuUKwzVm999ZbyS/NP+lgIXj7Dp//u+a/ZZQBtIgACCGmFlYX6+5d/19flX2vuKXM1JX2KIu2RHTpGrKt9rcPt3a+xSHukpqRP0dxT5urr8q/19y//rsLKwg4fBwC6EsuXAwh51XXVWrZjmUanjNbpaadrUO9BWp2/WjuO7mjX/TOSaxTn9h6bANJSN7CheHedMpJrOlTX4N6DNTl9sqIcUfp438cs8gwgaBAAAYSNDQUbtKt4l6akT9HMQTM1smykPjv4mfaX7T/h/ew2afa4Ei3JTZSaTRSqvz1rXIns7ZxwnBaXptP6n6b+cf218+hOrc5frfKa8o4/IQDoJgRAAGGlvKZcOTtzlBaXpjPSz9C8ofN0uPKwvvj6C+08urPV8aDD0z2aP6VIOXkJ8nqPnxrj3XU6b2KRhqd7Tvi4dptdg3oP0pi+Y9Qnuo8KKwq19KulbYZPADADARBAWNpftl9LNi9RalyqxvYdqxlZM3RmxpnaXbxbO4/u1P6y/c3C4PB0j4ameXSoKNq/7aa5BfIZ3hYfw26zKy0uTYN6D9LAXgMV5YjSvpL6iSkHyg506/MDgM4gAIa5CFuEHPbOv81dcQzADAfKDuhA2QH1cvXS4N6DNThxsIb1GSavz6vCikJ9XfG1CioKVFpdqvKactXU1WhAo7F+dlv9OoHOCKdinbGKj4pXSkyK+sb0VXJMshx2h4o9xdpYsFE7ju5QsafYvCcLdFJXnevD6Xrd4Ypv9TDU+IN3+ajLu/z4DrtDXl/LLSJAsCr2FGvdwXVad3CdEt2JSotLU9/YvhqSOETj+o3z71dbVyuP93h376UjL5XL4VJkxPGZxeU15SqoKNAn+z/R/rL9Kqoq6smnAnSpxqHvmrHXdPnxCYPBiQAIwHKKqopUVFWkDQUbJEnRkdGKdcYqJjJGsc5YRUdGa2y/sZKknUd3qrK2UuU15aqorVB5TbkqayvNLB8AOo0AGIaq66r9///Lhr8EtGZ0RsNfiV11PCBYVNZWBoQ6h93hD4DrDq6jxRthzeP1aPH6xZLUZb/rLodLV46+UlLgdxKCBwEwzHl93i77QPMlCADhqav/sOf7IvhxJRAAAACLIQACAABYDAEQAADAYgiAAAAAFkMABAAAsBgCIAAAgMUQAAEAACyGAAgAAGAxBEAAAACLIQACAABYDAEQAADAYgiAAAAAFkMABAAAsBgCIAAAgMUQAAEAACyGAAgAAGAxDrMLAGAdPkPaV+hUuSdCsa46ZSTXyG4zuyoAsB4CIIAesSXfpZy8BJVVHT/txLm9mj2uRMPTPSZWBgDWQxcwgG63Jd+lJbmJKquKCNheVhWhJbmJ2pLvMqkyALAmAiCAbuUzpJy8hGO3mvb31t9enpcgn9GjZQGApREAAXSrfYXOY92+rQ32s6m0yqF9hc6eLAsALI0ACKBblXsi2t6pA/sBADqPAAigW8W66rp0PwBA5xEAAXSrjOQaxbm9klob5Gco3u1VRnJNT5YFAJZGAATQrew2afa4kmO3mobA+tuzxpWwHiAA9CACIIBuNzzdo/lTihTnDuzmjXfXaf6UItYBBIAexkLQAHrE8HSPhqZ5uBIIAAQBAiCAHmO3SZkpjPUDALPRBQwAAGAxBEAAAACLIQACAABYDAEQAADAYgiAAAAAFkMABAAAsBgCIAAAgMUQAAEAACyGAAgAAGAxBEAAAACLIQACAABYDAEQAADAYgiAAAAAFkMABAAAsBgCIAAAgMU4zC4A3cthd8hht97bbMXnDCD4WPVcZNXnHUp4h8JQ4w/elaOvNLGS4OCwO+T1ec0uA4BFND4HXzP2GhMrCQ6EweBEFzAAAIDFEMvDkMfr8f9/8frFlm39avirs/HrAQDdzeP1aPH6xZJk6fNvQ+sn5+DgRAAMc16f17InIKs+bwDmI/Qg2NEFDAAAYDEEQAAAAIshAAIAAFgMARAAAMBiCIAAAAAWQwAEAACwGAIgAACAxRAAAQAALIaFoAEgyPgMaV+hU+WeCMW66pSRXCO7zeyqAIQTAiAABJEt+S7l5CWorOr46TnO7dXscSUans7VJQB0DbqAASBIbMl3aUluosqqIgK2l1VFaEluorbku0yqDEC4IQACQBDwGVJOXsKxW037e+tvL89LkM/o0bIAhCkCIAAEgX2FzmPdvq0N9rOptMqhfYXOniwLQJgiAAJAECj3RLS9Uwf2A4ATIQACQBCIddV16X4AcCIEQAAIAhnJNYpzeyW1NsjPULzbq4zkmp4sC0CYIgACQBCw26TZ40qO3WoaAutvzxpXwnqAALoEARAAgsTwdI/mTylSnDuwmzfeXaf5U4pYBxBAl2EhaAAIIsPTPRqa5uFKIAC6FQEQAIKM3SZlpjDWD0D3oQsYAADAYgiAAAAAFkMABAAAsBgCIAAAgMUQAAEAACyGAAgAAGAxLAMDwDQ+Q6x3BwAmIAACMMWWfJdy8hJUVnX8NBTn9mr2uBKueAEA3YwuYAA9bku+S0tyE1VWFRGwvawqQktyE7Ul32VSZQBgDQRAAD3KZ0g5eQnHbjXt762/vTwvQT6jR8sCAEshAALoUfsKnce6fVsb7GdTaZVD+wqdPVkWAFgKARBAjyr3RLS9Uwf2AwB0HAEQQI+KddV16X4AgI4jAALoURnJNYpzeyW1NsjPULzbq4zkmp4sCwAshQAIoEfZbdLscSXHbjUNgfW3Z40rYT1AAOhGBEAAPW54ukfzpxQpzh3YzRvvrtP8KUWsAwgA3YyFoAGYYni6R0PTPFwJBABMQAAEYBq7TcpMYawfAPQ0uoABAAAshgAIAABgMQRAAAAAiyEAAgAAWAwBEAAAwGIIgAAAABZDAAQAALAYAiAAAIDFEAABAAAshgAIAABgMQRAAAAAiyEAAgAAWAwBEAAAwGIIgAAAABZDAAQAALAYAiAAAIDFEAABAAAshgAIAABgMQRAAAAAi3GYXQC6l8POWwx0VOPPDZ8hoOP43AQ/3qEw1PiDd83Ya0ysBAh9fIaAznHYHfL6vGaXgSboAgYAALAYWgDDkMfr0eL1iyWJv7qAk9TQks5nCDg5DZ8hj9djciVoCQEwTPGBAzqH4Ad0Dp+h4EYXMAAAgMUQAAEAACyGAAgAAGAxBEAAAACLIQACAABYDAEQAADAYgiAAAAAFkMABAAAsBgCIAAAgMUQAAEAACyGAAgAAGAxBEAAAACLIQACAABYDAEQAADAYhxmF4DWfXfCd80uAQAAhCFaAAEAACyGAAgAAGAxBEAAAACLIQACAABYDAEQAADAYgiAAAAAFkMABAAAsBgCIAAAgMUQAAEAACyGAAgAAGAxBEAAAACLIQACAABYDAEQAADAYgiAAAAAFkMABAAAsBiH2QWEMsMwJEmlpaUmVwIAANqr4Xu74XvcigiAnVBWViZJysjIMLkSAADQUWVlZUpISDC7DFPYDCvH307y+Xw6cOCA4uLiZLPZzC4HAAC0g2EYKisrU2pqqux2a46GIwACAABYjDVjLwAAgIURAAEAACyGAAgAAGAxBEAAAACLIQACAABYDAEQAADAYgiAAAAAFkMABAAAsBgCIAAAgMUQAAEAACyGAAgAAGAxBEAAAACLIQACAABYDAEQAADAYgiAAAAAFkMADAHXXXedLrroIrPLAAAAYYIAaDKbzXbCf/fdd58WLlyoF1980dQ6QzWEFhUV6aqrrlJ8fLx69eql73znOyovLw/Y54svvtC0adPkcrmUkZGhhx9+uM3jejwe3XrrrUpKSlJsbKwWLFigr7/+OmCfvXv3at68eYqOjlZKSoruvPNOeb3edtf+/e9/XzabTY8//njA9oEDBzb7PXnwwQdNrxcAEDocZhdgdQcPHvT//7XXXtM999yjrVu3+rfFxsYqNjbWjNLCwlVXXaWDBw8qJydHtbW1uv766/Xd735Xf/nLXyRJpaWlys7O1qxZs/THP/5RGzZs0A033KBevXrpu9/9bqvHvf3227V06VK9/vrrSkhI0G233ab58+dr1apVkqS6ujrNmzdP/fr108cff6yDBw/qmmuuUWRkpB544IE2637zzTe1evVqpaamtvjzX/3qV7rpppv8t+Pi4k54vO6uFwAQYgwEjRdeeMFISEhotv3aa681vvnNb/pvn3322cZtt91m/PCHPzR69eplpKSkGH/605+M8vJy47rrrjNiY2ONwYMHG2+//XbAcTZs2GDMnTvXiImJMVJSUoxvf/vbRmFhof/nr7/+ujFq1CjD5XIZiYmJxsyZM43y8nLj3nvvNSQF/FuxYoVhGIZx1113GUOGDDHcbreRlZVl/OIXvzBqamr8x7z33nuNsWPHGs8995yRkZFhxMTEGDfffLPh9XqNhx56yOjbt6+RnJxs/L//9/8CapVkLFq0yJg7d67hcrmMrKws4/XXX+/Q6/nll18akoxPPvnEv+2dd94xbDabsX//fsMwDGPRokVG7969jerqav8+P/nJT4xhw4a1etzi4mIjMjIyoJ7Nmzcbkozc3FzDMAzj7bffNux2u3Ho0CH/Pk8//bQRHx8f8Fgtyc/PN9LS0oyNGzcamZmZxu9///uAn7e07US6u14AQOihCzhEvfTSS+rTp4/Wrl2r//mf/9HNN9+sSy+9VFOnTtVnn32m7OxsXX311aqsrJQkFRcXa8aMGRo/frw+/fRTvfvuu/r666912WWXSapvibziiit0ww03aPPmzVq5cqXmz58vwzB0xx136LLLLtPcuXN18OBBHTx4UFOnTpVU3/L04osv6ssvv9TChQv17LPP6ve//31ArTt27NA777yjd999V3/961/13HPPad68ecrPz9d///tfPfTQQ/rFL36hNWvWBNzvl7/8pRYsWKD169frqquu0re+9S1t3rzZ//NzzjlH1113XauvUW5urnr16qWJEyf6t82aNUt2u93/WLm5uZo+fbqcTqd/nzlz5mjr1q06evSoJGnlypWy2WzavXu3JGndunWqra3VrFmz/PcZPny4BgwYoNzcXP9xR48erb59+wYct7S0VJs2bZIk7d69WzabTStXrvTv4/P5dPXVV+vOO+/Uqaee2upze/DBB5WUlKTx48frkUceOWFXbVfVCwAIH3QBh6ixY8fqF7/4hSTp7rvv1oMPPqg+ffr4uwXvuecePf300/riiy80efJk/eEPf9D48eMDuvOef/55ZWRk6KuvvlJ5ebm8Xq/mz5+vzMxMSdLo0aP9+7rdblVXV6tfv34BdTTUINWPTbvjjjv06quv6q677vJv9/l8ev755xUXF6eRI0fq3HPP1datW/X222/Lbrdr2LBheuihh7RixQqdccYZ/vtdeumluvHGGyVJv/71r5WTk6Mnn3xSixYtkiQNGDBA/fv3b/U1OnTokFJSUgK2ORwOJSYm6tChQ/59srKyAvZpCEGHDh1S7969FR0drWHDhikyMtK/3el0qlevXs3u1/i4jcNU0+NKUmRkpIYNG6bo6Gj/Pg899JAcDod+8IMftPq8fvCDH+i0005TYmKiPv74Y9199906ePCgHnvssVZfh66oFwAQPgiAIWrMmDH+/0dERCgpKSkgsDV8eRcUFEiS1q9frxUrVrQ4nnDHjh3Kzs7WzJkzNXr0aM2ZM0fZ2dm65JJL1Lt37xPW8dprr+mJJ57Qjh07/CEyPj4+YJ+BAwcGjFHr27evIiIiZLfbA7Y11NpgypQpzW7n5eX5by9evPiEtXWVSZMmacuWLV1+3LS0tIDjrlu3TgsXLtRnn30mm83W6v1+/OMf+/8/ZswYOZ1Ofe9739Nvf/tbRUVFdXmdAIDwQxdwiGpojWpgs9kCtjUECJ/PJ0kqLy/XhRdeqLy8vIB/27Zt0/Tp0xUREaGcnBy98847GjlypJ588kkNGzZMu3btarWG3NxcXXXVVTr//PP11ltv6fPPP9fPf/5z1dTUdKjWhm0NtXaVfv36NQuVXq9XRUVF/pbMfv36NZsN23C7aWtn4+PW1NSouLi42f06c9wPP/xQBQUFGjBggBwOhxwOh/bs2aP//d//1cCBA1t9nmeccYa8Xq+/i7qn6gUAhC4CoEWcdtpp2rRpkwYOHKhTTjkl4F9MTIyk+hB25pln6v7779fnn38up9OpN998U5LkdDpVV1cXcMyPP/5YmZmZ+vnPf66JEydqyJAh2rNnT5fVvHr16ma3R4wY0e77T5kyRcXFxVq3bp1/2/vvvy+fz+fvap4yZYo++OAD1dbW+vfJycnRsGHDWm39nDBhgiIjI/Wf//zHv23r1q3au3evv9VyypQp2rBhQ0AAzcnJUXx8vEaOHNnica+++mp98cUXAQE9NTVVd955p5YtW9bq88zLy5Pdbm/W3d3d9QIAQhcB0CJuvfVWFRUV6YorrtAnn3yiHTt2aNmyZbr++utVV1enNWvW6IEHHtCnn36qvXv3asmSJSosLPQHroEDB+qLL77Q1q1bdfjwYdXW1mrIkCHau3evXn31Ve3YsUNPPPGEPzB2hddff13PP/+8vvrqK917771au3atbrvtNv/Pr7nmGt19992t3n/EiBGaO3eubrrpJq1du1arVq3Sbbfdpm9961v+5VWuvPJKOZ1Ofec739GmTZv02muvaeHChQHdrGvXrtXw4cO1f/9+SVJCQoK+853v6Mc//rFWrFihdevW6frrr9eUKVM0efJkSVJ2drZGjhypq6++WuvXr9eyZcv0i1/8Qrfeequ/m3b//v0aPny41q5dK0lKSkrSqFGjAv5FRkaqX79+GjZsmKT6VtfHH39c69ev186dO/XKK6/o9ttv17e//W1/YG163K6qFwAQPhgDaBGpqalatWqVfvKTnyg7O1vV1dXKzMzU3LlzZbfbFR8frw8++ECPP/64SktLlZmZqUcffVTnnXeeJOmmm27SypUrNXHiRJWXl2vFihX6xje+odtvv1233XabqqurNW/ePP3yl7/Ufffd1yU133///Xr11Vd1yy23qH///vrrX/8a0Bq1d+/egHGELXnllVd02223aebMmbLb7VqwYIGeeOIJ/88TEhL03nvv6dZbb9WECRPUp08f3XPPPQFrAFZWVmrr1q0BrYS///3v/cerrq7WnDlz/JNTpPpxmW+99ZZuvvlmTZkyRTExMbr22mv1q1/9yr9PbW2ttm7d6p+p3R5RUVF69dVXdd9996m6ulpZWVm6/fbbAwJrS8ftinoBAOHDZhiGYXYRQFM2m01vvvlmSF59BACAYEcXMAAAgMUQAAEAACyGMYAISoxMAACg+9ACCAAAYDEEQAAAAIshAAIAAFgMARAAAMBiCIAAAAAWQwAEAACwGAIgAACAxRAAAQAALIYACAAAYDEEQAAAAIshAAIAAFgMARAAAMBiCIAAAAAWQwAEAACwGAIgAACAxRAAAQAALIYACAAAYDEEQAAAAIshAAIAAFgMARAAAMBi/j8EydNCV0msGQAAAABJRU5ErkJggg==", + "text/html": [ + "\n", + "
\n", + "
\n", + " Skillcorner\n", + "
\n", + " \n", + "
\n", + " " + ], + "text/plain": [ + "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "skc_client.create_visualisation_per_frame(tracking_data, frame=500)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b356070d", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.8.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/skillcorner/visualisation_utils.py b/skillcorner/visualisation_utils.py new file mode 100644 index 0000000..2ba22df --- /dev/null +++ b/skillcorner/visualisation_utils.py @@ -0,0 +1,272 @@ +import json + + +def _get_json_list(response): + data_bytes = response.content + data_list = data_bytes.decode('utf-8').split("\n") + data = [] + for line in data_list: + if line: + data.append(json.loads(line)) + return data + + +def _convert_response_to_df(data, df_parameterization): + import pandas as pd + df = pd.DataFrame(data) + + if not df_parameterization: + return df + + elif df_parameterization=='positions_per_frame': + arrays = [[], []] + + for frame in df.frame: + if df.data[frame]: + for i in range(len(df.data[frame])): + trackable_object = df.data[frame][i]['trackable_object'] + if trackable_object not in arrays[0]: + arrays[0] += [trackable_object]*2 + arrays[1] += ['x', 'y'] + + positions = _preprepare_positions(len(df.frame), len(arrays[0])) + + for frame in df.frame: + if df.data[frame]: + for i in range(len(df.data[frame])): + trackable_object = df.data[frame][i]['trackable_object'] + index = arrays[0].index(trackable_object) + positions[frame][index] = df.data[frame][i]['x'] + positions[frame][index+1] = df.data[frame][i]['y'] + + names = ['trackable_object', 'position'] + arrays_2 = [[], []] + arrays_2[0] = df.frame + arrays_2[1] = df.timestamp + tuples_2 = list(zip(*arrays_2)) + names_2 = ['frame', 'timestamp'] + index = pd.MultiIndex.from_tuples(tuples_2, names=names_2) + + tuples = list(zip(*arrays)) + multi_index = pd.MultiIndex.from_tuples(tuples, names=names) + data = pd.DataFrame(positions, columns=multi_index, index=index) + + return data + + +def _preprepare_positions(dim_1, dim_2): + positions = [] + for i in range(dim_1): + temp = [] + for j in range(dim_2): + temp.append(0) + positions.append(temp) + return positions + + +def create_visualisation_per_frame(self, match, frame): + """ + Function to plot extrapolated data visualisation at indicated frame. + + :param match: df of extrapolated tracking data or int id of match which should be presented + :param frame: int indicating which frame visualisation to be presented + """ + import matplotlib.pyplot as plt + import pandas as pd + + if not isinstance(match, pd.DataFrame): + tracking_data = self.get_match_tracking_data(match) + else: + tracking_data = match + + x = tracking_data.loc[frame, tracking_data.columns.get_level_values(1)=="x"] + y = tracking_data.loc[frame, tracking_data.columns.get_level_values(1)=="y"] + trackable_objects = tracking_data.columns.droplevel(1).unique() + timestamp = tracking_data.index[frame][1] + x[x==0] = float('nan') + y[y==0] = float('nan') + + fig, ax = plt.subplots() + fig.suptitle(f'Tracking visualisation of frame: {frame}') + fig.canvas.set_window_title('Skillcorner') + _plot_field(ax) + ax.get_xaxis().set_visible(False) + ax.get_yaxis().set_visible(False) + text = fig.text(0.15, 0.05, f"Timestamp: {timestamp}") + scatter = ax.scatter(x, y) + plt.show() + + +def create_full_visualisation(self, match, valinit=100): + """ + Function to plot extrapolated data visualisation for the full game. Uses matplot Slider widget. + + :param match: df of extrapolated tracking data or int id of match which should be presented + :param valinit: int indicating frame of starting point for slider + """ + from matplotlib.widgets import Slider + import matplotlib.pyplot as plt + import pandas as pd + import numpy as np + + if not isinstance(match, pd.DataFrame): + tracking_data = self.get_match_tracking_data(match) + else: + tracking_data = match + + x = tracking_data.loc[valinit, tracking_data.columns.get_level_values(1)=="x"] + y = tracking_data.loc[valinit, tracking_data.columns.get_level_values(1)=="y"] + x[x==0] = float('nan') + y[y==0] = float('nan') + timestamp = tracking_data.index[valinit][1] + + fig, ax = plt.subplots() + fig.suptitle('Tracking visualisation') + fig.canvas.set_window_title('Skillcorner') + plt.subplots_adjust(bottom=0.25) + _plot_field(ax) + ax.get_xaxis().set_visible(False) + ax.get_yaxis().set_visible(False) + ax_slider = plt.axes([0.25, 0.1, 0.65, 0.03]) + ax_slider.get_xaxis().set_visible(False) + ax_slider.get_yaxis().set_visible(False) + text = fig.text(0.15, 0.05, f"Timestamp: {timestamp}") + scatter = ax.scatter(x, y) + slider = Slider(ax=ax_slider, label='Frames', valmin=0, valmax=len(tracking_data)-1, valinit=valinit, valstep=1) + + def update(frame): + x = tracking_data.loc[frame, tracking_data.columns.get_level_values(1)=="x"] + y = tracking_data.loc[frame, tracking_data.columns.get_level_values(1)=="y"] + x[x==0] = float('nan') + y[y==0] = float('nan') + timestamp = tracking_data.index[frame][1] + xx = np.vstack((x, y)) + scatter.set_offsets(xx.T) + text.set_text(f"Timestamp: {timestamp}") + + slider.on_changed(update) + + plt.show() + + +def _plot_rectangle(x1, x2, y1, y2, ax): + ax.plot([x1, x1], [y1, y2], color="white", zorder=8000) + ax.plot([x2, x2], [y1, y2], color="white", zorder=8000) + ax.plot([x1, x2], [y1, y1], color="white", zorder=8000) + ax.plot([x1, x2], [y2, y2], color="white", zorder=8000) + + +def _plot_field(ax): + import matplotlib.pyplot as plt + from matplotlib.patches import Arc + # Pitch Outline & Centre Line + origin_x1 = -52.5 + origin_x2 = 52.5 + origin_y1 = -34 + origin_y2 = 34 + + d = 2 + rectangle = plt.Rectangle( + (origin_x1 - 2 * d, origin_y1 - 2 * d), + 105 + 4 * d, + 68 + 4 * d, + fc="green", + alpha=0.4, + zorder = -5000, + ) + ax.add_patch(rectangle) + _plot_rectangle(origin_x1, origin_x2, origin_y1, origin_y2, ax) + ax.plot([0, 0], [origin_y1, origin_y2], color="white", zorder=8000) + + # Left Penalty Area + penalty_box_length = 16.5 + penalty_box_width = 40.3 + + x1 = origin_x1 + x2 = origin_x1 + penalty_box_length + y1 = -penalty_box_width / 2 + y2 = penalty_box_width / 2 + _plot_rectangle(x1, x2, y1, y2, ax) + + # Right Penalty Area + x1 = origin_x2 - penalty_box_length + x2 = origin_x2 + y1 = -penalty_box_width / 2 + y2 = penalty_box_width / 2 + _plot_rectangle(x1, x2, y1, y2, ax) + + # Left 6-yard Box + six_yard_box_length = 5.5 + six_yard_box_width = 18.3 + + x1 = origin_x1 + x2 = origin_x1 + six_yard_box_length + y1 = -six_yard_box_width / 2 + y2 = six_yard_box_width / 2 + _plot_rectangle(x1, x2, y1, y2, ax) + + # Right 6-yard Box + x1 = origin_x2 - six_yard_box_length + x2 = origin_x2 + y1 = -six_yard_box_width / 2 + y2 = six_yard_box_width / 2 + _plot_rectangle(x1, x2, y1, y2, ax) + + # Left Goal + goal_width = 7.3 + goal_length = 2 + + x1 = origin_x1 - goal_length + x2 = origin_x1 + y1 = -goal_width / 2 + y2 = goal_width / 2 + _plot_rectangle(x1, x2, y1, y2, ax) + + # Right Goal + x1 = origin_x2 + x2 = origin_x2 + goal_length + y1 = -goal_width / 2 + y2 = goal_width / 2 + _plot_rectangle(x1, x2, y1, y2, ax) + + # Prepare Circles + circle_radius = 9.15 + penalty_spot_distance = 11 + centreCircle = plt.Circle((0, 0), circle_radius, color="white", fill=False, zorder=8000) + centreSpot = plt.Circle((0, 0), 0.4, color="white", zorder=8000) + lx = origin_x1 + penalty_spot_distance + leftPenSpot = plt.Circle((lx, 0), 0.4, color="white", zorder=8000) + rx = origin_x2 - penalty_spot_distance + rightPenSpot = plt.Circle((rx, 0), 0.4, color="white", zorder=8000) + + # Draw Circles + ax.add_patch(centreCircle) + ax.add_patch(centreSpot) + ax.add_patch(leftPenSpot) + ax.add_patch(rightPenSpot) + + # Prepare Arcs + r = circle_radius * 2 + leftArc = Arc( + (lx, 0), + height=r, + width=r, + angle=0, + theta1=307, + theta2=53, + color="white", + zorder=8000, + ) + rightArc = Arc( + (rx, 0), + height=r, + width=r, + angle=0, + theta1=127, + theta2=233, + color="white", + zorder=8000, + ) + # Draw Arcs + ax.add_patch(leftArc) + ax.add_patch(rightArc)