From 6c5cb9f5bed02aa198b0d68b5711ce60b4f0489e Mon Sep 17 00:00:00 2001 From: mmaelicke Date: Fri, 19 Jan 2024 09:54:51 +0000 Subject: [PATCH] Deploy to GitHub pages --- .buildinfo | 4 + .nojekyll | 0 _sources/api/api.rst.txt | 46 + _sources/api/api_add_entry.ipynb.txt | 82 + _sources/api/api_add_group.ipynb.txt | 80 + _sources/api/api_add_keyword.ipynb.txt | 65 + _sources/api/api_add_license.ipynb.txt | 42 + _sources/api/api_add_person.ipynb.txt | 65 + _sources/api/api_add_thesaurus.ipynb.txt | 42 + _sources/api/api_add_variable.ipynb.txt | 65 + _sources/api/api_connect_database.ipynb.txt | 185 + _sources/api/api_create_tables.ipynb.txt | 42 + _sources/api/api_find_entry.ipynb.txt | 88 + _sources/api/api_find_keyword.ipynb.txt | 95 + _sources/api/api_find_license.ipynb.txt | 42 + _sources/api/api_find_person.ipynb.txt | 135 + _sources/api/api_find_thesaurus.ipynb.txt | 109 + _sources/api/api_find_variable.ipynb.txt | 83 + _sources/api/api_get_logger.ipynb.txt | 180 + _sources/api/api_get_uuid.ipynb.txt | 138 + _sources/api/api_populate_defaults.ipynb.txt | 42 + _sources/api/api_show_attributes.ipynb.txt | 110 + _sources/api/api_show_records.ipynb.txt | 290 + _sources/cli/cli.rst.txt | 85 + _sources/cli/cli_add.ipynb.txt | 421 + _sources/cli/cli_create.ipynb.txt | 175 + _sources/cli/cli_find.ipynb.txt | 302 + _sources/cli/cli_init.ipynb.txt | 95 + _sources/cli/cli_populate.ipynb.txt | 147 + _sources/cli/cli_show.ipynb.txt | 105 + _sources/cli/cli_uuid.ipynb.txt | 246 + _sources/dev/dev.rst.txt | 24 + _sources/dev/iso19115.rst.txt | 1116 ++ _sources/dev/tests.rst.txt | 80 + _sources/ext/custom.rst.txt | 6 + _sources/ext/export.rst.txt | 6 + _sources/ext/ext.rst.txt | 19 + _sources/ext/io.rst.txt | 8 + _sources/ext/standards_export.rst.txt | 8 + _sources/home/getting_started.rst.txt | 7 + _sources/home/home.rst.txt | 42 + _sources/home/install.rst.txt | 79 + _sources/home/scheme.rst.txt | 12 + _sources/index.rst.txt | 18 + _sources/models/data.rst.txt | 17 + _sources/models/datasource.rst.txt | 6 + _sources/models/entry.rst.txt | 9 + _sources/models/keyword.rst.txt | 7 + _sources/models/license.rst.txt | 7 + _sources/models/location.rst.txt | 12 + _sources/models/models.rst.txt | 21 + _sources/models/person.rst.txt | 6 + _sources/models/result.rst.txt | 6 + _sources/models/variable.rst.txt | 6 + .../_sphinx_javascript_frameworks_compat.js | 134 + _static/basic.css | 900 ++ _static/binder_badge_logo.svg | 1 + _static/brand.png | Bin 0 -> 19085 bytes _static/broken_example.png | Bin 0 -> 21404 bytes _static/css/custom.css | 3 + _static/doctools.js | 156 + _static/documentation_options.js | 14 + _static/file.png | Bin 0 -> 286 bytes _static/jquery-3.6.0.js | 10881 ++++++++++++++++ _static/jquery.js | 2 + _static/jupyterlite_badge_logo.svg | 3 + _static/language_data.js | 199 + _static/minus.png | Bin 0 -> 90 bytes _static/nbsphinx-broken-thumbnail.svg | 9 + _static/nbsphinx-code-cells.css | 259 + _static/nbsphinx-gallery.css | 31 + _static/nbsphinx-no-thumbnail.svg | 9 + _static/no_image.png | Bin 0 -> 4315 bytes _static/plus.png | Bin 0 -> 90 bytes _static/pygments.css | 152 + _static/scripts/bootstrap.js | 3 + _static/scripts/bootstrap.js.LICENSE.txt | 5 + _static/scripts/bootstrap.js.map | 1 + _static/scripts/pydata-sphinx-theme.js | 2 + _static/scripts/pydata-sphinx-theme.js.map | 1 + _static/searchtools.js | 566 + _static/sg_gallery-binder.css | 11 + _static/sg_gallery-dataframe.css | 47 + _static/sg_gallery-rendered-html.css | 224 + _static/sg_gallery.css | 342 + _static/sphinx_highlight.js | 144 + _static/styles/bootstrap.css | 6 + _static/styles/bootstrap.css.map | 1 + _static/styles/pydata-sphinx-theme.css | 2 + _static/styles/pydata-sphinx-theme.css.map | 1 + _static/styles/theme.css | 2 + _static/underscore-1.13.1.js | 2042 +++ _static/underscore.js | 6 + _static/vendor/fontawesome/6.5.1/LICENSE.txt | 165 + .../vendor/fontawesome/6.5.1/css/all.min.css | 5 + .../vendor/fontawesome/6.5.1/js/all.min.js | 2 + .../6.5.1/js/all.min.js.LICENSE.txt | 5 + .../6.5.1/webfonts/fa-brands-400.ttf | Bin 0 -> 207972 bytes .../6.5.1/webfonts/fa-brands-400.woff2 | Bin 0 -> 117372 bytes .../6.5.1/webfonts/fa-regular-400.ttf | Bin 0 -> 68004 bytes .../6.5.1/webfonts/fa-regular-400.woff2 | Bin 0 -> 25452 bytes .../6.5.1/webfonts/fa-solid-900.ttf | Bin 0 -> 419720 bytes .../6.5.1/webfonts/fa-solid-900.woff2 | Bin 0 -> 156496 bytes .../6.5.1/webfonts/fa-v4compatibility.ttf | Bin 0 -> 10832 bytes .../6.5.1/webfonts/fa-v4compatibility.woff2 | Bin 0 -> 4792 bytes _static/webpack-macros.html | 31 + api/api.html | 739 ++ api/api_add_entry.html | 767 ++ api/api_add_entry.ipynb | 82 + api/api_add_group.html | 685 + api/api_add_group.ipynb | 80 + api/api_add_keyword.html | 707 + api/api_add_keyword.ipynb | 65 + api/api_add_license.html | 641 + api/api_add_license.ipynb | 42 + api/api_add_person.html | 708 + api/api_add_person.ipynb | 65 + api/api_add_thesaurus.html | 728 ++ api/api_add_thesaurus.ipynb | 42 + api/api_add_variable.html | 685 + api/api_add_variable.ipynb | 65 + api/api_connect_database.html | 735 ++ api/api_connect_database.ipynb | 185 + api/api_create_tables.html | 622 + api/api_create_tables.ipynb | 42 + api/api_find_entry.html | 889 ++ api/api_find_entry.ipynb | 88 + api/api_find_keyword.html | 743 ++ api/api_find_keyword.ipynb | 95 + api/api_find_license.html | 655 + api/api_find_license.ipynb | 42 + api/api_find_person.html | 788 ++ api/api_find_person.ipynb | 135 + api/api_find_thesaurus.html | 713 + api/api_find_thesaurus.ipynb | 109 + api/api_find_variable.html | 694 + api/api_find_variable.ipynb | 83 + api/api_get_logger.html | 723 + api/api_get_logger.ipynb | 180 + api/api_get_uuid.html | 742 ++ api/api_get_uuid.ipynb | 138 + api/api_populate_defaults.html | 652 + api/api_populate_defaults.ipynb | 42 + api/api_show_attributes.html | 679 + api/api_show_attributes.ipynb | 110 + api/api_show_records.html | 863 ++ api/api_show_records.ipynb | 290 + cli/cli.html | 665 + cli/cli_add.html | 861 ++ cli/cli_add.ipynb | 421 + cli/cli_create.html | 665 + cli/cli_create.ipynb | 175 + cli/cli_find.html | 793 ++ cli/cli_find.ipynb | 302 + cli/cli_init.html | 637 + cli/cli_init.ipynb | 95 + cli/cli_populate.html | 674 + cli/cli_populate.ipynb | 147 + cli/cli_show.html | 641 + cli/cli_show.ipynb | 105 + cli/cli_uuid.html | 766 ++ cli/cli_uuid.ipynb | 246 + dev/dev.html | 576 + dev/iso19115.html | 2047 +++ dev/tests.html | 638 + ext/custom.html | 661 + ext/export.html | 814 ++ ext/ext.html | 574 + ext/io.html | 649 + ext/standards_export.html | 805 ++ genindex.html | 1348 ++ home/getting_started.html | 559 + home/home.html | 592 + home/install.html | 625 + home/scheme.html | 771 ++ index.html | 624 + models/data.html | 607 + models/datasource.html | 1140 ++ models/entry.html | 1542 +++ models/keyword.html | 932 ++ models/license.html | 756 ++ models/location.html | 716 + models/models.html | 584 + models/person.html | 797 ++ models/result.html | 785 ++ models/variable.html | 802 ++ objects.inv | Bin 0 -> 9439 bytes py-modindex.html | 590 + search.html | 504 + searchindex.js | 1 + 190 files changed, 66865 insertions(+) create mode 100644 .buildinfo create mode 100644 .nojekyll create mode 100644 _sources/api/api.rst.txt create mode 100644 _sources/api/api_add_entry.ipynb.txt create mode 100644 _sources/api/api_add_group.ipynb.txt create mode 100644 _sources/api/api_add_keyword.ipynb.txt create mode 100644 _sources/api/api_add_license.ipynb.txt create mode 100644 _sources/api/api_add_person.ipynb.txt create mode 100644 _sources/api/api_add_thesaurus.ipynb.txt create mode 100644 _sources/api/api_add_variable.ipynb.txt create mode 100644 _sources/api/api_connect_database.ipynb.txt create mode 100644 _sources/api/api_create_tables.ipynb.txt create mode 100644 _sources/api/api_find_entry.ipynb.txt create mode 100644 _sources/api/api_find_keyword.ipynb.txt create mode 100644 _sources/api/api_find_license.ipynb.txt create mode 100644 _sources/api/api_find_person.ipynb.txt create mode 100644 _sources/api/api_find_thesaurus.ipynb.txt create mode 100644 _sources/api/api_find_variable.ipynb.txt create mode 100644 _sources/api/api_get_logger.ipynb.txt create mode 100644 _sources/api/api_get_uuid.ipynb.txt create mode 100644 _sources/api/api_populate_defaults.ipynb.txt create mode 100644 _sources/api/api_show_attributes.ipynb.txt create mode 100644 _sources/api/api_show_records.ipynb.txt create mode 100644 _sources/cli/cli.rst.txt create mode 100644 _sources/cli/cli_add.ipynb.txt create mode 100644 _sources/cli/cli_create.ipynb.txt create mode 100644 _sources/cli/cli_find.ipynb.txt create mode 100644 _sources/cli/cli_init.ipynb.txt create mode 100644 _sources/cli/cli_populate.ipynb.txt create mode 100644 _sources/cli/cli_show.ipynb.txt create mode 100644 _sources/cli/cli_uuid.ipynb.txt create mode 100644 _sources/dev/dev.rst.txt create mode 100644 _sources/dev/iso19115.rst.txt create mode 100644 _sources/dev/tests.rst.txt create mode 100644 _sources/ext/custom.rst.txt create mode 100644 _sources/ext/export.rst.txt create mode 100644 _sources/ext/ext.rst.txt create mode 100644 _sources/ext/io.rst.txt create mode 100644 _sources/ext/standards_export.rst.txt create mode 100644 _sources/home/getting_started.rst.txt create mode 100644 _sources/home/home.rst.txt create mode 100644 _sources/home/install.rst.txt create mode 100644 _sources/home/scheme.rst.txt create mode 100644 _sources/index.rst.txt create mode 100644 _sources/models/data.rst.txt create mode 100644 _sources/models/datasource.rst.txt create mode 100644 _sources/models/entry.rst.txt create mode 100644 _sources/models/keyword.rst.txt create mode 100644 _sources/models/license.rst.txt create mode 100644 _sources/models/location.rst.txt create mode 100644 _sources/models/models.rst.txt create mode 100644 _sources/models/person.rst.txt create mode 100644 _sources/models/result.rst.txt create mode 100644 _sources/models/variable.rst.txt create mode 100644 _static/_sphinx_javascript_frameworks_compat.js create mode 100644 _static/basic.css create mode 100644 _static/binder_badge_logo.svg create mode 100644 _static/brand.png create mode 100644 _static/broken_example.png create mode 100644 _static/css/custom.css create mode 100644 _static/doctools.js create mode 100644 _static/documentation_options.js create mode 100644 _static/file.png create mode 100644 _static/jquery-3.6.0.js create mode 100644 _static/jquery.js create mode 100644 _static/jupyterlite_badge_logo.svg create mode 100644 _static/language_data.js create mode 100644 _static/minus.png create mode 100644 _static/nbsphinx-broken-thumbnail.svg create mode 100644 _static/nbsphinx-code-cells.css create mode 100644 _static/nbsphinx-gallery.css create mode 100644 _static/nbsphinx-no-thumbnail.svg create mode 100644 _static/no_image.png create mode 100644 _static/plus.png create mode 100644 _static/pygments.css create mode 100644 _static/scripts/bootstrap.js create mode 100644 _static/scripts/bootstrap.js.LICENSE.txt create mode 100644 _static/scripts/bootstrap.js.map create mode 100644 _static/scripts/pydata-sphinx-theme.js create mode 100644 _static/scripts/pydata-sphinx-theme.js.map create mode 100644 _static/searchtools.js create mode 100644 _static/sg_gallery-binder.css create mode 100644 _static/sg_gallery-dataframe.css create mode 100644 _static/sg_gallery-rendered-html.css create mode 100644 _static/sg_gallery.css create mode 100644 _static/sphinx_highlight.js create mode 100644 _static/styles/bootstrap.css create mode 100644 _static/styles/bootstrap.css.map create mode 100644 _static/styles/pydata-sphinx-theme.css create mode 100644 _static/styles/pydata-sphinx-theme.css.map create mode 100644 _static/styles/theme.css create mode 100644 _static/underscore-1.13.1.js create mode 100644 _static/underscore.js create mode 100644 _static/vendor/fontawesome/6.5.1/LICENSE.txt create mode 100644 _static/vendor/fontawesome/6.5.1/css/all.min.css create mode 100644 _static/vendor/fontawesome/6.5.1/js/all.min.js create mode 100644 _static/vendor/fontawesome/6.5.1/js/all.min.js.LICENSE.txt create mode 100644 _static/vendor/fontawesome/6.5.1/webfonts/fa-brands-400.ttf create mode 100644 _static/vendor/fontawesome/6.5.1/webfonts/fa-brands-400.woff2 create mode 100644 _static/vendor/fontawesome/6.5.1/webfonts/fa-regular-400.ttf create mode 100644 _static/vendor/fontawesome/6.5.1/webfonts/fa-regular-400.woff2 create mode 100644 _static/vendor/fontawesome/6.5.1/webfonts/fa-solid-900.ttf create mode 100644 _static/vendor/fontawesome/6.5.1/webfonts/fa-solid-900.woff2 create mode 100644 _static/vendor/fontawesome/6.5.1/webfonts/fa-v4compatibility.ttf create mode 100644 _static/vendor/fontawesome/6.5.1/webfonts/fa-v4compatibility.woff2 create mode 100644 _static/webpack-macros.html create mode 100644 api/api.html create mode 100644 api/api_add_entry.html create mode 100644 api/api_add_entry.ipynb create mode 100644 api/api_add_group.html create mode 100644 api/api_add_group.ipynb create mode 100644 api/api_add_keyword.html create mode 100644 api/api_add_keyword.ipynb create mode 100644 api/api_add_license.html create mode 100644 api/api_add_license.ipynb create mode 100644 api/api_add_person.html create mode 100644 api/api_add_person.ipynb create mode 100644 api/api_add_thesaurus.html create mode 100644 api/api_add_thesaurus.ipynb create mode 100644 api/api_add_variable.html create mode 100644 api/api_add_variable.ipynb create mode 100644 api/api_connect_database.html create mode 100644 api/api_connect_database.ipynb create mode 100644 api/api_create_tables.html create mode 100644 api/api_create_tables.ipynb create mode 100644 api/api_find_entry.html create mode 100644 api/api_find_entry.ipynb create mode 100644 api/api_find_keyword.html create mode 100644 api/api_find_keyword.ipynb create mode 100644 api/api_find_license.html create mode 100644 api/api_find_license.ipynb create mode 100644 api/api_find_person.html create mode 100644 api/api_find_person.ipynb create mode 100644 api/api_find_thesaurus.html create mode 100644 api/api_find_thesaurus.ipynb create mode 100644 api/api_find_variable.html create mode 100644 api/api_find_variable.ipynb create mode 100644 api/api_get_logger.html create mode 100644 api/api_get_logger.ipynb create mode 100644 api/api_get_uuid.html create mode 100644 api/api_get_uuid.ipynb create mode 100644 api/api_populate_defaults.html create mode 100644 api/api_populate_defaults.ipynb create mode 100644 api/api_show_attributes.html create mode 100644 api/api_show_attributes.ipynb create mode 100644 api/api_show_records.html create mode 100644 api/api_show_records.ipynb create mode 100644 cli/cli.html create mode 100644 cli/cli_add.html create mode 100644 cli/cli_add.ipynb create mode 100644 cli/cli_create.html create mode 100644 cli/cli_create.ipynb create mode 100644 cli/cli_find.html create mode 100644 cli/cli_find.ipynb create mode 100644 cli/cli_init.html create mode 100644 cli/cli_init.ipynb create mode 100644 cli/cli_populate.html create mode 100644 cli/cli_populate.ipynb create mode 100644 cli/cli_show.html create mode 100644 cli/cli_show.ipynb create mode 100644 cli/cli_uuid.html create mode 100644 cli/cli_uuid.ipynb create mode 100644 dev/dev.html create mode 100644 dev/iso19115.html create mode 100644 dev/tests.html create mode 100644 ext/custom.html create mode 100644 ext/export.html create mode 100644 ext/ext.html create mode 100644 ext/io.html create mode 100644 ext/standards_export.html create mode 100644 genindex.html create mode 100644 home/getting_started.html create mode 100644 home/home.html create mode 100644 home/install.html create mode 100644 home/scheme.html create mode 100644 index.html create mode 100644 models/data.html create mode 100644 models/datasource.html create mode 100644 models/entry.html create mode 100644 models/keyword.html create mode 100644 models/license.html create mode 100644 models/location.html create mode 100644 models/models.html create mode 100644 models/person.html create mode 100644 models/result.html create mode 100644 models/variable.html create mode 100644 objects.inv create mode 100644 py-modindex.html create mode 100644 search.html create mode 100644 searchindex.js diff --git a/.buildinfo b/.buildinfo new file mode 100644 index 00000000..f0a1895d --- /dev/null +++ b/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. +config: 631d9aeb8ec865c8f52d2ada4de9b661 +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/_sources/api/api.rst.txt b/_sources/api/api.rst.txt new file mode 100644 index 00000000..ecdf9fe6 --- /dev/null +++ b/_sources/api/api.rst.txt @@ -0,0 +1,46 @@ +=== +API +=== + +.. toctree:: + :maxdepth: 1 + :Caption: API Overview + :hidden: + + api_connect_database + api_create_tables + api_populate_defaults + api_add_entry + api_add_person + api_add_variable + api_add_group + api_add_license + api_add_keyword + api_add_thesaurus + api_find_entry + api_find_person + api_find_variable + api_find_license + api_find_keyword + api_find_thesaurus + api_show_attributes + api_show_records + api_get_uuid + api_get_logger + +Metacatalog API Overview +======================== + +.. automodule:: metacatalog.api + + +Command Overview +================ + +.. nbgallery:: + :caption: Command Overview + :name: api-galery + :glob: + :reversed: + + api_* \ No newline at end of file diff --git a/_sources/api/api_add_entry.ipynb.txt b/_sources/api/api_add_entry.ipynb.txt new file mode 100644 index 00000000..2ce1cd9d --- /dev/null +++ b/_sources/api/api_add_entry.ipynb.txt @@ -0,0 +1,82 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Add Entry" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Entry" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.add_entry" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Add Entry details" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.add_details_to_entries" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Add keywords to entries" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.add_keywords_to_entries\n", + " :noindex:" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/_sources/api/api_add_group.ipynb.txt b/_sources/api/api_add_group.ipynb.txt new file mode 100644 index 00000000..0ea1c5d0 --- /dev/null +++ b/_sources/api/api_add_group.ipynb.txt @@ -0,0 +1,80 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Add EntryGroup" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Group\n", + "\n", + "The grouping of existing Entries can be done by the `api.add_group` endpoint. This function also takes a specification of the type of group. Additionally, there is a `api.add_project` function for the special case of adding a group of type `'Project'`. " + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. note::\n", + "\n", + " ``'Composite'`` entries are best added by the \n", + " `model interface `_ of one of the entries.\n", + " Use the ``Entry.make_composite`` function for that." + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.add_group" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Project" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.add_project" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/_sources/api/api_add_keyword.ipynb.txt b/_sources/api/api_add_keyword.ipynb.txt new file mode 100644 index 00000000..203bb8d9 --- /dev/null +++ b/_sources/api/api_add_keyword.ipynb.txt @@ -0,0 +1,65 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Add keyword" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Keyword" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.add_keyword" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Add keywords to entries" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.add_keywords_to_entries" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/_sources/api/api_add_license.ipynb.txt b/_sources/api/api_add_license.ipynb.txt new file mode 100644 index 00000000..c28c349c --- /dev/null +++ b/_sources/api/api_add_license.ipynb.txt @@ -0,0 +1,42 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Add license" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.add_license" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/_sources/api/api_add_person.ipynb.txt b/_sources/api/api_add_person.ipynb.txt new file mode 100644 index 00000000..d5ca80b4 --- /dev/null +++ b/_sources/api/api_add_person.ipynb.txt @@ -0,0 +1,65 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Add Person" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Person" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.add_person" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Add persons to entries" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.add_persons_to_entries" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/_sources/api/api_add_thesaurus.ipynb.txt b/_sources/api/api_add_thesaurus.ipynb.txt new file mode 100644 index 00000000..bce1ee33 --- /dev/null +++ b/_sources/api/api_add_thesaurus.ipynb.txt @@ -0,0 +1,42 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Add Thesaurus" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.add_thesaurus" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/_sources/api/api_add_variable.ipynb.txt b/_sources/api/api_add_variable.ipynb.txt new file mode 100644 index 00000000..1f8aea8d --- /dev/null +++ b/_sources/api/api_add_variable.ipynb.txt @@ -0,0 +1,65 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Add variable and unit" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## variable" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.add_variable" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## unit" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.add_unit" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/_sources/api/api_connect_database.ipynb.txt b/_sources/api/api_connect_database.ipynb.txt new file mode 100644 index 00000000..97c013c4 --- /dev/null +++ b/_sources/api/api_connect_database.ipynb.txt @@ -0,0 +1,185 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Connect database" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.connect_database " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Stored default connection" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Engine(postgresql://postgres:***@localhost:5432/metacatalog)\n" + ] + } + ], + "source": [ + "from metacatalog import api\n", + "session = api.connect_database()\n", + "\n", + "print(session)\n", + "print(session.bind)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Connect without storing passwords" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In many productions scenarios you don't want to store a password in cleartext to any kind of file, even not in a trusted environment. Usually, you will set the password as an environment variable. In Windows you do that like:\n", + "\n", + "```powershell\n", + "set POSTGRES_PASSWORD=notmyrealpassword\n", + "```\n", + "\n", + "In Linux like:\n", + "\n", + "```sh\n", + "export POSTGRES_PASSWORD=notmyrealpassword\n", + "```\n", + "\n", + "In CIs like Travis CI, CirleCI or Github actions you can usually set them in the repositories settings. " + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. warning::\n", + " \n", + " If you are using a CI, remember to **not** set the password environment variable into any kind of yaml config files, which usually also define a section on envirnment variables. These yaml configs are often public!" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "env: POSTGRES_PASSWORD=notmyrealpassword\n" + ] + } + ], + "source": [ + "%env POSTGRES_PASSWORD=notmyrealpassword" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Then, istead of loading stored connection strings from the `~/.metacatalog/config.json`, you can pass the connection URI directly to `connect_database`:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Engine(postgresql://dbuser:***@localhost:5432/metacatalg)\n" + ] + } + ], + "source": [ + "import os\n", + "session = api.connect_database(\n", + " 'postgresql://dbuser:{pw}@localhost:5432/metacatalg'.format(pw=os.environ['POSTGRES_PASSWORD'])\n", + ")\n", + "\n", + "print(session)\n", + "print(session.bind)" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. warning::\n", + "\n", + " Remember that IPython consoles cache all commands into a sqlite database. If you store the password first into a variable like: \n", + " \n", + " .. code-block:: python\n", + " \n", + " password = 'notmyrealpassword'\n", + " api.connect_database('...'.format(pw=password))\n", + " \n", + " Your password will be in the cache then!" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "finalized": { + "timestamp": 1590313084911, + "trusted": true + }, + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/_sources/api/api_create_tables.ipynb.txt b/_sources/api/api_create_tables.ipynb.txt new file mode 100644 index 00000000..47cdafa3 --- /dev/null +++ b/_sources/api/api_create_tables.ipynb.txt @@ -0,0 +1,42 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Create Tables" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.create_tables" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/_sources/api/api_find_entry.ipynb.txt b/_sources/api/api_find_entry.ipynb.txt new file mode 100644 index 00000000..6c30a24a --- /dev/null +++ b/_sources/api/api_find_entry.ipynb.txt @@ -0,0 +1,88 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Find Entry" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `Entry` is the main and most atomic entity in metacatalog. A set of metadata that describes one datasource of unique data type is described as `Entry`. In cases, where more than one `Entry` need to be grouped together to provide useful information, a `n:m` relation from `Entry` to `EntryGroup`s can be created. Depending on the kind of connection between the entries, `EntryGroup`s are futher described by `EntryGroupType`s." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Entry" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.find_entry" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Entry-Group" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.find_group" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Group type" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.find_group_type" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/_sources/api/api_find_keyword.ipynb.txt b/_sources/api/api_find_keyword.ipynb.txt new file mode 100644 index 00000000..30fbde5c --- /dev/null +++ b/_sources/api/api_find_keyword.ipynb.txt @@ -0,0 +1,95 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Find Keyword" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Help" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.find_keyword" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "ename": "CommandError", + "evalue": "Path doesn't exist: '/home/mirko/Dropbox/python/metacatalog/docs/source/api/alembic'. Please use the 'init' command to create a new scripts folder.", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mCommandError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mmetacatalog\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mapi\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0msession\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mapi\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconnect_database\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m~/Dropbox/python/metacatalog/metacatalog/api/db.py\u001b[0m in \u001b[0;36mconnect_database\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 55\u001b[0m \"\"\"\n\u001b[1;32m 56\u001b[0m \u001b[0;31m# get session\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 57\u001b[0;31m \u001b[0msession\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mget_session\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 58\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 59\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0msession\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Dropbox/python/metacatalog/metacatalog/db/session.py\u001b[0m in \u001b[0;36mget_session\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 98\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 99\u001b[0m \u001b[0;31m# else build a new engine\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 100\u001b[0;31m \u001b[0mengine\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mget_engine\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 101\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 102\u001b[0m \u001b[0;31m# create the Session class\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Dropbox/python/metacatalog/metacatalog/db/session.py\u001b[0m in \u001b[0;36mget_engine\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 81\u001b[0m \u001b[0;31m# check alembic version\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 82\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 83\u001b[0;31m \u001b[0mcheck_database_version\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mengine\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mengine\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 84\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mRuntimeError\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 85\u001b[0m \u001b[0;31m# no missmatch allowed\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Dropbox/python/metacatalog/metacatalog/db/migration.py\u001b[0m in \u001b[0;36mcheck_database_version\u001b[0;34m(engine)\u001b[0m\n\u001b[1;32m 31\u001b[0m \u001b[0;31m# get the alembic config file\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 32\u001b[0m \u001b[0mconfig\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mConfig\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mos\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mjoin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mBASEPATH\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'..'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'alembic.ini'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 33\u001b[0;31m \u001b[0mscript_\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mscript\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mScriptDirectory\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfrom_config\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mconfig\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 34\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 35\u001b[0m \u001b[0;31m# connect to database\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/miniconda3/envs/py37/lib/python3.7/site-packages/alembic-1.4.2-py3.7.egg/alembic/script/base.py\u001b[0m in \u001b[0;36mfrom_config\u001b[0;34m(cls, config)\u001b[0m\n\u001b[1;32m 147\u001b[0m \u001b[0mversion_locations\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mversion_locations\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 148\u001b[0m \u001b[0mtimezone\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mconfig\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_main_option\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"timezone\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 149\u001b[0;31m \u001b[0mhook_config\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mconfig\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_section\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"post_write_hooks\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m{\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 150\u001b[0m )\n\u001b[1;32m 151\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/miniconda3/envs/py37/lib/python3.7/site-packages/alembic-1.4.2-py3.7.egg/alembic/script/base.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, dir, file_template, truncate_slug_length, version_locations, sourceless, output_encoding, timezone, hook_config)\u001b[0m\n\u001b[1;32m 68\u001b[0m \u001b[0;34m\"Path doesn't exist: %r. Please use \"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 69\u001b[0m \u001b[0;34m\"the 'init' command to create a new \"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 70\u001b[0;31m \u001b[0;34m\"scripts folder.\"\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0mos\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mabspath\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdir\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 71\u001b[0m )\n\u001b[1;32m 72\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mCommandError\u001b[0m: Path doesn't exist: '/home/mirko/Dropbox/python/metacatalog/docs/source/api/alembic'. Please use the 'init' command to create a new scripts folder." + ] + } + ], + "source": [ + "from metacatalog import api\n", + "\n", + "session = api.connect_database()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "for kw in api.find_keyword(session, value='*TEMPERATURE*'):\n", + " print(kw.full_path)" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/_sources/api/api_find_license.ipynb.txt b/_sources/api/api_find_license.ipynb.txt new file mode 100644 index 00000000..a43a1c20 --- /dev/null +++ b/_sources/api/api_find_license.ipynb.txt @@ -0,0 +1,42 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Find License" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.find_license" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/_sources/api/api_find_person.ipynb.txt b/_sources/api/api_find_person.ipynb.txt new file mode 100644 index 00000000..602a5df3 --- /dev/null +++ b/_sources/api/api_find_person.ipynb.txt @@ -0,0 +1,135 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Find person" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Person" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.find_person" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Role" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.find_role" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Associations" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. note::\n", + "\n", + " Persons are not directly bound to an :class:`Entry `. They are related by an association that have to be further described by the type of contribution to the :class:`Entry `. Therefore, if an :class:`Entry ` is deleted, the :class:`Person ` will persist.\n", + " Finally, you can't search for contributions directly, you either have to find a Person and load the associated entries, or you need to find an entry and load the contributors." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Alfred, E. Neumann as author for Entry \n", + "Alfred, E. Neumann as author for Entry \n", + "Alfred, E. Neumann as author for Entry \n" + ] + } + ], + "source": [ + "from metacatalog import api\n", + "session = api.connect_database()\n", + "\n", + "# get the person\n", + "alfred =api.find_person(session, id=2)[0]\n", + "\n", + "for assoc in alfred.entries:\n", + " print(assoc)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "First author: Alfred, E. Neumann \n", + "all authors: Alfred, E. Neumann \n", + "Alfred, E. Neumann as author for Entry \n" + ] + } + ], + "source": [ + "# now starting from the entry side\n", + "entry = api.find_entry(session, id=20)[0]\n", + "\n", + "print('First author:', str(entry.author))\n", + "print('all authors: ', ', '.join([str(a) for a in entry.authors]))\n", + "for assoc in entry.contributors:\n", + " print(assoc)" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/_sources/api/api_find_thesaurus.ipynb.txt b/_sources/api/api_find_thesaurus.ipynb.txt new file mode 100644 index 00000000..bb4af8bc --- /dev/null +++ b/_sources/api/api_find_thesaurus.ipynb.txt @@ -0,0 +1,109 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Find Thesaurus" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Help" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.find_thesaurus" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from metacatalog import api\n", + "from pprint import pprint\n", + "\n", + "session = api.connect_database()" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'description': 'NASA Global Clime change Master Dictionary Science Keywords',\n", + " 'id': 1,\n", + " 'name': 'GCMD',\n", + " 'organisation': 'NASA',\n", + " 'title': 'NASA/GCMD Earth Science Keywords',\n", + " 'url': 'https://gcmdservices.gsfc.nasa.gov/kms/concepts/concept_scheme/sciencekeywords/?format=xml',\n", + " 'uuid': '2e54668d-8fae-429f-a511-efe529420b12'}\n" + ] + } + ], + "source": [ + "gcmd = api.find_thesaurus(session, name='GCMD')[0]\n", + "pprint(gcmd.to_dict())" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Associated Keywords found: 3206\n" + ] + } + ], + "source": [ + "print('Associated Keywords found: %d' % len(gcmd.keywords))" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/_sources/api/api_find_variable.ipynb.txt b/_sources/api/api_find_variable.ipynb.txt new file mode 100644 index 00000000..4e3a31e1 --- /dev/null +++ b/_sources/api/api_find_variable.ipynb.txt @@ -0,0 +1,83 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Find Variable" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. note::\n", + " \n", + " The terminology of *variable* might be reworked, as a variable is not taken in a strict sense of physical parameters, but also compound datasets. This might be reworked in a future version." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "An Entry is defined to be the atomic entitiy in metacatalog, by describing the metadata of **one** dataset of unique *Variable*. Each variable has **one** unit. If another dataset of same variable is uploaded, but uses a different unit, **it has to be converted** to avoid misinterpretations. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Variable" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.find_variable" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Unit" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.find_unit" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/_sources/api/api_get_logger.ipynb.txt b/_sources/api/api_get_logger.ipynb.txt new file mode 100644 index 00000000..71317806 --- /dev/null +++ b/_sources/api/api_get_logger.ipynb.txt @@ -0,0 +1,180 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Logging\n", + "\n", + "With version `0.3.4`, a new logging module is available. `metacatalog` uses the buildin [`logging`](https://docs.python.org/3/howto/logging.html) module, by exposing a [new Handler](https://docs.python.org/3/library/logging.handlers.html). Hence, you can use the standard procedures in Python to set the Log level or add as many other handlers as necessary.\n", + "\n", + "The logger can be loaded through the API. On first creation, it needs the session object to the database. Any consecutive call to the logger can be done by `logging.getLogger`, like you would do it with any other logger. The `metacatalog` logger is simply named `'metacatalog'`." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from metacatalog import api\n", + "\n", + "# get a DB session\n", + "session = api.connect_database()\n", + "\n", + "# get the logger\n", + "logger = api.get_logger(session)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "From here on, you can use the logger just like any other `logging.Logger`." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# with default settings, this will be ignored\n", + "logger.info('Ignored message')\n", + "\n", + "# only warning and error will be ignored\n", + "logger.warning('Serious warning raised in the documentation')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "After first creation, it is also possible to load the logger from `logging`, i.e. in other files, but within the same Python session" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n" + ] + } + ], + "source": [ + "import logging\n", + "\n", + "same_logger = logging.getLogger('metacatalog')\n", + "\n", + "print(logger)\n", + "print(same_logger)\n", + "\n", + "same_logger.error('An error raised in the documentation')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Finally, use the `models.Log` class to load the last few messages." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[ERROR]: An error raised in the documentation (2021-06-07T10:06:08.658991)\n", + "[WARNING]: Serious warning raised in the documentation (2021-06-07T10:02:22.334275)\n", + "[MIGRATION]: Migrated database to 7 using metacatalog==0.3.3 (2021-06-07T07:42:08.686526)\n" + ] + } + ], + "source": [ + "from metacatalog import models\n", + "\n", + "for log in models.Log.load_last(session, n=3):\n", + " print(log)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As you can see, the error and warning messages were logged into the database, but the info message was ignored, as the log level is set to warning by default." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[INFO]: Ignored message - not ignored this time (2021-06-07T10:09:01.311257)\n", + "[ERROR]: An error raised in the documentation (2021-06-07T10:06:08.658991)\n", + "[WARNING]: Serious warning raised in the documentation (2021-06-07T10:02:22.334275)\n" + ] + } + ], + "source": [ + "logger.setLevel(10) # set to debug\n", + "logger.info('Ignored message - not ignored this time')\n", + "\n", + "\n", + "for log in models.Log.load_last(session, n=3):\n", + " print(log)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Function" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.get_logger" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/_sources/api/api_get_uuid.ipynb.txt b/_sources/api/api_get_uuid.ipynb.txt new file mode 100644 index 00000000..89a01815 --- /dev/null +++ b/_sources/api/api_get_uuid.ipynb.txt @@ -0,0 +1,138 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Get UUID\n", + "\n", + "The `api.get_uuid` api endpoint is a utility function for finding database entities across database models." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example\n", + "\n", + "We can easily find a specific record by UUID. If you [populated](api_populate_defaults.ipynb) the default `Keyword`, you will find a record of UUID `885735f3-121e-4ca0-ac8b-f37dbc972f03`. The `get_uuid` will figure out, what kind of database model has to be loaded." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "EARTH SCIENCE > TERRESTRIAL HYDROSPHERE \n" + ] + } + ], + "source": [ + "from metacatalog import api\n", + "\n", + "session = api.connect_database()\n", + "keyword = api.get_uuid(session, uuid='885735f3-121e-4ca0-ac8b-f37dbc972f03')\n", + "\n", + "print(keyword)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "While `Keyword` is already capable of loading all child keywords recursively, we can easily rebuild this function using the `get_uuid` function. This can be very helpful, when a list of objects needs to be loaded from `metacatalog`." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Parent object\n", + "########################################\n", + "{\n", + " \"id\": 25,\n", + " \"uuid\": \"885735f3-121e-4ca0-ac8b-f37dbc972f03\",\n", + " \"value\": \"TERRESTRIAL HYDROSPHERE\",\n", + " \"path\": \"EARTH SCIENCE > TERRESTRIAL HYDROSPHERE\",\n", + " \"children\": [\n", + " \"099ab1ae-f4d2-48cc-be2f-86bd58ffc4ca\",\n", + " \"734f8f27-6976-4b67-8794-c7fc79d6161e\",\n", + " \"50b8fe04-9149-4b7f-a8b2-b33b1e3aa192\",\n", + " \"5debb283-51e4-435e-b2a2-e8e2a977220d\",\n", + " \"8c02f5d1-ce86-4bf5-84d5-b3496cdba6ad\"\n", + " ],\n", + " \"thesaurus_id\": 1\n", + "}\n", + "\n", + "Children:\n", + "########################################\n", + "EARTH SCIENCE > TERRESTRIAL HYDROSPHERE > GLACIERS/ICE SHEETS\n", + "EARTH SCIENCE > TERRESTRIAL HYDROSPHERE > GROUND WATER\n", + "EARTH SCIENCE > TERRESTRIAL HYDROSPHERE > SNOW/ICE\n", + "EARTH SCIENCE > TERRESTRIAL HYDROSPHERE > SURFACE WATER\n", + "EARTH SCIENCE > TERRESTRIAL HYDROSPHERE > WATER QUALITY/WATER CHEMISTRY\n" + ] + } + ], + "source": [ + "import json\n", + "kw_dict = keyword.to_dict(deep=False)\n", + "\n", + "print('Parent object\\n' + '#' * 40)\n", + "print(json.dumps(kw_dict, indent=4))\n", + "\n", + "# load the children\n", + "print('\\nChildren:\\n' + '#' * 40)\n", + "for uuid in kw_dict.get('children', []):\n", + " print(api.get_uuid(session, uuid=uuid).full_path)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Funtion" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.get_uuid" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/_sources/api/api_populate_defaults.ipynb.txt b/_sources/api/api_populate_defaults.ipynb.txt new file mode 100644 index 00000000..57038277 --- /dev/null +++ b/_sources/api/api_populate_defaults.ipynb.txt @@ -0,0 +1,42 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Populate defaults" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.populate_defaults" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/_sources/api/api_show_attributes.ipynb.txt b/_sources/api/api_show_attributes.ipynb.txt new file mode 100644 index 00000000..1c418a2d --- /dev/null +++ b/_sources/api/api_show_attributes.ipynb.txt @@ -0,0 +1,110 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Show Attributes" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. note::\n", + "\n", + " The ``show_`` endpoints are meant to be used by admins to build functionality into their metacatalog deployment. These endpoints are not really helpful for the end-user." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Help" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.show_attributes" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. note::\n", + "\n", + " ``api.show_attribute`` is one of the very few api endpoints that don't need a session to connect to the database, as it is using the models to collect the information requested. Thus, you don't need actual database connection to use this function." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from metacatalog import api" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('id', Integer()),\n", + " ('first_name', String(length=128)),\n", + " ('last_name', String(length=128)),\n", + " ('affiliation', String(length=1024))]" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "api.show_attributes(table_name='persons', add_type=True)" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/_sources/api/api_show_records.ipynb.txt b/_sources/api/api_show_records.ipynb.txt new file mode 100644 index 00000000..e6c67037 --- /dev/null +++ b/_sources/api/api_show_records.ipynb.txt @@ -0,0 +1,290 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Show Records" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. note::\n", + "\n", + " The ``show_`` endpoints are meant to be used by admins to build functionality into their metacatalog deployment. These endpoints are not really helpful for the end-user." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Help" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.show_records" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. note::\n", + "\n", + " This endpoint is not meant to be used to load metadata from the database in a production scenario. You can use it for backups or during development. It will return the raw table contents. To actually use the metadata, use the ``metacatalog.models`` classes or the ``api.find_*`` endpoints." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from metacatalog import api\n", + "\n", + "session = api.connect_database()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'id': 1,\n", + " 'title': 'Sap Flow - Hohes Holz - Tree 022',\n", + " 'abstract': 'Sap flow tree 022- 20mm- east',\n", + " 'external_id': '212',\n", + " 'location': '01010000006806F1811D0B4A4002452C62D8712640',\n", + " 'geom': None,\n", + " 'creation': None,\n", + " 'end': None,\n", + " 'version': 1,\n", + " 'latest_version_id': None,\n", + " 'comment': 'not yet converted',\n", + " 'license_id': 2,\n", + " 'variable_id': 14,\n", + " 'datasource_id': 1,\n", + " 'embargo': True,\n", + " 'embargo_end': datetime.datetime(2022, 5, 15, 12, 43, 57, 712110),\n", + " 'publication': datetime.datetime(2020, 5, 15, 12, 43, 57, 712195),\n", + " 'lastUpdate': datetime.datetime(2020, 5, 15, 13, 17, 47, 540713)},\n", + " {'id': 2,\n", + " 'title': 'Sap Flow - Hohes Holz - Tree 022',\n", + " 'abstract': 'Sap flow tree 022- 20mm- north-west',\n", + " 'external_id': '214',\n", + " 'location': '01010000006806F1811D0B4A4002452C62D8712640',\n", + " 'geom': None,\n", + " 'creation': None,\n", + " 'end': None,\n", + " 'version': 1,\n", + " 'latest_version_id': None,\n", + " 'comment': 'not yet converted',\n", + " 'license_id': 2,\n", + " 'variable_id': 14,\n", + " 'datasource_id': 2,\n", + " 'embargo': True,\n", + " 'embargo_end': datetime.datetime(2022, 5, 15, 12, 43, 57, 749350),\n", + " 'publication': datetime.datetime(2020, 5, 15, 12, 43, 57, 749407),\n", + " 'lastUpdate': datetime.datetime(2020, 5, 15, 13, 17, 47, 565270)},\n", + " {'id': 3,\n", + " 'title': 'Sap Flow - Hohes Holz - Tree 029',\n", + " 'abstract': 'Sap flow tree 029- 20mm- north',\n", + " 'external_id': '246',\n", + " 'location': '0101000000761C3F541A0B4A4099F04BFDBC712640',\n", + " 'geom': None,\n", + " 'creation': None,\n", + " 'end': None,\n", + " 'version': 1,\n", + " 'latest_version_id': None,\n", + " 'comment': 'not yet converted',\n", + " 'license_id': 2,\n", + " 'variable_id': 14,\n", + " 'datasource_id': 3,\n", + " 'embargo': True,\n", + " 'embargo_end': datetime.datetime(2022, 5, 15, 12, 43, 57, 770584),\n", + " 'publication': datetime.datetime(2020, 5, 15, 12, 43, 57, 770633),\n", + " 'lastUpdate': datetime.datetime(2020, 5, 15, 13, 17, 47, 576064)},\n", + " {'id': 4,\n", + " 'title': 'Sap Flow - Hohes Holz - Tree 048',\n", + " 'abstract': 'Sap flow tree 048- 20mm- north 2015',\n", + " 'external_id': '269',\n", + " 'location': '01010000008BA8893E1F0B4A400B24287E8C712640',\n", + " 'geom': None,\n", + " 'creation': None,\n", + " 'end': None,\n", + " 'version': 1,\n", + " 'latest_version_id': None,\n", + " 'comment': 'nan',\n", + " 'license_id': 2,\n", + " 'variable_id': 14,\n", + " 'datasource_id': 4,\n", + " 'embargo': True,\n", + " 'embargo_end': datetime.datetime(2022, 5, 15, 12, 43, 57, 789248),\n", + " 'publication': datetime.datetime(2020, 5, 15, 12, 43, 57, 789294),\n", + " 'lastUpdate': datetime.datetime(2020, 5, 15, 13, 17, 47, 587474)},\n", + " {'id': 5,\n", + " 'title': 'Sap Flow - Hohes Holz - Tree 050',\n", + " 'abstract': 'Sap flow tree 050- 20mm- east',\n", + " 'external_id': '275',\n", + " 'location': '0101000000C11DA8531E0B4A404A26A77686712640',\n", + " 'geom': None,\n", + " 'creation': None,\n", + " 'end': None,\n", + " 'version': 1,\n", + " 'latest_version_id': None,\n", + " 'comment': 'nan',\n", + " 'license_id': 2,\n", + " 'variable_id': 14,\n", + " 'datasource_id': 5,\n", + " 'embargo': True,\n", + " 'embargo_end': datetime.datetime(2022, 5, 15, 12, 43, 57, 807256),\n", + " 'publication': datetime.datetime(2020, 5, 15, 12, 43, 57, 807302),\n", + " 'lastUpdate': datetime.datetime(2020, 5, 15, 13, 17, 47, 598154)},\n", + " {'id': 6,\n", + " 'title': 'Sap Flow - Hohes Holz - Tree 056',\n", + " 'abstract': 'Sap flow tree 056- 20mm- north',\n", + " 'external_id': '282',\n", + " 'location': '01010000003FA7203F1B0B4A4036E50AEF72712640',\n", + " 'geom': None,\n", + " 'creation': None,\n", + " 'end': None,\n", + " 'version': 1,\n", + " 'latest_version_id': None,\n", + " 'comment': 'nan',\n", + " 'license_id': 2,\n", + " 'variable_id': 14,\n", + " 'datasource_id': 6,\n", + " 'embargo': True,\n", + " 'embargo_end': datetime.datetime(2022, 5, 15, 12, 43, 57, 825347),\n", + " 'publication': datetime.datetime(2020, 5, 15, 12, 43, 57, 825393),\n", + " 'lastUpdate': datetime.datetime(2020, 5, 15, 13, 17, 47, 608043)},\n", + " {'id': 7,\n", + " 'title': 'Sap Flow - Hohes Holz - Tree 057',\n", + " 'abstract': 'Sap flow tree 057- 20mm- north',\n", + " 'external_id': '292',\n", + " 'location': '01010000009335EA211A0B4A40D6743DD175712640',\n", + " 'geom': None,\n", + " 'creation': None,\n", + " 'end': None,\n", + " 'version': 1,\n", + " 'latest_version_id': None,\n", + " 'comment': 'nan',\n", + " 'license_id': 2,\n", + " 'variable_id': 14,\n", + " 'datasource_id': 7,\n", + " 'embargo': True,\n", + " 'embargo_end': datetime.datetime(2022, 5, 15, 12, 43, 57, 844292),\n", + " 'publication': datetime.datetime(2020, 5, 15, 12, 43, 57, 844339),\n", + " 'lastUpdate': datetime.datetime(2020, 5, 15, 13, 17, 47, 618236)},\n", + " {'id': 8,\n", + " 'title': 'Sap Flow - Hohes Holz - Tree 058',\n", + " 'abstract': 'Sap flow tree 058- 20mm- east-north-east',\n", + " 'external_id': '306',\n", + " 'location': '0101000000A46C91B41B0B4A40807EDFBF79712640',\n", + " 'geom': None,\n", + " 'creation': None,\n", + " 'end': None,\n", + " 'version': 1,\n", + " 'latest_version_id': None,\n", + " 'comment': 'nan',\n", + " 'license_id': 2,\n", + " 'variable_id': 14,\n", + " 'datasource_id': 8,\n", + " 'embargo': True,\n", + " 'embargo_end': datetime.datetime(2022, 5, 15, 12, 43, 57, 863693),\n", + " 'publication': datetime.datetime(2020, 5, 15, 12, 43, 57, 863740),\n", + " 'lastUpdate': datetime.datetime(2020, 5, 15, 13, 17, 47, 627976)},\n", + " {'id': 9,\n", + " 'title': 'Sap Flow - Hohes Holz - Tree 106',\n", + " 'abstract': 'Sap flow tree 106 - 20mm - east - commertial',\n", + " 'external_id': '326',\n", + " 'location': '0101000000A87004A9140B4A40B79C4B7155712640',\n", + " 'geom': None,\n", + " 'creation': None,\n", + " 'end': None,\n", + " 'version': 1,\n", + " 'latest_version_id': None,\n", + " 'comment': 'commertial sensor in 20mm depth',\n", + " 'license_id': 2,\n", + " 'variable_id': 14,\n", + " 'datasource_id': 9,\n", + " 'embargo': True,\n", + " 'embargo_end': datetime.datetime(2022, 5, 15, 12, 43, 57, 884135),\n", + " 'publication': datetime.datetime(2020, 5, 15, 12, 43, 57, 884185),\n", + " 'lastUpdate': datetime.datetime(2020, 5, 15, 13, 17, 47, 637880)},\n", + " {'id': 10,\n", + " 'title': 'Sap Flow - Hohes Holz - Tree 108',\n", + " 'abstract': 'Sap flow tree 108 - 20mm - east-north-east - commertial',\n", + " 'external_id': '333',\n", + " 'location': '0101000000D828EB37130B4A403C855CA967712640',\n", + " 'geom': None,\n", + " 'creation': None,\n", + " 'end': None,\n", + " 'version': 1,\n", + " 'latest_version_id': None,\n", + " 'comment': 'commertial sensor in 20mm depth',\n", + " 'license_id': 2,\n", + " 'variable_id': 14,\n", + " 'datasource_id': 10,\n", + " 'embargo': True,\n", + " 'embargo_end': datetime.datetime(2022, 5, 15, 12, 43, 57, 904529),\n", + " 'publication': datetime.datetime(2020, 5, 15, 12, 43, 57, 904578),\n", + " 'lastUpdate': datetime.datetime(2020, 5, 15, 13, 17, 47, 647861)}]" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "api.show_records(session, table_name='entries', limit=10)" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/_sources/cli/cli.rst.txt b/_sources/cli/cli.rst.txt new file mode 100644 index 00000000..7fcde7ff --- /dev/null +++ b/_sources/cli/cli.rst.txt @@ -0,0 +1,85 @@ +=== +CLI +=== + +.. toctree:: + :maxdepth: 1 + :Caption: Command Overview + :hidden: + + cli_connection + cli_create + cli_populate + cli_init + cli_add + cli_find + cli_show + cli_uuid + +Command Line Interface +====================== + + +The `setup.py` used on installing metacatalog installed a command line script +which is also registered in the current anaconda environment (if used any). Therefore, just running + +.. code-block:: bash + + metacatalog + + +should work fine. +If you however experience problems, which seem to happen on Windows quite frequently, +there is also a CLI-like entrypoint into ``metacatalog``. You can use it like: + +.. code-block:: powershell + + python -m metacatalog + + +Under the hood, exactly the same script gets executed. + +Builtin Help +============= + +Like with most cli, you can pass the ``-h`` flag to any command and sub-command to show +the builtin help for the current command. + +.. code-block:: bash + + metacatalog -h + +renders: +:: + + usage: metacatalog [-h] {create,populate,init,connection,find,show,add} ... + + MetaCatalog management CLI + + optional arguments: + -h, --help show this help message and exit + + Commands: + CLI commands + + {create,populate,init,connection,find,show,add} + create Create a new Metacatalog instance. + populate Populate the database with default auxiliary data. + init Runs the create and and the populate command. + connection Manage stored connections + find Find records in the database on exact matches. + show Show database structure or records. + add Add new records to the database. Has to be combined + with one of the data origin flags. + + +Command Overview +================ + +.. nbgallery:: + :caption: Command Overview + :name: cmd-gallery + :glob: + :reversed: + + cli_* \ No newline at end of file diff --git a/_sources/cli/cli_add.ipynb.txt b/_sources/cli/cli_add.ipynb.txt new file mode 100644 index 00000000..860b64e7 --- /dev/null +++ b/_sources/cli/cli_add.ipynb.txt @@ -0,0 +1,421 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Add command" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Help\n", + "The help text for the `add` subcommand can be shown by passing the `-h` flag." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "usage: metacatalog add [-h] [--version] [--connection CONNECTION] [--verbose]\n", + " [--quiet] [--dev] [--logfile LOGFILE] [--csv CSV]\n", + " [--txt TXT] [--json JSON]\n", + " entity\n", + "\n", + "positional arguments:\n", + " entity Name of the record entity to be added.\n", + "\n", + "optional arguments:\n", + " -h, --help show this help message and exit\n", + " --version, -v Returns the module version\n", + " --connection CONNECTION, -C CONNECTION\n", + " Connection string to the database instance.Follows the\n", + " syntax: driver://user:password@host:port/database\n", + " --verbose, -V Activate extended output.\n", + " --quiet, -q Suppress any kind of output.\n", + " --dev Development mode. Unexpected errors will not be\n", + " handled and the full traceback is printed to the\n", + " screen.\n", + " --logfile LOGFILE If a file is given, output will be written to that\n", + " file instead of printed to StdOut.\n", + " --csv CSV Data Origin Flag. Pass a CSV filename or content\n", + " containing the data. Column header have to match the\n", + " ADD API keywords.\n", + " --txt TXT Data Origin Flag. Pass a text filename or content\n", + " containing whitespace separated key=value pairs where\n", + " key has to match the ADD API keywords. If used\n", + " directly remember to quote accordingly.\n", + " --json JSON Data Origin Flag. Pass a JSON filename or content\n", + " containing the data. Must contain a list of objects\n", + " matchin the ADD API keywords.\n" + ] + } + ], + "source": [ + "%%bash\n", + "metacatalog add -h" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Prerequisites\n", + "\n", + "The `add` command assumes that either [`create`](cli_create.ipynb) and [`populate`](cli_populate.ipynb) or [`init`](cli_init.ipynb) were executed successfully." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Usage\n", + "\n", + "### entity\n", + "\n", + "The `add` command has one positional argument `entity` that has to be provided. This is the name of the record entitiy that should be added. There is a dictionary in `metacatalog` that maps enitity names to database models:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'author': ,\n", + " 'contributor': ,\n", + " 'datasource': ,\n", + " 'datasource_type': ,\n", + " 'datasourcetype': ,\n", + " 'entry': ,\n", + " 'keyword': ,\n", + " 'license': ,\n", + " 'person': ,\n", + " 'person_role': ,\n", + " 'personrole': ,\n", + " 'unit': ,\n", + " 'variable': }\n" + ] + } + ], + "source": [ + "from metacatalog.api._mapping import ENTITY_MAPPING\n", + "from pprint import pprint\n", + "pprint(ENTITY_MAPPING)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Many entities map to the same model. This is either due to different spelling, or because the API creates database records in different contexts. E.g. the API forces the user to pass at least one *person* as the first author of an *Entry* on creation. The *contributors* are optional and can be added if applicable. All *person*s will, however, be saved into the same table." + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. warning::\n", + "\n", + " The CLI is operating at a much lower level than the Python API. Many semantical workflows which add data to the database include way more individual steps using the CLI." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### connection\n", + "\n", + "In case no default connection was created and saved, you have to supply a connection string to the database using the `--connection` flag. See [`connection`](cli_connection.ipynb) command." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### passing arguments\n", + "\n", + "Obviously, you need to pass the actual metadata, that should be stored in metacatalog. There are three data origin flags available: \n", + "\n", + "* `--csv` - comma separated\n", + "* `--txt` - key=value pairs\n", + "* --`json` - JSON\n", + "\n", + "All three flags accept either a filename (including path) to a file in the specified format, or the content itself.\n", + "Instead of creating a file and passing the filename:\n", + "\n", + "```csv\n", + "name,symbol\n", + "foo,F\n", + "Bar,B\n", + "```\n", + "\n", + "you can can also use the flag like: `--csv 'name,symbol\\nnfoo,F\\nbar,B'`. This might be the easier approach if only one or two records are added." + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. note::\n", + " \n", + " You can inspect entities using the `show` command." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Operations" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. note::\n", + "\n", + " This section assumes that you are familiar with the metacatalog data model. As the CLI just uses the Python API under the hood, you will have to refer to the API documentation for a full reference." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Most metadata creation task cannot be done with one call to add. Furthermore, some entities relate to records, that have to be added in the first place, to not violate relation constraints. A prior example is that a person has to exist in the database, before it can be placed as an author.\n", + "\n", + "A typical workflow is to add missing lookup data, which includes `variables,units,licenses,keywords` and `details`. Then, you create all `person`s involved. Finally, the metadata `Entry` can be added. For most lookup data, a `1:n` relation is modelled and you can pass anything accepted by the `find` api or the ID. \n", + "`keyword`s and `person`s are, however, modelled in a `m:n` relation, which has to be specified in a second step." + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. warning:: \n", + "\n", + " The ``details`` entity is not reflected in the CLI or API yet. \n", + " \n", + " The usage of passing other identifiers to `find` than the ID, is experimental and not fully functions. It might also change in a future release\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example\n", + "\n", + "The following example should illustrate a workflow for adding new meta-data.\n", + "At first we add a unit of `awesomeness` and a variable of `awesome` - because most of our data is awesome." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Using session: Engine(postgresql://postgres:***@localhost:5432/metacatalog)\n", + "Added 1 unit records.\n", + "Done.\n" + ] + } + ], + "source": [ + "%%bash\n", + "metacatalog add unit --csv 'name,symbol,si\\nawesomeness,a,m'" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Using session: Engine(postgresql://postgres:***@localhost:5432/metacatalog)\n", + "Added 1 variable records.\n", + "Done.\n" + ] + } + ], + "source": [ + "%%bash\n", + "#metacatalog show attributes variables\n", + "metacatalog add variable --csv 'name,symbol,unit\\nawesome,A,awesomeness'" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here, we passed the new newly created unit name to the `add variable` endpoint." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Using session: Engine(postgresql://postgres:***@localhost:5432/metacatalog)\n", + "Added 1 person records.\n", + "Done.\n" + ] + } + ], + "source": [ + "%%bash\n", + "metacatalog add person --json '[{\"first_name\": \"Alfred, E.\", \"last_name\": \"Neumann\", \"affiliation\": \"Institute of Awesomeness\"}]'" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Finally, we can create the Entries of Alfred, E.'s data from a json file:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "\n", + "meta = dict(\n", + " title=\"Alfred data\", \n", + " abstract=\"A dummy test entry, which Alfred created\",\n", + " location=(37.422051, -122.084615),\n", + " license=2,\n", + " embargo=True,\n", + " variable=\"awesome\",\n", + " author=\"Neumann\"\n", + ")\n", + "with open('alfred.json', 'w') as js:\n", + " json.dump([meta], js)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Using session: Engine(postgresql://postgres:***@localhost:5432/metacatalog)\n", + "Added 1 entry records.\n", + "Done.\n" + ] + } + ], + "source": [ + "%%bash\n", + "metacatalog add entry --json alfred.json\n", + "rm alfred.json" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And finally, using the [`find`](cli_find.ipynb) and [`show`](cli_show.ipynb) command we can inspect the newly created entry:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Using session: Engine(postgresql://postgres:***@localhost:5432/metacatalog)\n", + "\n", + "Using session: Engine(postgresql://postgres:***@localhost:5432/metacatalog)\n", + " id title abstract external_id location geom creation end version latest_version_id comment license_id variable_id datasource_id embargo embargo_end publication lastUpdate\n", + "---- -------------- --------------- ------------- --------------- ------ ---------- ----- --------- ------------------- --------- ------------ ------------- --------------- --------- -------------------------- -------------------------- --------------------------\n", + " 20 Alfred data... A dummy test... 01010000003F... 1 2 15 True 2022-05-22 05:45:24.827462 2020-05-22 05:45:24.827531 2020-05-22 05:45:24.827539\n" + ] + } + ], + "source": [ + "%%bash\n", + "metacatalog find entry --by title \"Alfred data\"\n", + "metacatalog show records entries --where \"id=20\" -T" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. note::\n", + " \n", + " In a future release, a set of flags will be added to the ``find`` command. These will make the export of found records into a file or as output to StdOut possbile. The ``show`` command is intended for raw table inspections only and just a workaround here. " + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "finalized": { + "timestamp": 1590129848476, + "trusted": false + }, + "kernelspec": { + "display_name": "base", + "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.9.13 (main, Aug 25 2022, 23:26:10) \n[GCC 11.2.0]" + }, + "vscode": { + "interpreter": { + "hash": "f54d8176e82297fa872ac8c77277e50c0e193f921954c1c4a0b1ae2e8be99b71" + } + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/_sources/cli/cli_create.ipynb.txt b/_sources/cli/cli_create.ipynb.txt new file mode 100644 index 00000000..aff1f687 --- /dev/null +++ b/_sources/cli/cli_create.ipynb.txt @@ -0,0 +1,175 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Create Command" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Help\n", + "The help text for the `create` subcommand can be shown by passing the `-h` flag." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "usage: metacatalog create [-h] [--version] [--connection CONNECTION]\n", + " [--verbose] [--quiet] [--dev] [--logfile LOGFILE]\n", + "\n", + "optional arguments:\n", + " -h, --help show this help message and exit\n", + " --version, -v Returns the module version\n", + " --connection CONNECTION, -C CONNECTION\n", + " Connection string to the database instance.Follows the\n", + " syntax: driver://user:password@host:port/database\n", + " --verbose, -V Activate extended output.\n", + " --quiet, -q Suppress any kind of output.\n", + " --dev Development mode. Unexpected errors will not be\n", + " handled and the full traceback is printed to the\n", + " screen.\n", + " --logfile LOGFILE If a file is given, output will be written to that\n", + " file instead of printed to StdOut.\n" + ] + } + ], + "source": [ + "%%bash\n", + "metacatalog create -h" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating tables" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The create command will create all necessary tables in the connected database. It will **not** fail if the tables are already present and create missing table. However, table changes are **not** reflected." + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. note::\n", + " \n", + " The ``create`` command has the purpose of creating metacatalog into a **fresh installation**. \n", + " If you want to update the database, refer to the migration section (place link here)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If no ``--connection`` or ``-C`` flag is given, the default connection URI will be loaded from metacatalog's config file." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Prerequisites" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Metacatalog does neither install the database, nor the PostGIS extension. We assume that the user specified in the connection string has granted full rights on the given database, but no super-user rights and no right to create addiontional databases. \n", + "Installing PostGIS does need further installation steps on the host system, which are dependent on the host OS. Refer to [PostGIS website](https://postgis.net/) for further explanations." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "With everything installed, the database and PostGIS extension can be created like:\n", + "\n", + "```SQL\n", + "CREATE DATABASE metacatalog;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Connect to that database and run:\n", + "\n", + "```sql\n", + "CREATE EXTENSTION postgis;\n", + "```\n", + "\n", + "You can verify that installation succeeded if the following query yields the installed version:\n", + "\n", + "```sql\n", + "SELECT PostGIS_full_version();\n", + "```\n", + "\n", + "```\n", + "\"POSTGIS=\"2.4.4 r16526\" PGSQL=\"100\" GEOS=\"3.7.1-CAPI-1.11.1 27a5e771\" PROJ=\"Rel. 4.9.3, 15 August 2016\" GDAL=\"GDAL 2.2.3, released 2017/11/20\" LIBXML=\"2.9.4\" LIBJSON=\"0.12.1\" LIBPROTOBUF=\"1.2.1\" RASTER\"\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Related commands\n", + "\n", + "In many scenarios you run rather the `metacatalog init` command, which runs `create` and `populate` at the same time. " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + "## See Also\n", + "\n", + "* [init command](cli_init.ipynb)\n", + "* [populate command](cli_populate.ipynb)" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/_sources/cli/cli_find.ipynb.txt b/_sources/cli/cli_find.ipynb.txt new file mode 100644 index 00000000..cedee0e4 --- /dev/null +++ b/_sources/cli/cli_find.ipynb.txt @@ -0,0 +1,302 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Find Command" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Help\n", + "The help text for the `find` subcommand can be shown by passing the `-h` flag." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "usage: metacatalog find [-h] [--version] [--connection CONNECTION] [--verbose]\n", + " [--quiet] [--dev] [--logfile LOGFILE] [--by BY BY]\n", + " [--json] [--stdout] [--csv]\n", + " entity\n", + "\n", + "positional arguments:\n", + " entity Name of the requested database entity.\n", + "\n", + "optional arguments:\n", + " -h, --help show this help message and exit\n", + " --version, -v Returns the module version\n", + " --connection CONNECTION, -C CONNECTION\n", + " Connection string to the database instance.Follows the\n", + " syntax: driver://user:password@host:port/database\n", + " --verbose, -V Activate extended output.\n", + " --quiet, -q Suppress any kind of output.\n", + " --dev Development mode. Unexpected errors will not be\n", + " handled and the full traceback is printed to the\n", + " screen.\n", + " --logfile LOGFILE If a file is given, output will be written to that\n", + " file instead of printed to StdOut.\n", + " --by BY BY key value pair to be used for finding record(s) in the\n", + " database. Flag can be used multiple times.\n", + " --json Output the found entities as JSON objects\n", + " --stdout Default option. Print the string representation of\n", + " found entities to StdOut.\n", + " --csv Output the found entities as CSV.\n" + ] + } + ], + "source": [ + "%%bash\n", + "metacatalog find -h" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Prerequists\n", + "\n", + "The `find` command assumes that either [`create`](cli_create.ipynb) and [`populate`](cli_populate.ipynb) or [`init`](cli_init.ipynb) were executed successfully." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Usage\n", + "\n", + "### entity" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. note::\n", + "\n", + " The CLI endpoint of ``find`` is just wrapping the Python API endpoint. The API is designed for building model instances, which is often not really helpful from the command line. In future releases, more database model clases will represent themselves correctly when printed to StdOut. Furthermore a set of *export flags* are planned, to export models into CSV or JSON files.\n", + " Until then, some entities might not turn out very helpful at the current state." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `find` command has one positional argument `entity` that has to be provided. This is the name of the record entitiy that should be `found`. There is a dictionary in `metacatalog` that maps enitity names to database models:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'datasource_types': ,\n", + " 'datasources': ,\n", + " 'entries': ,\n", + " 'entry_groups': ,\n", + " 'keywords': ,\n", + " 'licenses': ,\n", + " 'person_roles': ,\n", + " 'persons': ,\n", + " 'thesaurus': ,\n", + " 'units': ,\n", + " 'variables': }\n" + ] + } + ], + "source": [ + "from metacatalog.api._mapping import TABLE_MAPPING\n", + "from pprint import pprint\n", + "pprint(TABLE_MAPPING)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Many entities map to the same model. This is either due to different spelling, or because the API creates database records in different contexts. E.g. the API forces the user to pass at least one *person* as the first author of an *Entry* on creation. The *contributors* are optional and can be added if applicable. All *person*s will, however, be saved into the same table." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### connection\n", + "\n", + "In case no default connection was created and saved, you have to supply a connection string to the database using the `--connection` flag. See [`connection`](cli_connection.ipynb) command." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### passing arguments\n", + "\n", + "Arguments to filter for the correct records can be spcified by the `--by` flag. It's usage is optional. If no filter is set, **all** records will be returned, which might be a lot.\n", + "You can pass `--by` multiple times to create multiple filters. " + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. note::\n", + "\n", + " The ``find`` endpoint is not made for open searches and does not offer fine-granular filtering. Each filter passed is stacked **on top** of each other, effectively resulting in a logical **AND** connection. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `--by` flag requires exactly two arguments. The first is the column to filter and the second the value which has to be matched. It cannot perform *not*-filters and does not accept a `None` or `null`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Open Data Commons Open Database License \n" + ] + } + ], + "source": [ + "%%bash\n", + "metacatalog find licenses --by short_title ODbL" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Open Data Commons Open Database License \n" + ] + } + ], + "source": [ + "%%bash\n", + "metacatalog find licenses --by id 4" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Open Data Commons Open Database License \n", + "Open Data Commons Attribution License v1.0 \n", + "Creative Commons Attribution 4.0 International \n", + "Creative Commons Attribution-ShareAlike 4.0 International \n", + "Creative Commons Attribution-NonCommerical 4.0 International \n", + "Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International \n" + ] + } + ], + "source": [ + "%%bash \n", + "metacatalog find licenses --by by_attribution True" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ] + } + ], + "source": [ + "%%bash\n", + "metacatalog find entry" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "finalized": { + "timestamp": 1590129905396, + "trusted": true + }, + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/_sources/cli/cli_init.ipynb.txt b/_sources/cli/cli_init.ipynb.txt new file mode 100644 index 00000000..784f6b64 --- /dev/null +++ b/_sources/cli/cli_init.ipynb.txt @@ -0,0 +1,95 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Init command" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Help\n", + "The help text for the `init` subcommand can be shown by passing the `-h` flag." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "usage: metacatalog init [-h] [--version] [--connection CONNECTION] [--verbose]\n", + " [--quiet] [--dev] [--logfile LOGFILE]\n", + " [--ignore IGNORE [IGNORE ...]]\n", + "\n", + "optional arguments:\n", + " -h, --help show this help message and exit\n", + " --version, -v Returns the module version\n", + " --connection CONNECTION, -C CONNECTION\n", + " Connection string to the database instance.Follows the\n", + " syntax: driver://user:password@host:port/database\n", + " --verbose, -V Activate extended output.\n", + " --quiet, -q Suppress any kind of output.\n", + " --dev Development mode. Unexpected errors will not be\n", + " handled and the full traceback is printed to the\n", + " screen.\n", + " --logfile LOGFILE If a file is given, output will be written to that\n", + " file instead of printed to StdOut.\n", + " --ignore IGNORE [IGNORE ...], -I IGNORE [IGNORE ...]\n", + " List tables to be ignored for default population.\n" + ] + } + ], + "source": [ + "%%bash\n", + "metacatalog init -h" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Usage\n", + "\n", + "The `init` command just runs two other commands. First, `create` and then `populate`. Refer to their documentation for more info" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## See Also\n", + "\n", + "* [create command](cli_create.ipynb)\n", + "* [populate command](cli_populate.ipynb)" + ] + } + ], + "metadata": { + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/_sources/cli/cli_populate.ipynb.txt b/_sources/cli/cli_populate.ipynb.txt new file mode 100644 index 00000000..4618c276 --- /dev/null +++ b/_sources/cli/cli_populate.ipynb.txt @@ -0,0 +1,147 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Populate command" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Help\n", + "The help text for the `populate` subcommand can be shown by passing the `-h` flag." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "usage: metacatalog populate [-h] [--version] [--connection CONNECTION]\n", + " [--verbose] [--quiet] [--dev] [--logfile LOGFILE]\n", + " [--ignore IGNORE [IGNORE ...]]\n", + "\n", + "optional arguments:\n", + " -h, --help show this help message and exit\n", + " --version, -v Returns the module version\n", + " --connection CONNECTION, -C CONNECTION\n", + " Connection string to the database instance.Follows the\n", + " syntax: driver://user:password@host:port/database\n", + " --verbose, -V Activate extended output.\n", + " --quiet, -q Suppress any kind of output.\n", + " --dev Development mode. Unexpected errors will not be\n", + " handled and the full traceback is printed to the\n", + " screen.\n", + " --logfile LOGFILE If a file is given, output will be written to that\n", + " file instead of printed to StdOut.\n", + " --ignore IGNORE [IGNORE ...], -I IGNORE [IGNORE ...]\n", + " List tables to be ignored for default population.\n" + ] + } + ], + "source": [ + "%%bash\n", + "metacatalog populate -h" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Usage" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `populate` command will load some default values for a set of lookup tables into the (default) database instanstance defined. You can omit tables by passing their table names using the `--ignore` flag.\n", + "\n", + "The tables that have default values and will be imported are mapped to their model class in a dict called `IMPORTABLE_TABLES`, which is printed below:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'datasource_types': ,\n", + " 'datatypes': ,\n", + " 'entrygroup_types': ,\n", + " 'keywords': ,\n", + " 'licenses': ,\n", + " 'person_roles': ,\n", + " 'thesaurus': ,\n", + " 'units': ,\n", + " 'variables': }\n" + ] + } + ], + "source": [ + "from pprint import pprint\n", + "from metacatalog.api.db import IMPORTABLE_TABLES\n", + "pprint(IMPORTABLE_TABLES)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Prerequisites\n", + "\n", + "It is assumed that `create` was executed before." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Related commands\n", + "\n", + "In many scenarios you run rather the `metacatalog init` command, which runs `create` and `populate` at the same time. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## See also\n", + "\n", + "* [create command](cli_create.ipynb)\n", + "* [init command](cli_init.ipynb)" + ] + } + ], + "metadata": { + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/_sources/cli/cli_show.ipynb.txt b/_sources/cli/cli_show.ipynb.txt new file mode 100644 index 00000000..953077c6 --- /dev/null +++ b/_sources/cli/cli_show.ipynb.txt @@ -0,0 +1,105 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Show command" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Help\n", + "The help text for the `show` subcommand can be shown by passing the `-h` flag." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "usage: metacatalog show [-h] [--version] [--connection CONNECTION] [--verbose]\n", + " [--quiet] [--dev] [--logfile LOGFILE] [--names-only]\n", + " [--limit LIMIT] [--where WHERE] [--truncate]\n", + " {attributes,records} table\n", + "\n", + "positional arguments:\n", + " {attributes,records} Element to be shown. attributes Show table attributes.\n", + " records Show raw table records\n", + " table Table name.\n", + "\n", + "optional arguments:\n", + " -h, --help show this help message and exit\n", + " --version, -v Returns the module version\n", + " --connection CONNECTION, -C CONNECTION\n", + " Connection string to the database instance.Follows the\n", + " syntax: driver://user:password@host:port/database\n", + " --verbose, -V Activate extended output.\n", + " --quiet, -q Suppress any kind of output.\n", + " --dev Development mode. Unexpected errors will not be\n", + " handled and the full traceback is printed to the\n", + " screen.\n", + " --logfile LOGFILE If a file is given, output will be written to that\n", + " file instead of printed to StdOut.\n", + " --names-only Show only the attribute names. Only valid with\n", + " 'attribute' action.\n", + " --limit LIMIT, -L LIMIT\n", + " Only valid with 'records' action. Will limit the\n", + " number of records returned\n", + " --where WHERE Only valid with 'records' action. Raw SQL WHERE clause\n", + " to filter the results. Use carefully.\n", + " --truncate, -T Only valid with 'records' action. Truncates string\n", + " output to 12 signs.\n" + ] + } + ], + "source": [ + "%%bash\n", + "metacatalog show -h" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. warning::\n", + "\n", + " The ``show`` command might be completely replaced by a ``export`` command." + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "finalized": { + "timestamp": 1590129908857, + "trusted": true + }, + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/_sources/cli/cli_uuid.ipynb.txt b/_sources/cli/cli_uuid.ipynb.txt new file mode 100644 index 00000000..fc91ab0c --- /dev/null +++ b/_sources/cli/cli_uuid.ipynb.txt @@ -0,0 +1,246 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Uuid command" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Help\n", + "The help text for the `uuid` subcommand can be shown by passing the `-h` flag." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "usage: metacatalog uuid [-h] [--version] [--connection CONNECTION] [--verbose]\n", + " [--quiet] [--dev] [--logfile LOGFILE] [--json]\n", + " uuid\n", + "\n", + "positional arguments:\n", + " uuid Version 4 UUID of the requested resource\n", + "\n", + "optional arguments:\n", + " -h, --help show this help message and exit\n", + " --version, -v Returns the module version\n", + " --connection CONNECTION, -C CONNECTION\n", + " Connection string to the database instance.Follows the\n", + " syntax: driver://user:password@host:port/database\n", + " --verbose, -V Activate extended output.\n", + " --quiet, -q Suppress any kind of output.\n", + " --dev Development mode. Unexpected errors will not be\n", + " handled and the full traceback is printed to the\n", + " screen.\n", + " --logfile LOGFILE If a file is given, output will be written to that\n", + " file instead of printed to StdOut.\n", + " --json If set, the object will be returned as JSON\n" + ] + } + ], + "source": [ + "%%bash\n", + "metacatalog uuid -h" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Prerequisites\n", + "\n", + "The `uuid` command assumes that metadata records have been added using [`add`](cli_add.ipynb). " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Usage\n", + "\n", + "The `uuid` command can be used to search the database for an object of given UUID. `metacatalog` always refers to UUID version 4. The command will search records across models for the reqeusted resource. Currently, the following model instances can be found:\n", + "\n", + "* `Entry`\n", + "* `EntryGroup`\n", + "* `Keyword`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example\n", + "\n", + "The following example shows how a keyword can be found by UUID and related keywords can be searched.\n", + "If the database included the GCMD `Keywords` on [`init`](cli_init.ipynb), there will be an controlled keyword of UUID `885735f3-121e-4ca0-ac8b-f37dbc972f03`, which tags the hydrosphere." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Using session: Engine(postgresql://postgres:***@localhost:5432/metacatalog)\n", + "EARTH SCIENCE > TERRESTRIAL HYDROSPHERE\n" + ] + } + ], + "source": [ + "%%bash\n", + "metacatalog uuid 885735f3-121e-4ca0-ac8b-f37dbc972f03" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "By passing the `--json` flag, the output can be transformed to json, giving us more information" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Using session: Engine(postgresql://postgres:***@localhost:5432/metacatalog)\n", + "{\n", + " \"id\": 25,\n", + " \"uuid\": \"885735f3-121e-4ca0-ac8b-f37dbc972f03\",\n", + " \"value\": \"TERRESTRIAL HYDROSPHERE\",\n", + " \"path\": \"EARTH SCIENCE > TERRESTRIAL HYDROSPHERE\",\n", + " \"children\": [\n", + " \"099ab1ae-f4d2-48cc-be2f-86bd58ffc4ca\",\n", + " \"734f8f27-6976-4b67-8794-c7fc79d6161e\",\n", + " \"50b8fe04-9149-4b7f-a8b2-b33b1e3aa192\",\n", + " \"5debb283-51e4-435e-b2a2-e8e2a977220d\",\n", + " \"8c02f5d1-ce86-4bf5-84d5-b3496cdba6ad\"\n", + " ],\n", + " \"thesaurus_id\": 1\n", + "}\n" + ] + } + ], + "source": [ + "%%bash\n", + "metacatalog uuid --json 885735f3-121e-4ca0-ac8b-f37dbc972f03" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can easily check the children keywords:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Using session: Engine(postgresql://postgres:***@localhost:5432/metacatalog)\n", + "{\n", + " \"id\": 214,\n", + " \"uuid\": \"5debb283-51e4-435e-b2a2-e8e2a977220d\",\n", + " \"value\": \"SURFACE WATER\",\n", + " \"path\": \"EARTH SCIENCE > TERRESTRIAL HYDROSPHERE > SURFACE WATER\",\n", + " \"children\": [\n", + " \"1baa552d-c563-43fb-b618-54651f8b07e6\",\n", + " \"959f1861-a776-41b1-ba6b-d23c71d4d1eb\",\n", + " \"9d86cd70-062a-4c39-b3f3-226abebc07f7\",\n", + " \"c84b61fe-720a-4240-b6c8-8dcc9ae24a36\"\n", + " ],\n", + " \"thesaurus_id\": 1\n", + "}\n" + ] + } + ], + "source": [ + "%%bash \n", + "metacatalog uuid --json 5debb283-51e4-435e-b2a2-e8e2a977220d" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Using session: Engine(postgresql://postgres:***@localhost:5432/metacatalog)\n", + "{\n", + " \"id\": 6220,\n", + " \"uuid\": \"9d86cd70-062a-4c39-b3f3-226abebc07f7\",\n", + " \"value\": \"SURFACE WATER PROCESSES/MEASUREMENTS\",\n", + " \"path\": \"EARTH SCIENCE > TERRESTRIAL HYDROSPHERE > SURFACE WATER > SURFACE WATER PROCESSES/MEASUREMENTS\",\n", + " \"children\": [\n", + " \"3609b843-d840-460c-b1a3-d4fcc69a32f6\",\n", + " \"36a2999b-2255-4d4e-a249-40df3b7b3aaf\",\n", + " \"269c7277-fa8f-4c1c-bd8b-ab772c1df4e5\",\n", + " \"7fdc339e-017f-4e4b-89a3-12e441a40bad\",\n", + " \"960037c5-57b1-4cdf-84be-4542beee7d5a\",\n", + " \"d4e8b5c5-9203-4982-82bc-2611b517ffdb\",\n", + " \"c6c0c5dd-c0ca-4670-bbaa-c22d39e73570\",\n", + " \"5cb5d5b9-0c0b-497f-a4ea-a8cece52d13d\",\n", + " \"6f52de55-f5f2-45c0-b83f-59dbfb1fe221\",\n", + " \"42aa1fa1-56a9-4e96-8063-077bd7ba88d8\",\n", + " \"84784fef-5b76-45a0-91e0-28788e09fea6\",\n", + " \"04922ba6-8f00-4f54-b80c-ce2414c91e2e\",\n", + " \"f6a54329-486b-4d5f-b105-c639cec42351\"\n", + " ],\n", + " \"thesaurus_id\": 1\n", + "}\n" + ] + } + ], + "source": [ + "%%bash \n", + "metacatalog uuid --json 9d86cd70-062a-4c39-b3f3-226abebc07f7" + ] + } + ], + "metadata": { + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/_sources/dev/dev.rst.txt b/_sources/dev/dev.rst.txt new file mode 100644 index 00000000..f4550987 --- /dev/null +++ b/_sources/dev/dev.rst.txt @@ -0,0 +1,24 @@ +========== +Developers +========== + +.. toctree:: + :maxdepth: 1 + :hidden: + + tests + iso19115 + +Guide +===== + +You will find different guides and how-tos here that help +database administrators and developers to customize metacatalog to +fit their specific requirements. + +You will also find a section (soon) that will guide you through +contributing to metacatalog. + +We are working on opening some of metacatalog's functionality to make +it easier to overwrite some of metacatalogs default behaviors. So be +sure to check out this section from time to time. \ No newline at end of file diff --git a/_sources/dev/iso19115.rst.txt b/_sources/dev/iso19115.rst.txt new file mode 100644 index 00000000..641fdd13 --- /dev/null +++ b/_sources/dev/iso19115.rst.txt @@ -0,0 +1,1116 @@ +========= +ISO 19115 +========= + +Overview +======== + +Metacatalog makes it possible to store metadata in ISO 19115 standard. +Some of the required information for ISO 19115 will be the same for the +whole metacatalog instance, or, is specific to your installation and use +case. Below is a table of ISO 19115 code lists, which are implemented +and how they translate into metacatalog. + +ISO 19115 Fields +================ + +ISO 19115, 19115-1, and 19115-2 define only a few mandatory (M) fields, but a long +list of optional (O) and coditional (C) fields. In the table below you find a mapping +of ISO names to metacatalog names. If the column *Code List* is filled, metacatalog +makes use of published ISO 19115 CodeList values. In the section below you can find +for every list, how the single values map into metacatalog. + +MD_Metadata +----------- + +.. list-table:: + :widths: 15 15 20 10 40 + :header-rows: 1 + + * - ISO field + - metacatalog + - CodeList or Table + - Cardinality + - description + * - fileIdentifier + - Entry.uuid + - + - [1] + - + * - language + - ``'en-US'`` + - ISO 639-2 values are allowed + - [1] + - metacatalog is not multi-language and therefore a common value should be used + * - characterSet + - DataSource.encoding + - MD_CharacterSetCode + - [1] + - DataSource.encoding + * - parentIdentifier + - EntryGroup.id if Entry.is_partial + - + - [0] or [1] + - Only for partial Entries a Parent Entry is mandatory. + * - hierachyLevel + - ``'dataset'`` + - MD_ScopeCode + - [1] + - All other values are not applicable for metacatalog. + * - hierachyLevelName + - + - + - [0..*] + - Conditional: not applicable if ``hierachyLevel == 'dataset'`` + * - contact + - Entry.author | Entry.contributors + - CI_ResponsibleParty, CI_RoleCode + - [1..*] + - In metacatalog, the first author is contact person. + * - dateStamp + - Entry.lastUpdate + - ISO 19103 + - [1] + - either creation or last edit + * - metadataStandardName + - ``'ISO19115-2'`` + - + - [1] + - As of now ISO is the only standard used. More can be added here + * - metadataStandardVersion + - ``ISO19115-2:2019'`` + - + - [1] + - Not entirely sure about this + * - locale + - ``'en-US.utf8'`` + - ISO 19139 + - [0..*] + - Not sure about the format + * - metadataLinkageURL + - + - + - [0..*] + - The specific installation of metacatalog has to define this. + * - spatialRepresentationInfo + - + - MD_SpatialRepresentation + - [0..*] + - Recommended for INSPIRE topic 'elevation' + * - referenceSystemInfo + - + - MD_ReferenceSystem + - [1] + - In INSPIRE [1..*] cardinality. EPSG:4326 for metadata + * - metadataExtensionInfo + - + - MD_MetadataExtensionInformation + - [0..*] + - Details.value if Details.thesaurus_id not None + * - identificationInfo + - + - MD_Identification + - [1..*] + - Many possible. Only the first occarance is used for INSPIRE + * - contentInfo + - DataSourceType.name & Variable.name + - MD_ContentInformation + - [1..*] + - this does not map exactly, but is optional in ISO anyway + * - distributionInfo + - + - MD_Distribution + - [1] + - INSPIRE requires distribution information. Has to be implemented by metacatalog admin. + * - dataQualityInfo + - + - DQ_DataQuality + - [1..*] + - not yet implemended + * - portrayalCatalogueInfo + - + - MD_PortrayalCatalogueReference + - [0..*] + - This has to be defined outside metacatalog. + * - applicationSchemaInfo + - + - MD_ApplicationSchema + - [0] + - Not sure if metacatalog can implement this at all. + +MD_Indentification +------------------ + +.. list-table:: + :widths: 15 15 20 10 40 + :header-rows: 1 + + * - ISO field + - metacatalog + - CodeList or Table + - Cardinality + - description + * - citation + - Entry.citation + - CI_Citation + - [1] + - + * - abstract + - Entry.abstract + - + - [1] + - The abstract may contain a details table (`Entry.details_table(fmt='markdown')`) + * - purpose + - + - + - [0] + - The purpose goes into the abstract in metacatalog + * - status + - + - MD_ProgressCode + - [0..1] + - not yet implemented. + * - pointOfContact + - Entry.author + - CI_ResponsibleParty + - [1..*] + - In metacatalog, this is a double entry to contact. Filled by `Entry.author` + * - resourceMaintenance + - + - MD_MaintenaceInformation + - [0..*] + - as of now, no planned implementation + * - graphicOverview + - + - MD_BrowseGraphic + - [0..*] + - as of now, no planned implementation. + * - descriptiveKeywords + - Keyword + - MD_Keywords + - [1..*] + - I have no idea, what the forced Keyword is... + * - resourceSpecificUsage + - + - MD_Usage + - [0..*] + - as of now, no planned implementation + * - resourceConstraints + - License + - MD_Constrains + - [1..*] + - only a few values are allowed within metacatalog + * - aggregationInfo + - EntryGroup + - MD_AggregationInformation + - [0..*] + - only a few values are alled within metacatalog + +MD_DataIdentification +--------------------- + +.. list-table:: + :widths: 15 15 20 10 40 + :header-rows: 1 + + * - ISO field + - metacatalog + - CodeList or Table + - Cardinality + - description + * - spatialRepresentationType + - ``'raster'`` or ``'vector'`` + - MD_SpatialRepresentation_TypeCode + - [1..*] + - ``'raster'`` for raster data-types, ``'vector'`` else. + * - spatialResolution + - DataSource.spatial_scale + - MD_Resolution + - [0..*] + - if applicable (DataSource.spatial_scale can be None) + * - language + - ``'en-US'`` + - ISO 639-2 + - [1] + - metacatalog is as of now not multi-language + * - characterSet + - ``'utf8'`` + - MD_CharacterSetCode + - [1] + - it is recommended to only use UTF-8 encodings + * - topicCategory + - Keyword.value + - MD_TopicCategoryCode + - [1..*] + - This may be mappable from Keywords + * - environmentDescription + - Entry.abstract + - + - [0..1] + - If important, should go into the abstract + * - extent + - SpatialScale.extent TemporalScale.extent + - Ex_Extent + - [1..*] + - not sure if temporal scale is supported by ISO + * - supplementalInformation + - DataSource.args + - + - [0..1] + - This may not be helpful on export + +MD_BrowseGraphic +---------------- + +.. note:: + + The ``MD_Identification.graphicOverview`` is as of now not implemented in metacatalog. + Currently, no implementation is planned. + + +MD_Keywords +----------- + +.. list-table:: + :widths: 15 15 20 10 40 + :header-rows: 1 + + * - ISO field + - metacatalog + - CodeList or Table + - Cardinality + - description + * - keyword + - Keyword.full_path + - + - [1..*] + - Keyword hierachies will always be separated by ``' > '`` + * - type + - ``'theme'`` + - MD_KeywordTypeCode + - [0..1] + - in metacatalog keywords are: ``'topic > term > ...'`` + * - thesaurusName + - ``Keyword.thesaurusName`` + - CI_Citation + - [0..1] + - ``Keyword.thesaurusName`` is a read only + +.. note:: + To reference a thesaurus, the thesaurus name is needed. + Then, the url of the given Keyword is given as + `thesaurusUrl`. The system, which operates metacatalog + has to be able to create a valid ``CI_Citation``. + The `thesaurusName.citedResponsibleParty` is represented + by an `organisationName` given by `thesaurus_organisation` + and `contactInfo`, which is an `OnlineResource` of `linkage` + given as `thesaurus_url`. + +MD_RepresentativeFraction +------------------------- + +Has only one field: ``denominator``, which is the ISO 19103 scale. Applies only to +raster sources in metacatalog. See MD_Resolution. + +MD_Resolution +------------- + +Has only one of two fields. The resolution applies only to raster sources in metacatalog +and is either a MD_RepresentativeFraction (scale) or a ground distance stored in the +field ``distance``. If :class:`DataSource ` has a +spatial scale, the :class:`SpatialScale.resolution ` +can be used to give the ground distance. + +MD_Usage +-------- + +.. note:: + + Metacatalog does not store the usage information in extra fields, but they can be + extracted from existing ISO fields, that are extended in metacatalog + +.. list-table:: + :widths: 15 15 20 10 40 + :header-rows: 1 + + * - ISO field + - metacatalog + - CodeList or Table + - Cardinality + - description + * - specificUsage + - Entry.abstract + - + - [1] + - This information is added to the abstract in metacatalog + * - userContactInfo + - Entry.author + - + - [1] + - metacatalog defines the first author as a universal contact person + + +MD_AggregateInformation +----------------------- + +.. list-table:: + :widths: 15 15 20 10 40 + :header-rows: 1 + + * - ISO field + - metacatalog + - CodeList or Table + - Cardinality + - description + * - aggregateDataSetIdentifier + - Entry.associated_groups.entries.uuid + - MD_Identifier + - [1] + - can either implement the UUID or full MD_Identifier + * - associationType + - EntryGroupType.name + - DS_AssociationTypeCode + - [1] + - not all types are mapped into metacatalog + +MD_Constraints +-------------- + +.. list-table:: + :widths: 15 15 20 10 40 + :header-rows: 1 + + * - ISO field + - metacatalog + - CodeList or Table + - Cardinality + - description + * - useLimitation + - License.full_text + - + - [1] + - This is redunant to meet INSPIRE + +.. note:: + + There is an onging debate about this field between INSPIRE and GDI-DE. At the moment the + useLimitations are meant to describe use-cases where the data is not applicable. But it + is a mandatory field and it is not possible to leave it blank. DGI-DE is duplication the + useConstraints from MD_LegalConstraints into this field to satisfy ISO 19115 and INSPIRE. + +MD_LegalConstraints +------------------- + +.. list-table:: + :widths: 15 15 20 10 40 + :header-rows: 1 + + * - ISO field + - metacatalog + - CodeList or Table + - Cardinality + - description + * - accessConstraints + - + - MD_RestrictionsCode + - [C..0] + - not mapped in metacatalog + * - useConstraints + - ``'otherRestrictions'`` + - MD_RestrictionsCode + - [1] + - note the warning below! + * - otherConstraints + - License.full_text + - + - [1] + - note the warning below! + +.. note:: + + ISO 19115 makes the fields ``accessConstraints``, ``useConstraints`` and ``otherConstraints`` + dependent on each other. ``otherConstraints`` is mandatory and either ``accessConstraints`` or + ``useConstraints`` need at least a reference ``'otherRestrictions'`` as a value to reference + the field. All of them together are needed to set the legal framework of working with data. + GDI-DE is trying to unify this semantic und suggests to duplicate open data licenses into + all of these fields. They also have a suggestion how to store open data licenses (which + only applies to the german geodata infrastructure and is therefore not a part of metacatalog.) + +.. warning:: + + If you implement metacatalog in your application, you have to make sure, that the license + information is mapped into ISO 19115 accordingly. Other restrictions and use limitation do + not apply as metacatalog is made for open data. If you wish to store private or restricted + information, you will need a security, authorization and authentification middleware as + metacatalog does not handle these issues. + + +MD_SecurityConstraints +---------------------- + +.. list-table:: + :widths: 15 15 20 10 40 + :header-rows: 1 + + * - ISO field + - metacatalog + - CodeList or Table + - Cardinality + - description + * - classification + - ``'unclassified'`` + - MD_ClassificationCode + - [1] + - note the warning below + +.. warning:: + + Please also see MD_LegalConstraints. The metadata in metacatalog is always + ``'unclassified'``. If you wish to implement classified information, you need a + security middleware. However, ISO 19115 and INSPIRE define this field as + mandatory and you have to include it. + +DQ_DataQuality +-------------- + +.. list-table:: + :widths: 15 15 20 10 40 + :header-rows: 1 + + * - ISO field + - metacatalog + - CodeList or Table + - Cardinality + - description + * - scope + - ``'dataset'``, ``'series'`` + - DQ_Scope + - [1] + - see note below + * - report + - + - DQ_Element + - [1..*] + - not yet implemented + * - lineage + - + - LI_Lineage + - [1..*] + - not yet implemented + + +.. note:: + + The scope is always ``'dataset'`` for ``Entry`` and ``'series'`` for ``EntryGroup``. + The DQ_Scope can then be filled automatically, as both entries do not need user-information + to fill the other DQ_Scope fields and are therefore not implemented into metacatalog. + +.. note:: + + All related data-quality tables are not mapped into metacatalog as the implementation + is still in discussion. + +MD_MaintenaceInformation +------------------------ + +.. note:: + + Currently, there are no plans to implement MD_MaintenaceInformation + +MD_SpatialRepresentation +------------------------ + +.. note:: + + This only applies to ``'raster'`` and ``'vector'`` data types, which can be derived from + a data source type. + Any further implementations are not planned. + +MD_ReferenceSystem +------------------ + +.. note:: + + Metacatalog stores all geographic information in EPS:4326, WGS84 and you can therefore + handle the reference system. If the data uses different reference systems, the ``DataSource`` + will be able to handle this information with the next revision. + +.. warning:: + + Due to technical reasons, the DataSource can not yet handle CRS information. Please store + only EPSG:4326 referenced data in metacatalog. This will be resolved with one of + the future releases. + +MD_ContentInformation +--------------------- + +.. note:: + + The MD_ContentInformation and all related entities are not yet implemented in metacatalog. + As metacatalog only uses a very limited amount of the defined values, ``DataSource`` and + ``Variable`` will be mappable to MD_ContentInformation in the future. + + +MD_PortrayalCatalogueReference +------------------------------ + +.. note:: + + There are currently no plans to implement portayal information into metacatalog. + But these records would have a ``m:1`` relationship to ``Entry`` and can be + implemented outside metacatalog in a data-delivery middleware. + + +MD_Distribution +--------------- +.. note:: + + By the use of I/O extensions, almost any format and way + of distributing data can be implemented into metacatalog. + It is recommended to append distribution information on export + filling the fields accordingly. Some of the fields can be + determined by following metacatalog's data types. + + The distributor will always be the authority running the + metacatalog installation (not the data owner!) + + +.. list-table:: + :widths: 15 15 20 10 40 + :header-rows: 1 + + * - ISO field + - metacatalog + - CodeList or Table + - Cardinality + - description + * - testData + - + - + - [0] + - this is not mapped in metacatalog + * - distributionFormat + - ``'.txt'`` or ``.csv'`` + - MD_Format + - [1] + - See note above. + * - distributor + - + - MD_Distributor + - [1] + - metacatalog admin + * - transferOptions + - + - MD_DigitalTransferOptions + - [0..*] + - Depending on the distribution system + +.. note:: + + If the ``DataSourceTypes`` should be used for distribution, an CI_OnlineResource + with the ``DataSource.path`` as ``linkage`` can be automatically derived. + +.. note:: + + All other entities related to distribution are not part of metacatalog and have to be + added, specifying the ways how the data can be requested and who is responsible. + The I/O Extentions might store some useful information. + +MD_MetadataExtensionInformation +------------------------------- + +.. list-table:: + :widths: 15 15 20 10 40 + :header-rows: 1 + + * - ISO field + - metacatalog + - CodeList or Table + - Cardinality + - description + * - extendedRoleInformation + - nm_entries_details + - MD_ExtendedElementInformation + - [1..*] + - + +MD_ExtendedElementInformation +----------------------------- + +.. note:: + + MD_ExtendedElementInformation can be realized by ``Details`` that relate a + ``Thesaurus`` with public URI. + +.. list-table:: + :widths: 15 15 20 10 40 + :header-rows: 1 + + * - ISO field + - metacatalog + - CodeList or Table + - Cardinality + - description + * - name + - Details.key + - + - [1] + - + * - shortName + - Details.stem + - + - [0..1] + - can be omitted if ``'codeListElement'`` + * - domainCode + - + - + - [1] + - 3-digit integer code. No idea for what. + * - definition + - Detail.description + - + - [1] + - + * - obligation + - + - MD_ObligationCode + - [0] + - + * - condition + - + - + - [0..1] + - Can be omitted because ``obligation`` cannot be ``'conditional'``. + * - dataType + - ``characterString`` + - MD_DataTypeCode + - [1] + - ``models.Detail.value`` is always string + * - maximumOccurence + - ``1`` + - + - [1] + - A key may not be duplicated on the same ``Entry`` + * - domainValue + - ``'any'`` + - + - [1] + - ``key=value`` are arbitrary. + * - parentEntity + - ``MD_Metadata`` + - + - [1..*] + - in metacatalog ``Detail`` is bound to ``Entry`` + * - rule + - ``'descriptive Value'`` + - + - [1] + - ``Detail`` is always specifying ``Entry``. You can set other text. + * - rationale + - Detail.description + - + - [0..1] + - the description may contain a rationale + * - source + - Entry.author + - + - [1] + - metacatalog specifies the author to be the source + + +MD_ApplicationSchema +-------------------- + +.. note:: + + There are no plans to implement MD_ApplicationSchema. + + +Ex_Extent +--------- + +.. list-table:: + :widths: 15 15 20 10 40 + :header-rows: 1 + + * - ISO field + - metacatalog + - CodeList or Table + - Cardinality + - description + * - description + - + - + - [0..1] + - Only one field of Ex_Extent can to be filled + * - geographicElement + - + - EX_GeographicExtent + - [0..1] + - Only one field of Ex_Extent can to be filled + * - temporalElement + - + - EX_TemporalExtent + - [0..1] + - Only one field of Ex_Extent can to be filled + * - verticalElement + - + - EX_VerticalElement + - [0] + - verticalElements are not mapped in metacatalog + +EX_GeographicExtent +------------------- + +The geographic extent is always given as bounding box in +EPSG:4326, if applicable. Other values and objects defined in +IOS 19115 are not supported by metacatalog. + + +EX_TemporalExtent +----------------- + +ISO 19115 only requires a ``EX_TemporalExtent.extent`` value, which has to be +a ISO 19108 time range. The :class:`TemporalScale ` +has a ``start`` and a ``end`` property and any instance of it can return an +ISO 19108 time range. + +EX_VerticalExtent +------------------ + +.. note:: + + EX_VerticalExtent cannot be mapped in metacatalog + +CI_Citation +----------- + +.. list-table:: + :widths: 15 15 20 10 40 + :header-rows: 1 + + * - ISO field + - metacatalog + - CodeList or Table + - Cardinality + - description + * - title + - Entry.title + - + - [1] + - + * - alternateTitle + - + - + - [0..*] + - not available in metacatalog + * - date + - Entry.publication + - + - [1] + - The CI_Date is always publication in metacatalog + * - edition + - Entry.version + - + - [0..1] + - This might change in the future + * - editionDate + - Entry.publication + - ISO 19103 + - [0..1] + - mandatory if edition is set. It is the publication of the new ``Entry.version`` + * - identifier + - + - + - [0] + - this does not apply to metacatalog + * - citedResponsibleParty + - + - CI_ResponsibleParty + - [0..1] + - Not implemented, but could be filled by the metacatlog admin as CI_ResponsibleParty. + * - presentationForm + - + - CI_PresentationFormCode + - [0] + - Not implemented in metacatalog. + * - series + - EntryGroup.uuid + - CI_Series + - [0..1] + - only applicable for ``EntryGroup`` + * - otherCitationDetails + - + - + - [0] + - not available in metacatalog + * - collectiveTitle + - EntryGroup.title + - + - [0..1] + - only applicable for ``EntryGroup`` + * - ISBN + - + - + - [0] + - not available + * - ISSN + - + - + - [0] + - not available + +CI_ResponsibleParty +------------------- + +.. note:: + + In metacatalog only two cases of using CI_ResponsibleParty are covered. Either it + is the first author of the dataset and can then be filled by ``Entry.author``, or it + is the authority running metacatalog and CI_ResponsibleParty can automatically be + filled on export. + + +Code-Lists +========== + +Metacatalog mappings are based on the CodeList dictionaries published by NOAA. +The following list gives you an idea, where and how the codes lists and the values +are implemented. + +https://www.ngdc.noaa.gov/wiki/index.php/ISO_19115_and_19115-2_CodeList_Dictionaries#CI_DateTypeCode + + +CI_DateTypeCode +--------------- + +.. list-table:: + :widths: 25 25 50 + :header-rows: 1 + + * - ISO + - metacatalog + - description + * - creation + - Entry.creation + - start-date of the *data* + * - publication + - Entry.publication + - creation date of the Entry record + * - revision + - / + - we use ISO 19115-2 lastUpdate + * - adopted + - n.a. + - not applicable + * - deprecated + - / + - not yet implemented + * - distribution + - n.a. + - not applicable as metacatalog is a distribution system. Will be the same as ``publication`` here. + * - expiry + - n.a. + - not applicable + * - inForce + - n.a. + - not applicable + * - lastRevision + - / + - not yet implemented + * - lastUpdate + - Entry.lastUpdate + - updates on every edit + * - nextUpdate + - n.a. + - not applicable + * - release + - n.a. + - metacatalog is intended for open data + * - superseded + - / + - not yet implemented + * - unavailable + - n.a. + - not applicable + * - validityBegins + - n.a. + - not applicable + * - validityExpires + - n.a. + - not applicable + +CI_PresentationFormCode +----------------------- + +The definitions given in this list do not apply to environmental datasets. +Depending on the metacatalog instance and the metadata stored, the +CI_PresentationFormCode will apply to all data. If applicable it will be one of + +* mapDigital +* modelDigital +* tableDigital +* physicalSample + + +.. note:: + + You will have to implement this _after_ metacatalog has exported the + :class:`Entry ` information, if needed. + +CI_RoleCode +----------- +.. note:: + + The full `CI_RoleCode Codelist `_ + is implemented exactly into `metacatalog.PersonRole`. + +.. csv-table:: Roles + :file: ../../../metacatalog/data/person_roles.csv + :widths: 20, 20, 60 + :header-rows: 1 + +DQ_EvaluationMethodTypeCode +--------------------------- + +The `DQ_EvaluationMethodTypeCode `_ +list is not yet implemented. + +DS_AssociationTypeCode +---------------------- + +The :class:`EntryGroup ` maps some of the +DS_AssociationTypeCode. + +.. list-table:: + :widths: 25 25 50 + :header-rows: 1 + + * - ISO + - metacatalog + - description + * - crossReference + - / + - not implemented yet + * - largerWorkCitation + - :class:`EntryGroupType.name=='Composite' ` + - 'Citation' might be misleading here. + * - partOfSeamlessDatabase + - n.a. + - not sure if this applies to metacatalog + * - source + - n.a. + - not applicable, as metacatalog does not store dependencies if the data is an image + * - stereoMate + - n.a. + - not applicable + * - collectiveTitle + - `EntryGroupType.name=='Project'` + - applies if the Entries are grouped by Project name + * - dependency + - :class:`Entry ` if :class:`Entry.is_partial==True ` + - :class:`Entry.uuid ` of all :class:`Entry.is_partial==False ` for a partial Entry within the same composite + * - isComposedOf + - :class:`Entry.uuid ` + - :class:`Entry.uuid ` of all child Entries for a EntryGroup + * - revisionOf + - / + - not yet implemented + * - series + - n.a. + - not applicable. + +DS_InitiativeTypeCode +--------------------- + +The `InitiativeTypeCode List `_ +does not apply to metacatalog. In cases you use a data platform around metacatalog, +which can either return aggregated datasets or processing results or datasets +that share a context, you have to implement this list to describe the type of +dataset aggregation. + +MD_CellGeometryCode +------------------- + +The `MD_CellGeometryCode List `_ +is extended in metacatalog by the :class:`Entry.location `_ + is describing **grid cells**, therefore this section only applies to + raster datasources and is not yet implemented. + +.. list-table:: + :widths: 25 25 50 + :header-rows: 1 + + * - ISO + - metacatalog + - description + * - point + - :class:`Entry.location `_ +describes classified information. As metacatalog is designed for and dedicated to +managing open data this list does not apply. + +However a :class:`Entry ` can be put into embargo for a +limited amount of time. This defaults to two years after ``'publication'`` date. +An Entry under embargo is still ``'unclassified'`` following +`MD_ClassificationCode List `_ +but just not visible in the system. + +MD_CoverageContentTypeCode +-------------------------- + +The `MD_CoverageContentTypeCode List `_ +is not yet implemented. + +MD_DatatypeCode +--------------- + +The `MD_DatatypeCode List `_ +is not implemented yet, but will be available as a lookup value for data types. + +MD_DimensionNameTypeCode +------------------------ + +The `MD_DimensionNameTypeCode List `_ +does not apply to metacatalog, as the data can be more generalized than geometric dimensions. + +MD_GeometricObjectTypeCode +-------------------------- + +The value is always ``'point'`` for :class:`Entry.location ` + + +MD_ImagingConditionCode +----------------------- + +The `MD_ImagingConditionCode List `_ +is not yet implemented, but will be available optinally, to be linked to +:class:`Detail ` information. + +MD_KeywordTypeCode +------------------ + +The `MD_KeywordTypeCode `_ +is not yet implemented. Some of the keyword types can be used to specify the controlled +keywords implemented as :class:`Keyword ` and some might +further specify :class:`Details `. +It will be decided with Version 0.2 of metacatalog how much of this information +will be reflected within metacatalog. + diff --git a/_sources/dev/tests.rst.txt b/_sources/dev/tests.rst.txt new file mode 100644 index 00000000..8ed71c3b --- /dev/null +++ b/_sources/dev/tests.rst.txt @@ -0,0 +1,80 @@ +========= +E2E Tests +========= + +Testing in metacatalog +====================== + +metacatalog includes an icreasing suite of tests to assure functional correctness. +As typical unittests would need some serious amount of database mocking and be +of one magnitude larger than the database management code, it was decided to +include end-to-end tests, as they are common in javascript frameworks. + +The tests are implemented using pytest and can be run like that. They run +common tasks like importing, editing and searching data and compare the +results of queries against expected results. + +On this path, some minor functions might not be covered by a unittest, but +in a larger context, the tests assure that the whole application does what +it should do. + +.. note:: + + metacatalog is a one-man project. If you spot a function that is not + covered and needs coverage, contact me via Github but be patient please. + +Run +=== + +Running tests locally +--------------------- + +The tests are designed to run on Github actions, by actually installing a +PostgreSQL database and actually uploading data into the DB. You can run +the tests locally, as well. You need to install: + +.. code-block:: python + + pip install pytest pytest-cov pytest-depends + +as these testing packages are not in the metacatalog requirements. +By simply running: + +.. code-block:: bash + + pytest + +metacatalog will install a new database instance called ``'test_[a-z]8`` +with user ``postgres`` and password``postgres`` at port ``5432``. +You can overwrite these settings by the environment variables: + +* ``POSTGRES_USER`` +* ``POSTGRES_PASSWORD`` +* ``POSTGRES_PORT`` + +.. note:: + + After each test run, a ``DBNAME`` file will be added to the + ``metacatalog/test`` folder, to track the current database + between the tests. After the tests have finished, you should + remove this file. This might be done by an optional cleanup + tests in the future. + +Clean up +-------- + +If you run a lot of local tests, or if your copy of metacatalog runs +tests after upgrading (which makes a lot of sense), you will find +yourself left with maybe hundereds of databases, as the test suite does +not drop the database. + +Run the following chunk to delete all databases that follow the +naming convention of metacatalog tests: + +.. code-block:: bash + + sudo -u postgres psql -d postgres < <( sudo -u postgres psql -Atc "select 'drop database \"' || datname || '\";' from pg_database where datname like 'test_%';") + +.. warning:: + + If you have other databases that start with ``test_``, **they will be deleted as well.** \ No newline at end of file diff --git a/_sources/ext/custom.rst.txt b/_sources/ext/custom.rst.txt new file mode 100644 index 00000000..9e8c2fa3 --- /dev/null +++ b/_sources/ext/custom.rst.txt @@ -0,0 +1,6 @@ +================= +Custom Extensions +================= + +.. automodule:: metacatalog.ext.base + :members: MetacatalogExtensionInterface \ No newline at end of file diff --git a/_sources/ext/export.rst.txt b/_sources/ext/export.rst.txt new file mode 100644 index 00000000..f070265a --- /dev/null +++ b/_sources/ext/export.rst.txt @@ -0,0 +1,6 @@ +================= +Export Extenstion +================= + +.. automodule:: metacatalog.ext.export + :members: ExportExtension diff --git a/_sources/ext/ext.rst.txt b/_sources/ext/ext.rst.txt new file mode 100644 index 00000000..5c4113ca --- /dev/null +++ b/_sources/ext/ext.rst.txt @@ -0,0 +1,19 @@ +========== +Extensions +========== + +.. toctree:: + :maxdepth: 1 + :hidden: + + io + export + standards_export + custom + + +Overview +======== + +.. automodule:: metacatalog.ext + :members: extension, activate_extension, deactivate_extension diff --git a/_sources/ext/io.rst.txt b/_sources/ext/io.rst.txt new file mode 100644 index 00000000..923630eb --- /dev/null +++ b/_sources/ext/io.rst.txt @@ -0,0 +1,8 @@ +====================== +Read / Write Extension +====================== + + + +.. automodule:: metacatalog.ext.io + :members: IOExtension diff --git a/_sources/ext/standards_export.rst.txt b/_sources/ext/standards_export.rst.txt new file mode 100644 index 00000000..21876785 --- /dev/null +++ b/_sources/ext/standards_export.rst.txt @@ -0,0 +1,8 @@ +====================== +Standards Export Extension +====================== + + + +.. automodule:: metacatalog.ext.standards_export + :members: StandardsExportExtension diff --git a/_sources/home/getting_started.rst.txt b/_sources/home/getting_started.rst.txt new file mode 100644 index 00000000..3bfef297 --- /dev/null +++ b/_sources/home/getting_started.rst.txt @@ -0,0 +1,7 @@ +=============== +Getting Started +=============== + +.. note:: + + Will will follow soon \ No newline at end of file diff --git a/_sources/home/home.rst.txt b/_sources/home/home.rst.txt new file mode 100644 index 00000000..bdd33f87 --- /dev/null +++ b/_sources/home/home.rst.txt @@ -0,0 +1,42 @@ +==== +Home +==== + +.. toctree:: + :maxdepth: 1 + :hidden: + + install + scheme + getting_started + +How the docs work +================= + +Metacatalog is a management tool for a PostgreSQL/PostGIS database that is +primarily used to store meta-data of environmental open data. While it can +also store the data itself, it is more meant as a Meta-database that should +rather interface the original data stores. + +Metacatalog has three main submodules: + +* the `Database Models <../models/models.rst>`_ +* a Python `API <../api/api.rst>`_ +* a `CLI <../cli/cli.rst>`_ + +Please refer to each section to learn more about each submodules. +In general terms, the Models give you great freedom in adding, editing and +changing metadata and use `sqlalchemy `_ to search the +database. But the models are only the Python classes that model the metadata. +You will have to implement all steps to manage the data and check integrity yourself. + +The command line interface offers some robust functionality to automate some +common tasks and quickly add some entries. But the CLI gives you less freedom and +some of the API and Model functionality is not implemented in the CLI. + +The Python API gives you the best balance between abstraction and usability. Usually, +you will import the api and do all necessary work on the database from this entrypoint. + +.. code-block:: python + + from metacatalog import api diff --git a/_sources/home/install.rst.txt b/_sources/home/install.rst.txt new file mode 100644 index 00000000..86cce013 --- /dev/null +++ b/_sources/home/install.rst.txt @@ -0,0 +1,79 @@ +============ +Installation +============ + +Prerequisites +------------- + +First you need to install PostgreSQL and the PostGIS extension. +You can find the PostgreSQL installer for Windows on the official PostgreSQL website. +Make sure that the stack installer is installed during the PostgreSQL installation process to install the PostGIS extension as well. + +On Linux the commands might look similar to: + +.. code-block:: bash + + sudo apt install postgresql postgis + + +PostGIS will in many cases be a rather outdated version. This is up to now not a big issue, as +metacatalog uses only a limited amount of spatial functions. Anything > v2.0 should be fine. + +Next, you need to install the database and create the PostGIS extension. In this example, the chosen database name is 'metacatalog'. +You can create the database and the extension in the GUI application pgAdmin, which is installed together with PostgreSQL or +you can open a SQL console to postgresql or use psql: + +.. code-block:: sql + + create database metacatalog with encoding='UTF8'; + create extension postgis; + +Install metacatalog +------------------- + +You can install metacatalog from PyPI + +.. code-block:: bash + + pip install metacatalog + + +Create Tables +------------- + +.. note:: + + Refer to the CLI command `create <../cli/cli_create.ipynb>`_, `populate <../cli/cli_populate.ipynb>`_ and + `init <../cli/cli_init.ipynb>`_ for more detailed information. + +After the database has been installed, you can use the `metacatalog CLI <../cli/cli.rst>`_ +to create the necessary tables. +Follow the syntax of the init command and replace *driver*, *user*, *password*, *host* and *database* with your parameters + +.. note:: + + All parameters can be accessed in pgAdmin or have been previously set by the user. + +.. code-block:: bash + + metacatalog init --connection driver://user:password@host:port/database + +When using Windows this command can lead to errors and must be changed in this case (refer to `metacatalog CLI <../cli/cli.rst>`_): + +.. code-block:: bash + + python -m metacatalog init --connection driver://user:password@host:port/database + + + + +The (standard) connection command could look like this: + +.. code-block:: bash + + metacatalog init --connection postgresql://postgres:\ *yourpassword*\ @localhost:5432/metacatalog + + +.. note:: + + If you get a **FileNotFoundError** when first running the init command, try to (re)install the shapely package with **conda install shapely**. diff --git a/_sources/home/scheme.rst.txt b/_sources/home/scheme.rst.txt new file mode 100644 index 00000000..1698a77c --- /dev/null +++ b/_sources/home/scheme.rst.txt @@ -0,0 +1,12 @@ +================= +Metadata Overview +================= + +Available metdata entities +-------------------------- + +The following table sumamrizes all Metadata information that can be stored in metacatalog. + +.. csv-table:: Metadata descriptions + :widths: 20 60 10 10 + :file: descriptions.csv \ No newline at end of file diff --git a/_sources/index.rst.txt b/_sources/index.rst.txt new file mode 100644 index 00000000..b7cb0781 --- /dev/null +++ b/_sources/index.rst.txt @@ -0,0 +1,18 @@ +.. Metacatalog documentation master file, created by + sphinx-quickstart on Fri May 8 08:38:09 2020. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to Metacatalog's documentation! +======================================= + +.. toctree:: + :maxdepth: 3 + :caption: Contents: + + home/home + cli/cli + api/api + models/models + ext/ext + dev/dev diff --git a/_sources/models/data.rst.txt b/_sources/models/data.rst.txt new file mode 100644 index 00000000..ff1ccf48 --- /dev/null +++ b/_sources/models/data.rst.txt @@ -0,0 +1,17 @@ +=========== +Data Tables +=========== + +While metacatalog is quite flexible in handling metadata of sources that are +actually stored remotely, or file based, it has some internal tables defined. +You can also add additionaly specialized data tables in your implementation +of metacatalog. For this, refer to the classes provided below. + +.. automodule:: metacatalog.models.timeseries + :members: + +.. automodule:: metacatalog.models.geometry_data + :members: + +.. automodule:: metacatalog.models.generic_data + :members: diff --git a/_sources/models/datasource.rst.txt b/_sources/models/datasource.rst.txt new file mode 100644 index 00000000..0c82cb19 --- /dev/null +++ b/_sources/models/datasource.rst.txt @@ -0,0 +1,6 @@ +========== +DataSource +========== + +.. automodule:: metacatalog.models.datasource + :members: \ No newline at end of file diff --git a/_sources/models/entry.rst.txt b/_sources/models/entry.rst.txt new file mode 100644 index 00000000..1890c1db --- /dev/null +++ b/_sources/models/entry.rst.txt @@ -0,0 +1,9 @@ +===== +Entry +===== + +.. automodule:: metacatalog.models.entry + :members: + +.. automodule:: metacatalog.models.entrygroup + :members: \ No newline at end of file diff --git a/_sources/models/keyword.rst.txt b/_sources/models/keyword.rst.txt new file mode 100644 index 00000000..375a124f --- /dev/null +++ b/_sources/models/keyword.rst.txt @@ -0,0 +1,7 @@ +=================== +Controlled Keywords +=================== + +.. automodule:: metacatalog.models.keyword + :members: + \ No newline at end of file diff --git a/_sources/models/license.rst.txt b/_sources/models/license.rst.txt new file mode 100644 index 00000000..6fbfdeae --- /dev/null +++ b/_sources/models/license.rst.txt @@ -0,0 +1,7 @@ +============= +Data Licenses +============= + +.. automodule:: metacatalog.models.license + :members: + diff --git a/_sources/models/location.rst.txt b/_sources/models/location.rst.txt new file mode 100644 index 00000000..e1fea36a --- /dev/null +++ b/_sources/models/location.rst.txt @@ -0,0 +1,12 @@ +=============== +Location Filter +=============== + +The location filter module is a utility module, that is usually accessed via +The `Entry.neighbors `_ function or the +`find_entry `_ API. +However, the uitl module is importable and the functions can be used directly +for more advanced use cases. + +.. automodule:: metacatalog.util.location + :members: \ No newline at end of file diff --git a/_sources/models/models.rst.txt b/_sources/models/models.rst.txt new file mode 100644 index 00000000..120a6c6e --- /dev/null +++ b/_sources/models/models.rst.txt @@ -0,0 +1,21 @@ +=============== +Database Models +=============== + + +.. automodule:: metacatalog.models + + +.. toctree:: + :maxdepth: 1 + :Caption: Models: + + entry + result + location + keyword + person + variable + datasource + data + license \ No newline at end of file diff --git a/_sources/models/person.rst.txt b/_sources/models/person.rst.txt new file mode 100644 index 00000000..3b79ada3 --- /dev/null +++ b/_sources/models/person.rst.txt @@ -0,0 +1,6 @@ +====================== +Authors & Contributors +====================== + +.. automodule:: metacatalog.models.person + :members: \ No newline at end of file diff --git a/_sources/models/result.rst.txt b/_sources/models/result.rst.txt new file mode 100644 index 00000000..2891ad06 --- /dev/null +++ b/_sources/models/result.rst.txt @@ -0,0 +1,6 @@ +========= +ResultSet +========= + +.. automodule:: metacatalog.util.results + :members: ImmutableResultSet diff --git a/_sources/models/variable.rst.txt b/_sources/models/variable.rst.txt new file mode 100644 index 00000000..484e82eb --- /dev/null +++ b/_sources/models/variable.rst.txt @@ -0,0 +1,6 @@ +========= +Variables +========= + +.. automodule:: metacatalog.models.variable + :members: \ No newline at end of file diff --git a/_static/_sphinx_javascript_frameworks_compat.js b/_static/_sphinx_javascript_frameworks_compat.js new file mode 100644 index 00000000..8549469d --- /dev/null +++ b/_static/_sphinx_javascript_frameworks_compat.js @@ -0,0 +1,134 @@ +/* + * _sphinx_javascript_frameworks_compat.js + * ~~~~~~~~~~ + * + * Compatability shim for jQuery and underscores.js. + * + * WILL BE REMOVED IN Sphinx 6.0 + * xref RemovedInSphinx60Warning + * + */ + +/** + * select a different prefix for underscore + */ +$u = _.noConflict(); + + +/** + * small helper function to urldecode strings + * + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL + */ +jQuery.urldecode = function(x) { + if (!x) { + return x + } + return decodeURIComponent(x.replace(/\+/g, ' ')); +}; + +/** + * small helper function to urlencode strings + */ +jQuery.urlencode = encodeURIComponent; + +/** + * This function returns the parsed url parameters of the + * current request. Multiple values per key are supported, + * it will always return arrays of strings for the value parts. + */ +jQuery.getQueryParameters = function(s) { + if (typeof s === 'undefined') + s = document.location.search; + var parts = s.substr(s.indexOf('?') + 1).split('&'); + var result = {}; + for (var i = 0; i < parts.length; i++) { + var tmp = parts[i].split('=', 2); + var key = jQuery.urldecode(tmp[0]); + var value = jQuery.urldecode(tmp[1]); + if (key in result) + result[key].push(value); + else + result[key] = [value]; + } + return result; +}; + +/** + * highlight a given string on a jquery object by wrapping it in + * span elements with the given class name. + */ +jQuery.fn.highlightText = function(text, className) { + function highlight(node, addItems) { + if (node.nodeType === 3) { + var val = node.nodeValue; + var pos = val.toLowerCase().indexOf(text); + if (pos >= 0 && + !jQuery(node.parentNode).hasClass(className) && + !jQuery(node.parentNode).hasClass("nohighlight")) { + var span; + var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.className = className; + } + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + node.parentNode.insertBefore(span, node.parentNode.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling)); + node.nodeValue = val.substr(0, pos); + if (isInSVG) { + var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); + var bbox = node.parentElement.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute('class', className); + addItems.push({ + "parent": node.parentNode, + "target": rect}); + } + } + } + else if (!jQuery(node).is("button, select, textarea")) { + jQuery.each(node.childNodes, function() { + highlight(this, addItems); + }); + } + } + var addItems = []; + var result = this.each(function() { + highlight(this, addItems); + }); + for (var i = 0; i < addItems.length; ++i) { + jQuery(addItems[i].parent).before(addItems[i].target); + } + return result; +}; + +/* + * backward compatibility for jQuery.browser + * This will be supported until firefox bug is fixed. + */ +if (!jQuery.browser) { + jQuery.uaMatch = function(ua) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || + /(webkit)[ \/]([\w.]+)/.exec(ua) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || + /(msie) ([\w.]+)/.exec(ua) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; + }; + jQuery.browser = {}; + jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; +} diff --git a/_static/basic.css b/_static/basic.css new file mode 100644 index 00000000..18495ea0 --- /dev/null +++ b/_static/basic.css @@ -0,0 +1,900 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 270px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 360px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} +nav.contents, +aside.topic, +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ +nav.contents, +aside.topic, +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +nav.contents > :last-child, +aside.topic > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +nav.contents::after, +aside.topic::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} +aside.footnote > span, +div.citation > span { + float: left; +} +aside.footnote > span:last-of-type, +div.citation > span:last-of-type { + padding-right: 0.5em; +} +aside.footnote > p { + margin-left: 2em; +} +div.citation > p { + margin-left: 4em; +} +aside.footnote > p:last-of-type, +div.citation > p:last-of-type { + margin-bottom: 0em; +} +aside.footnote > p:last-of-type:after, +div.citation > p:last-of-type:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/_static/binder_badge_logo.svg b/_static/binder_badge_logo.svg new file mode 100644 index 00000000..327f6b63 --- /dev/null +++ b/_static/binder_badge_logo.svg @@ -0,0 +1 @@ + launchlaunchbinderbinder \ No newline at end of file diff --git a/_static/brand.png b/_static/brand.png new file mode 100644 index 0000000000000000000000000000000000000000..bb6dbe05cc05c603d5c821c38d2839b0037f9de0 GIT binary patch literal 19085 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 { + if (document.readyState !== "loading") { + callback(); + } else { + document.addEventListener("DOMContentLoaded", callback); + } +}; + +/** + * Small JavaScript module for the documentation. + */ +const Documentation = { + init: () => { + Documentation.initDomainIndexTable(); + Documentation.initOnKeyListeners(); + }, + + /** + * i18n support + */ + TRANSLATIONS: {}, + PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), + LOCALE: "unknown", + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext: (string) => { + const translated = Documentation.TRANSLATIONS[string]; + switch (typeof translated) { + case "undefined": + return string; // no translation + case "string": + return translated; // translation exists + default: + return translated[0]; // (singular, plural) translation tuple exists + } + }, + + ngettext: (singular, plural, n) => { + const translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated !== "undefined") + return translated[Documentation.PLURAL_EXPR(n)]; + return n === 1 ? singular : plural; + }, + + addTranslations: (catalog) => { + Object.assign(Documentation.TRANSLATIONS, catalog.messages); + Documentation.PLURAL_EXPR = new Function( + "n", + `return (${catalog.plural_expr})` + ); + Documentation.LOCALE = catalog.locale; + }, + + /** + * helper function to focus on search bar + */ + focusSearchBar: () => { + document.querySelectorAll("input[name=q]")[0]?.focus(); + }, + + /** + * Initialise the domain index toggle buttons + */ + initDomainIndexTable: () => { + const toggler = (el) => { + const idNumber = el.id.substr(7); + const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); + if (el.src.substr(-9) === "minus.png") { + el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; + toggledRows.forEach((el) => (el.style.display = "none")); + } else { + el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; + toggledRows.forEach((el) => (el.style.display = "")); + } + }; + + const togglerElements = document.querySelectorAll("img.toggler"); + togglerElements.forEach((el) => + el.addEventListener("click", (event) => toggler(event.currentTarget)) + ); + togglerElements.forEach((el) => (el.style.display = "")); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); + }, + + initOnKeyListeners: () => { + // only install a listener if it is really needed + if ( + !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && + !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS + ) + return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.altKey || event.ctrlKey || event.metaKey) return; + + if (!event.shiftKey) { + switch (event.key) { + case "ArrowLeft": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const prevLink = document.querySelector('link[rel="prev"]'); + if (prevLink && prevLink.href) { + window.location.href = prevLink.href; + event.preventDefault(); + } + break; + case "ArrowRight": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const nextLink = document.querySelector('link[rel="next"]'); + if (nextLink && nextLink.href) { + window.location.href = nextLink.href; + event.preventDefault(); + } + break; + } + } + + // some keyboard layouts may need Shift to get / + switch (event.key) { + case "/": + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; + Documentation.focusSearchBar(); + event.preventDefault(); + } + }); + }, +}; + +// quick alias for translations +const _ = Documentation.gettext; + +_ready(Documentation.init); diff --git a/_static/documentation_options.js b/_static/documentation_options.js new file mode 100644 index 00000000..4184d466 --- /dev/null +++ b/_static/documentation_options.js @@ -0,0 +1,14 @@ +var DOCUMENTATION_OPTIONS = { + URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), + VERSION: '0.9.0', + LANGUAGE: 'en', + COLLAPSE_INDEX: false, + BUILDER: 'html', + FILE_SUFFIX: '.html', + LINK_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: false, + SHOW_SEARCH_SUMMARY: true, + ENABLE_SEARCH_SHORTCUTS: true, +}; \ No newline at end of file diff --git a/_static/file.png b/_static/file.png new file mode 100644 index 0000000000000000000000000000000000000000..a858a410e4faa62ce324d814e4b816fff83a6fb3 GIT binary patch literal 286 zcmV+(0pb3MP)s`hMrGg#P~ix$^RISR_I47Y|r1 z_CyJOe}D1){SET-^Amu_i71Lt6eYfZjRyw@I6OQAIXXHDfiX^GbOlHe=Ae4>0m)d(f|Me07*qoM6N<$f}vM^LjV8( literal 0 HcmV?d00001 diff --git a/_static/jquery-3.6.0.js b/_static/jquery-3.6.0.js new file mode 100644 index 00000000..fc6c299b --- /dev/null +++ b/_static/jquery-3.6.0.js @@ -0,0 +1,10881 @@ +/*! + * jQuery JavaScript Library v3.6.0 + * https://jquery.com/ + * + * Includes Sizzle.js + * https://sizzlejs.com/ + * + * Copyright OpenJS Foundation and other contributors + * Released under the MIT license + * https://jquery.org/license + * + * Date: 2021-03-02T17:08Z + */ +( function( global, factory ) { + + "use strict"; + + if ( typeof module === "object" && typeof module.exports === "object" ) { + + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info. + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 +// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode +// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common +// enough that all such attempts are guarded in a try block. +"use strict"; + +var arr = []; + +var getProto = Object.getPrototypeOf; + +var slice = arr.slice; + +var flat = arr.flat ? function( array ) { + return arr.flat.call( array ); +} : function( array ) { + return arr.concat.apply( [], array ); +}; + + +var push = arr.push; + +var indexOf = arr.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var fnToString = hasOwn.toString; + +var ObjectFunctionString = fnToString.call( Object ); + +var support = {}; + +var isFunction = function isFunction( obj ) { + + // Support: Chrome <=57, Firefox <=52 + // In some browsers, typeof returns "function" for HTML elements + // (i.e., `typeof document.createElement( "object" ) === "function"`). + // We don't want to classify *any* DOM node as a function. + // Support: QtWeb <=3.8.5, WebKit <=534.34, wkhtmltopdf tool <=0.12.5 + // Plus for old WebKit, typeof returns "function" for HTML collections + // (e.g., `typeof document.getElementsByTagName("div") === "function"`). (gh-4756) + return typeof obj === "function" && typeof obj.nodeType !== "number" && + typeof obj.item !== "function"; + }; + + +var isWindow = function isWindow( obj ) { + return obj != null && obj === obj.window; + }; + + +var document = window.document; + + + + var preservedScriptAttributes = { + type: true, + src: true, + nonce: true, + noModule: true + }; + + function DOMEval( code, node, doc ) { + doc = doc || document; + + var i, val, + script = doc.createElement( "script" ); + + script.text = code; + if ( node ) { + for ( i in preservedScriptAttributes ) { + + // Support: Firefox 64+, Edge 18+ + // Some browsers don't support the "nonce" property on scripts. + // On the other hand, just using `getAttribute` is not enough as + // the `nonce` attribute is reset to an empty string whenever it + // becomes browsing-context connected. + // See https://github.com/whatwg/html/issues/2369 + // See https://html.spec.whatwg.org/#nonce-attributes + // The `node.getAttribute` check was added for the sake of + // `jQuery.globalEval` so that it can fake a nonce-containing node + // via an object. + val = node[ i ] || node.getAttribute && node.getAttribute( i ); + if ( val ) { + script.setAttribute( i, val ); + } + } + } + doc.head.appendChild( script ).parentNode.removeChild( script ); + } + + +function toType( obj ) { + if ( obj == null ) { + return obj + ""; + } + + // Support: Android <=2.3 only (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; +} +/* global Symbol */ +// Defining this global in .eslintrc.json would create a danger of using the global +// unguarded in another place, it seems safer to define global only for this module + + + +var + version = "3.6.0", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }; + +jQuery.fn = jQuery.prototype = { + + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + + // Return all the elements in a clean array + if ( num == null ) { + return slice.call( this ); + } + + // Return just the one element from the set + return num < 0 ? this[ num + this.length ] : this[ num ]; + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + each: function( callback ) { + return jQuery.each( this, callback ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + even: function() { + return this.pushStack( jQuery.grep( this, function( _elem, i ) { + return ( i + 1 ) % 2; + } ) ); + }, + + odd: function() { + return this.pushStack( jQuery.grep( this, function( _elem, i ) { + return i % 2; + } ) ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // Skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !isFunction( target ) ) { + target = {}; + } + + // Extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { + + // Extend the base object + for ( name in options ) { + copy = options[ name ]; + + // Prevent Object.prototype pollution + // Prevent never-ending loop + if ( name === "__proto__" || target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = Array.isArray( copy ) ) ) ) { + src = target[ name ]; + + // Ensure proper type for the source value + if ( copyIsArray && !Array.isArray( src ) ) { + clone = []; + } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) { + clone = {}; + } else { + clone = src; + } + copyIsArray = false; + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend( { + + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + isPlainObject: function( obj ) { + var proto, Ctor; + + // Detect obvious negatives + // Use toString instead of jQuery.type to catch host objects + if ( !obj || toString.call( obj ) !== "[object Object]" ) { + return false; + } + + proto = getProto( obj ); + + // Objects with no prototype (e.g., `Object.create( null )`) are plain + if ( !proto ) { + return true; + } + + // Objects with prototype are plain iff they were constructed by a global Object function + Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; + return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; + }, + + isEmptyObject: function( obj ) { + var name; + + for ( name in obj ) { + return false; + } + return true; + }, + + // Evaluates a script in a provided context; falls back to the global one + // if not specified. + globalEval: function( code, options, doc ) { + DOMEval( code, { nonce: options && options.nonce }, doc ); + }, + + each: function( obj, callback ) { + var length, i = 0; + + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } + + return obj; + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArrayLike( Object( arr ) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var length, value, + i = 0, + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return flat( ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +} ); + +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; +} + +// Populate the class2type map +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), + function( _i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); + } ); + +function isArrayLike( obj ) { + + // Support: real iOS 8.2 only (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = !!obj && "length" in obj && obj.length, + type = toType( obj ); + + if ( isFunction( obj ) || isWindow( obj ) ) { + return false; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v2.3.6 + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://js.foundation/ + * + * Date: 2021-02-16 + */ +( function( window ) { +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + 1 * new Date(), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + nonnativeSelectorCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // Instance methods + hasOwn = ( {} ).hasOwnProperty, + arr = [], + pop = arr.pop, + pushNative = arr.push, + push = arr.push, + slice = arr.slice, + + // Use a stripped-down indexOf as it's faster than native + // https://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { + var i = 0, + len = list.length; + for ( ; i < len; i++ ) { + if ( list[ i ] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|" + + "ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + + // https://www.w3.org/TR/css-syntax-3/#ident-token-diagram + identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace + + "?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+", + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + + // "Attribute values must be CSS identifiers [capture 5] + // or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + + whitespace + "*\\]", + + pseudos = ":(" + identifier + ")(?:\\((" + + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + + "*" ), + rdescend = new RegExp( whitespace + "|>" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + identifier + ")" ), + "CLASS": new RegExp( "^\\.(" + identifier + ")" ), + "TAG": new RegExp( "^(" + identifier + "|[*])" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + + whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + + whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rhtml = /HTML$/i, + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + + // CSS escapes + // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace + "?|\\\\([^\\r\\n\\f])", "g" ), + funescape = function( escape, nonHex ) { + var high = "0x" + escape.slice( 1 ) - 0x10000; + + return nonHex ? + + // Strip the backslash prefix from a non-hex escape sequence + nonHex : + + // Replace a hexadecimal escape sequence with the encoded Unicode code point + // Support: IE <=11+ + // For values outside the Basic Multilingual Plane (BMP), manually construct a + // surrogate pair + high < 0 ? + String.fromCharCode( high + 0x10000 ) : + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, + + // CSS string/identifier serialization + // https://drafts.csswg.org/cssom/#common-serializing-idioms + rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, + fcssescape = function( ch, asCodePoint ) { + if ( asCodePoint ) { + + // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER + if ( ch === "\0" ) { + return "\uFFFD"; + } + + // Control characters and (dependent upon position) numbers get escaped as code points + return ch.slice( 0, -1 ) + "\\" + + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; + } + + // Other potentially-special ASCII characters get backslash-escaped + return "\\" + ch; + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); + }, + + inDisabledFieldset = addCombinator( + function( elem ) { + return elem.disabled === true && elem.nodeName.toLowerCase() === "fieldset"; + }, + { dir: "parentNode", next: "legend" } + ); + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + ( arr = slice.call( preferredDoc.childNodes ) ), + preferredDoc.childNodes + ); + + // Support: Android<4.0 + // Detect silently failing push.apply + // eslint-disable-next-line no-unused-expressions + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + pushNative.apply( target, slice.call( els ) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + + // Can't trust NodeList.length + while ( ( target[ j++ ] = els[ i++ ] ) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var m, i, elem, nid, match, groups, newSelector, + newContext = context && context.ownerDocument, + + // nodeType defaults to 9, since context defaults to document + nodeType = context ? context.nodeType : 9; + + results = results || []; + + // Return early from calls with invalid selector or context + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + + // Try to shortcut find operations (as opposed to filters) in HTML documents + if ( !seed ) { + setDocument( context ); + context = context || document; + + if ( documentIsHTML ) { + + // If the selector is sufficiently simple, try using a "get*By*" DOM method + // (excepting DocumentFragment context, where the methods don't exist) + if ( nodeType !== 11 && ( match = rquickExpr.exec( selector ) ) ) { + + // ID selector + if ( ( m = match[ 1 ] ) ) { + + // Document context + if ( nodeType === 9 ) { + if ( ( elem = context.getElementById( m ) ) ) { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + + // Element context + } else { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( newContext && ( elem = newContext.getElementById( m ) ) && + contains( context, elem ) && + elem.id === m ) { + + results.push( elem ); + return results; + } + } + + // Type selector + } else if ( match[ 2 ] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Class selector + } else if ( ( m = match[ 3 ] ) && support.getElementsByClassName && + context.getElementsByClassName ) { + + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // Take advantage of querySelectorAll + if ( support.qsa && + !nonnativeSelectorCache[ selector + " " ] && + ( !rbuggyQSA || !rbuggyQSA.test( selector ) ) && + + // Support: IE 8 only + // Exclude object elements + ( nodeType !== 1 || context.nodeName.toLowerCase() !== "object" ) ) { + + newSelector = selector; + newContext = context; + + // qSA considers elements outside a scoping root when evaluating child or + // descendant combinators, which is not what we want. + // In such cases, we work around the behavior by prefixing every selector in the + // list with an ID selector referencing the scope context. + // The technique has to be used as well when a leading combinator is used + // as such selectors are not recognized by querySelectorAll. + // Thanks to Andrew Dupont for this technique. + if ( nodeType === 1 && + ( rdescend.test( selector ) || rcombinators.test( selector ) ) ) { + + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + + // We can use :scope instead of the ID hack if the browser + // supports it & if we're not changing the context. + if ( newContext !== context || !support.scope ) { + + // Capture the context ID, setting it first if necessary + if ( ( nid = context.getAttribute( "id" ) ) ) { + nid = nid.replace( rcssescape, fcssescape ); + } else { + context.setAttribute( "id", ( nid = expando ) ); + } + } + + // Prefix every selector in the list + groups = tokenize( selector ); + i = groups.length; + while ( i-- ) { + groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " + + toSelector( groups[ i ] ); + } + newSelector = groups.join( "," ); + } + + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + nonnativeSelectorCache( selector, true ); + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {function(string, object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return ( cache[ key + " " ] = value ); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created element and returns a boolean result + */ +function assert( fn ) { + var el = document.createElement( "fieldset" ); + + try { + return !!fn( el ); + } catch ( e ) { + return false; + } finally { + + // Remove from its parent by default + if ( el.parentNode ) { + el.parentNode.removeChild( el ); + } + + // release memory in IE + el = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split( "|" ), + i = arr.length; + + while ( i-- ) { + Expr.attrHandle[ arr[ i ] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + a.sourceIndex - b.sourceIndex; + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( ( cur = cur.nextSibling ) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return ( name === "input" || name === "button" ) && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for :enabled/:disabled + * @param {Boolean} disabled true for :disabled; false for :enabled + */ +function createDisabledPseudo( disabled ) { + + // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable + return function( elem ) { + + // Only certain elements can match :enabled or :disabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled + if ( "form" in elem ) { + + // Check for inherited disabledness on relevant non-disabled elements: + // * listed form-associated elements in a disabled fieldset + // https://html.spec.whatwg.org/multipage/forms.html#category-listed + // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled + // * option elements in a disabled optgroup + // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled + // All such elements have a "form" property. + if ( elem.parentNode && elem.disabled === false ) { + + // Option elements defer to a parent optgroup if present + if ( "label" in elem ) { + if ( "label" in elem.parentNode ) { + return elem.parentNode.disabled === disabled; + } else { + return elem.disabled === disabled; + } + } + + // Support: IE 6 - 11 + // Use the isDisabled shortcut property to check for disabled fieldset ancestors + return elem.isDisabled === disabled || + + // Where there is no isDisabled, check manually + /* jshint -W018 */ + elem.isDisabled !== !disabled && + inDisabledFieldset( elem ) === disabled; + } + + return elem.disabled === disabled; + + // Try to winnow out elements that can't be disabled before trusting the disabled property. + // Some victims get caught in our net (label, legend, menu, track), but it shouldn't + // even exist on them, let alone have a boolean value. + } else if ( "label" in elem ) { + return elem.disabled === disabled; + } + + // Remaining elements are neither :enabled nor :disabled + return false; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction( function( argument ) { + argument = +argument; + return markFunction( function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ ( j = matchIndexes[ i ] ) ] ) { + seed[ j ] = !( matches[ j ] = seed[ j ] ); + } + } + } ); + } ); +} + +/** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== "undefined" && context; +} + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ +isXML = Sizzle.isXML = function( elem ) { + var namespace = elem && elem.namespaceURI, + docElem = elem && ( elem.ownerDocument || elem ).documentElement; + + // Support: IE <=8 + // Assume HTML when documentElement doesn't yet exist, such as inside loading iframes + // https://bugs.jquery.com/ticket/4833 + return !rhtml.test( namespace || docElem && docElem.nodeName || "HTML" ); +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var hasCompare, subWindow, + doc = node ? node.ownerDocument || node : preferredDoc; + + // Return early if doc is invalid or already selected + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( doc == document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Update global variables + document = doc; + docElem = document.documentElement; + documentIsHTML = !isXML( document ); + + // Support: IE 9 - 11+, Edge 12 - 18+ + // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( preferredDoc != document && + ( subWindow = document.defaultView ) && subWindow.top !== subWindow ) { + + // Support: IE 11, Edge + if ( subWindow.addEventListener ) { + subWindow.addEventListener( "unload", unloadHandler, false ); + + // Support: IE 9 - 10 only + } else if ( subWindow.attachEvent ) { + subWindow.attachEvent( "onunload", unloadHandler ); + } + } + + // Support: IE 8 - 11+, Edge 12 - 18+, Chrome <=16 - 25 only, Firefox <=3.6 - 31 only, + // Safari 4 - 5 only, Opera <=11.6 - 12.x only + // IE/Edge & older browsers don't support the :scope pseudo-class. + // Support: Safari 6.0 only + // Safari 6.0 supports :scope but it's an alias of :root there. + support.scope = assert( function( el ) { + docElem.appendChild( el ).appendChild( document.createElement( "div" ) ); + return typeof el.querySelectorAll !== "undefined" && + !el.querySelectorAll( ":scope fieldset div" ).length; + } ); + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties + // (excepting IE8 booleans) + support.attributes = assert( function( el ) { + el.className = "i"; + return !el.getAttribute( "className" ); + } ); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert( function( el ) { + el.appendChild( document.createComment( "" ) ); + return !el.getElementsByTagName( "*" ).length; + } ); + + // Support: IE<9 + support.getElementsByClassName = rnative.test( document.getElementsByClassName ); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programmatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert( function( el ) { + docElem.appendChild( el ).id = expando; + return !document.getElementsByName || !document.getElementsByName( expando ).length; + } ); + + // ID filter and find + if ( support.getById ) { + Expr.filter[ "ID" ] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute( "id" ) === attrId; + }; + }; + Expr.find[ "ID" ] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var elem = context.getElementById( id ); + return elem ? [ elem ] : []; + } + }; + } else { + Expr.filter[ "ID" ] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== "undefined" && + elem.getAttributeNode( "id" ); + return node && node.value === attrId; + }; + }; + + // Support: IE 6 - 7 only + // getElementById is not reliable as a find shortcut + Expr.find[ "ID" ] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var node, i, elems, + elem = context.getElementById( id ); + + if ( elem ) { + + // Verify the id attribute + node = elem.getAttributeNode( "id" ); + if ( node && node.value === id ) { + return [ elem ]; + } + + // Fall back on getElementsByName + elems = context.getElementsByName( id ); + i = 0; + while ( ( elem = elems[ i++ ] ) ) { + node = elem.getAttributeNode( "id" ); + if ( node && node.value === id ) { + return [ elem ]; + } + } + } + + return []; + } + }; + } + + // Tag + Expr.find[ "TAG" ] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( tag ); + + // DocumentFragment nodes don't have gEBTN + } else if ( support.qsa ) { + return context.querySelectorAll( tag ); + } + } : + + function( tag, context ) { + var elem, + tmp = [], + i = 0, + + // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( ( elem = results[ i++ ] ) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find[ "CLASS" ] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See https://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( ( support.qsa = rnative.test( document.querySelectorAll ) ) ) { + + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert( function( el ) { + + var input; + + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // https://bugs.jquery.com/ticket/12359 + docElem.appendChild( el ).innerHTML = "" + + ""; + + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( el.querySelectorAll( "[msallowcapture^='']" ).length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !el.querySelectorAll( "[selected]" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ + if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + rbuggyQSA.push( "~=" ); + } + + // Support: IE 11+, Edge 15 - 18+ + // IE 11/Edge don't find elements on a `[name='']` query in some cases. + // Adding a temporary attribute to the document before the selection works + // around the issue. + // Interestingly, IE 10 & older don't seem to have the issue. + input = document.createElement( "input" ); + input.setAttribute( "name", "" ); + el.appendChild( input ); + if ( !el.querySelectorAll( "[name='']" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*name" + whitespace + "*=" + + whitespace + "*(?:''|\"\")" ); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !el.querySelectorAll( ":checked" ).length ) { + rbuggyQSA.push( ":checked" ); + } + + // Support: Safari 8+, iOS 8+ + // https://bugs.webkit.org/show_bug.cgi?id=136851 + // In-page `selector#id sibling-combinator selector` fails + if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { + rbuggyQSA.push( ".#.+[+~]" ); + } + + // Support: Firefox <=3.6 - 5 only + // Old Firefox doesn't throw on a badly-escaped identifier. + el.querySelectorAll( "\\\f" ); + rbuggyQSA.push( "[\\r\\n\\f]" ); + } ); + + assert( function( el ) { + el.innerHTML = "" + + ""; + + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = document.createElement( "input" ); + input.setAttribute( "type", "hidden" ); + el.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( el.querySelectorAll( "[name=d]" ).length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( el.querySelectorAll( ":enabled" ).length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: IE9-11+ + // IE's :disabled selector does not pick up the children of disabled fieldsets + docElem.appendChild( el ).disabled = true; + if ( el.querySelectorAll( ":disabled" ).length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: Opera 10 - 11 only + // Opera 10-11 does not throw on post-comma invalid pseudos + el.querySelectorAll( "*,:x" ); + rbuggyQSA.push( ",.*:" ); + } ); + } + + if ( ( support.matchesSelector = rnative.test( ( matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector ) ) ) ) { + + assert( function( el ) { + + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( el, "*" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( el, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + } ); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( "|" ) ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join( "|" ) ); + + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + // Element contains another + // Purposefully self-exclusive + // As in, an element does not contain itself + contains = hasCompare || rnative.test( docElem.contains ) ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + ) ); + } : + function( a, b ) { + if ( b ) { + while ( ( b = b.parentNode ) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = hasCompare ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + // Calculate position if both inputs belong to the same document + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + compare = ( a.ownerDocument || a ) == ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if ( compare & 1 || + ( !support.sortDetached && b.compareDocumentPosition( a ) === compare ) ) { + + // Choose the first element that is related to our preferred document + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( a == document || a.ownerDocument == preferredDoc && + contains( preferredDoc, a ) ) { + return -1; + } + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( b == document || b.ownerDocument == preferredDoc && + contains( preferredDoc, b ) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Parentless nodes are either documents or disconnected + if ( !aup || !bup ) { + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + /* eslint-disable eqeqeq */ + return a == document ? -1 : + b == document ? 1 : + /* eslint-enable eqeqeq */ + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( ( cur = cur.parentNode ) ) { + ap.unshift( cur ); + } + cur = b; + while ( ( cur = cur.parentNode ) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[ i ] === bp[ i ] ) { + i++; + } + + return i ? + + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[ i ], bp[ i ] ) : + + // Otherwise nodes in our document sort first + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + /* eslint-disable eqeqeq */ + ap[ i ] == preferredDoc ? -1 : + bp[ i ] == preferredDoc ? 1 : + /* eslint-enable eqeqeq */ + 0; + }; + + return document; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + setDocument( elem ); + + if ( support.matchesSelector && documentIsHTML && + !nonnativeSelectorCache[ expr + " " ] && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch ( e ) { + nonnativeSelectorCache( expr, true ); + } + } + + return Sizzle( expr, document, null, [ elem ] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + + // Set document vars if needed + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( ( context.ownerDocument || context ) != document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + + // Set document vars if needed + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( ( elem.ownerDocument || elem ) != document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + ( val = elem.getAttributeNode( name ) ) && val.specified ? + val.value : + null; +}; + +Sizzle.escape = function( sel ) { + return ( sel + "" ).replace( rcssescape, fcssescape ); +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( ( elem = results[ i++ ] ) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + + // If no nodeType, this is expected to be an array + while ( ( node = elem[ i++ ] ) ) { + + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[ 1 ] = match[ 1 ].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[ 3 ] = ( match[ 3 ] || match[ 4 ] || + match[ 5 ] || "" ).replace( runescape, funescape ); + + if ( match[ 2 ] === "~=" ) { + match[ 3 ] = " " + match[ 3 ] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[ 1 ] = match[ 1 ].toLowerCase(); + + if ( match[ 1 ].slice( 0, 3 ) === "nth" ) { + + // nth-* requires argument + if ( !match[ 3 ] ) { + Sizzle.error( match[ 0 ] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[ 4 ] = +( match[ 4 ] ? + match[ 5 ] + ( match[ 6 ] || 1 ) : + 2 * ( match[ 3 ] === "even" || match[ 3 ] === "odd" ) ); + match[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === "odd" ); + + // other types prohibit arguments + } else if ( match[ 3 ] ) { + Sizzle.error( match[ 0 ] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[ 6 ] && match[ 2 ]; + + if ( matchExpr[ "CHILD" ].test( match[ 0 ] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[ 3 ] ) { + match[ 2 ] = match[ 4 ] || match[ 5 ] || ""; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + + // Get excess from tokenize (recursively) + ( excess = tokenize( unquoted, true ) ) && + + // advance to the next closing parenthesis + ( excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length ) ) { + + // excess is a negative index + match[ 0 ] = match[ 0 ].slice( 0, excess ); + match[ 2 ] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { + return true; + } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + ( pattern = new RegExp( "(^|" + whitespace + + ")" + className + "(" + whitespace + "|$)" ) ) && classCache( + className, function( elem ) { + return pattern.test( + typeof elem.className === "string" && elem.className || + typeof elem.getAttribute !== "undefined" && + elem.getAttribute( "class" ) || + "" + ); + } ); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + /* eslint-disable max-len */ + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + /* eslint-enable max-len */ + + }; + }, + + "CHILD": function( type, what, _argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, _context, xml ) { + var cache, uniqueCache, outerCache, node, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType, + diff = false; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( ( node = node[ dir ] ) ) { + if ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) { + + return false; + } + } + + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + + // Seek `elem` from a previously-cached index + + // ...in a gzip-friendly way + node = parent; + outerCache = node[ expando ] || ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex && cache[ 2 ]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( ( node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + ( diff = nodeIndex = 0 ) || start.pop() ) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + } else { + + // Use previously-cached element index if available + if ( useCache ) { + + // ...in a gzip-friendly way + node = elem; + outerCache = node[ expando ] || ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex; + } + + // xml :nth-child(...) + // or :nth-last-child(...) or :nth(-last)?-of-type(...) + if ( diff === false ) { + + // Use the same loop as above to seek `elem` from the start + while ( ( node = ++nodeIndex && node && node[ dir ] || + ( diff = nodeIndex = 0 ) || start.pop() ) ) { + + if ( ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) && + ++diff ) { + + // Cache the index of each encountered element + if ( useCache ) { + outerCache = node[ expando ] || + ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + uniqueCache[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction( function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf( seed, matched[ i ] ); + seed[ idx ] = !( matches[ idx ] = matched[ i ] ); + } + } ) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + + // Potentially complex pseudos + "not": markFunction( function( selector ) { + + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction( function( seed, matches, _context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( ( elem = unmatched[ i ] ) ) { + seed[ i ] = !( matches[ i ] = elem ); + } + } + } ) : + function( elem, _context, xml ) { + input[ 0 ] = elem; + matcher( input, null, xml, results ); + + // Don't keep the element (issue #299) + input[ 0 ] = null; + return !results.pop(); + }; + } ), + + "has": markFunction( function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + } ), + + "contains": markFunction( function( text ) { + text = text.replace( runescape, funescape ); + return function( elem ) { + return ( elem.textContent || getText( elem ) ).indexOf( text ) > -1; + }; + } ), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + + // lang value must be a valid identifier + if ( !ridentifier.test( lang || "" ) ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( ( elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute( "xml:lang" ) || elem.getAttribute( "lang" ) ) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( ( elem = elem.parentNode ) && elem.nodeType === 1 ); + return false; + }; + } ), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && + ( !document.hasFocus || document.hasFocus() ) && + !!( elem.type || elem.href || ~elem.tabIndex ); + }, + + // Boolean properties + "enabled": createDisabledPseudo( false ), + "disabled": createDisabledPseudo( true ), + + "checked": function( elem ) { + + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return ( nodeName === "input" && !!elem.checked ) || + ( nodeName === "option" && !!elem.selected ); + }, + + "selected": function( elem ) { + + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + // eslint-disable-next-line no-unused-expressions + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos[ "empty" ]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ( ( attr = elem.getAttribute( "type" ) ) == null || + attr.toLowerCase() === "text" ); + }, + + // Position-in-collection + "first": createPositionalPseudo( function() { + return [ 0 ]; + } ), + + "last": createPositionalPseudo( function( _matchIndexes, length ) { + return [ length - 1 ]; + } ), + + "eq": createPositionalPseudo( function( _matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + } ), + + "even": createPositionalPseudo( function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "odd": createPositionalPseudo( function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "lt": createPositionalPseudo( function( matchIndexes, length, argument ) { + var i = argument < 0 ? + argument + length : + argument > length ? + length : + argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "gt": createPositionalPseudo( function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ) + } +}; + +Expr.pseudos[ "nth" ] = Expr.pseudos[ "eq" ]; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +tokenize = Sizzle.tokenize = function( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || ( match = rcomma.exec( soFar ) ) ) { + if ( match ) { + + // Don't consume trailing commas as valid + soFar = soFar.slice( match[ 0 ].length ) || soFar; + } + groups.push( ( tokens = [] ) ); + } + + matched = false; + + // Combinators + if ( ( match = rcombinators.exec( soFar ) ) ) { + matched = match.shift(); + tokens.push( { + value: matched, + + // Cast descendant combinators to space + type: match[ 0 ].replace( rtrim, " " ) + } ); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( ( match = matchExpr[ type ].exec( soFar ) ) && ( !preFilters[ type ] || + ( match = preFilters[ type ]( match ) ) ) ) { + matched = match.shift(); + tokens.push( { + value: matched, + type: type, + matches: match + } ); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +}; + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[ i ].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + skip = combinator.next, + key = skip || dir, + checkNonElements = base && key === "parentNode", + doneName = done++; + + return combinator.first ? + + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + return false; + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, uniqueCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching + if ( xml ) { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || ( elem[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ elem.uniqueID ] || + ( outerCache[ elem.uniqueID ] = {} ); + + if ( skip && skip === elem.nodeName.toLowerCase() ) { + elem = elem[ dir ] || elem; + } else if ( ( oldCache = uniqueCache[ key ] ) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return ( newCache[ 2 ] = oldCache[ 2 ] ); + } else { + + // Reuse newcache so results back-propagate to previous elements + uniqueCache[ key ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) { + return true; + } + } + } + } + } + return false; + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[ i ]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[ 0 ]; +} + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[ i ], results ); + } + return results; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( ( elem = unmatched[ i ] ) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction( function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( + selector || "*", + context.nodeType ? [ context ] : context, + [] + ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( ( elem = temp[ i ] ) ) { + matcherOut[ postMap[ i ] ] = !( matcherIn[ postMap[ i ] ] = elem ); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( ( elem = matcherOut[ i ] ) ) { + + // Restore matcherIn since elem is not yet a final match + temp.push( ( matcherIn[ i ] = elem ) ); + } + } + postFinder( null, ( matcherOut = [] ), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( ( elem = matcherOut[ i ] ) && + ( temp = postFinder ? indexOf( seed, elem ) : preMap[ i ] ) > -1 ) { + + seed[ temp ] = !( results[ temp ] = elem ); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + } ); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[ 0 ].type ], + implicitRelative = leadingRelative || Expr.relative[ " " ], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + ( checkContext = context ).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + + // Avoid hanging onto element (issue #299) + checkContext = null; + return ret; + } ]; + + for ( ; i < len; i++ ) { + if ( ( matcher = Expr.relative[ tokens[ i ].type ] ) ) { + matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ]; + } else { + matcher = Expr.filter[ tokens[ i ].type ].apply( null, tokens[ i ].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[ j ].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens + .slice( 0, i - 1 ) + .concat( { value: tokens[ i - 2 ].type === " " ? "*" : "" } ) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find[ "TAG" ]( "*", outermost ), + + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = ( dirruns += contextBackup == null ? 1 : Math.random() || 0.1 ), + len = elems.length; + + if ( outermost ) { + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + outermostContext = context == document || context || outermost; + } + + // Add elements passing elementMatchers directly to results + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id + for ( ; i !== len && ( elem = elems[ i ] ) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( !context && elem.ownerDocument != document ) { + setDocument( elem ); + xml = !documentIsHTML; + } + while ( ( matcher = elementMatchers[ j++ ] ) ) { + if ( matcher( elem, context || document, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + + // They will have gone through all possible matchers + if ( ( elem = !matcher && elem ) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // `i` is now the count of elements visited above, and adding it to `matchedCount` + // makes the latter nonnegative. + matchedCount += i; + + // Apply set filters to unmatched elements + // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` + // equals `i`), unless we didn't visit _any_ elements in the above loop because we have + // no element matchers and no seed. + // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that + // case, which will result in a "00" `matchedCount` that differs from `i` but is also + // numerically zero. + if ( bySet && i !== matchedCount ) { + j = 0; + while ( ( matcher = setMatchers[ j++ ] ) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !( unmatched[ i ] || setMatched[ i ] ) ) { + setMatched[ i ] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + + // Generate a function of recursive functions that can be used to check each element + if ( !match ) { + match = tokenize( selector ); + } + i = match.length; + while ( i-- ) { + cached = matcherFromTokens( match[ i ] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( + selector, + matcherFromGroupMatchers( elementMatchers, setMatchers ) + ); + + // Save selector and tokenization + cached.selector = selector; + } + return cached; +}; + +/** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ +select = Sizzle.select = function( selector, context, results, seed ) { + var i, tokens, token, type, find, + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( ( selector = compiled.selector || selector ) ); + + results = results || []; + + // Try to minimize operations if there is only one selector in the list and no seed + // (the latter of which guarantees us context) + if ( match.length === 1 ) { + + // Reduce context if the leading compound selector is an ID + tokens = match[ 0 ] = match[ 0 ].slice( 0 ); + if ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === "ID" && + context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[ 1 ].type ] ) { + + context = ( Expr.find[ "ID" ]( token.matches[ 0 ] + .replace( runescape, funescape ), context ) || [] )[ 0 ]; + if ( !context ) { + return results; + + // Precompiled matchers will still verify ancestry, so step up a level + } else if ( compiled ) { + context = context.parentNode; + } + + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr[ "needsContext" ].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[ i ]; + + // Abort if we hit a combinator + if ( Expr.relative[ ( type = token.type ) ] ) { + break; + } + if ( ( find = Expr.find[ type ] ) ) { + + // Search, expanding context for leading sibling combinators + if ( ( seed = find( + token.matches[ 0 ].replace( runescape, funescape ), + rsibling.test( tokens[ 0 ].type ) && testContext( context.parentNode ) || + context + ) ) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + + // Compile and execute a filtering function if one is not provided + // Provide `match` to avoid retokenization if we modified the selector above + ( compiled || compile( selector, match ) )( + seed, + context, + !documentIsHTML, + results, + !context || rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; +}; + +// One-time assignments + +// Sort stability +support.sortStable = expando.split( "" ).sort( sortOrder ).join( "" ) === expando; + +// Support: Chrome 14-35+ +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = !!hasDuplicate; + +// Initialize against the default document +setDocument(); + +// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Detached nodes confoundingly follow *each other* +support.sortDetached = assert( function( el ) { + + // Should return 1, but returns 4 (following) + return el.compareDocumentPosition( document.createElement( "fieldset" ) ) & 1; +} ); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert( function( el ) { + el.innerHTML = ""; + return el.firstChild.getAttribute( "href" ) === "#"; +} ) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + } ); +} + +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert( function( el ) { + el.innerHTML = ""; + el.firstChild.setAttribute( "value", "" ); + return el.firstChild.getAttribute( "value" ) === ""; +} ) ) { + addHandle( "value", function( elem, _name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + } ); +} + +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert( function( el ) { + return el.getAttribute( "disabled" ) == null; +} ) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return elem[ name ] === true ? name.toLowerCase() : + ( val = elem.getAttributeNode( name ) ) && val.specified ? + val.value : + null; + } + } ); +} + +return Sizzle; + +} )( window ); + + + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; + +// Deprecated +jQuery.expr[ ":" ] = jQuery.expr.pseudos; +jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; +jQuery.escapeSelector = Sizzle.escape; + + + + +var dir = function( elem, dir, until ) { + var matched = [], + truncate = until !== undefined; + + while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); + } + } + return matched; +}; + + +var siblings = function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; +}; + + +var rneedsContext = jQuery.expr.match.needsContext; + + + +function nodeName( elem, name ) { + + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + +} +var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); + + + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + return !!qualifier.call( elem, i, elem ) !== not; + } ); + } + + // Single element + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + } ); + } + + // Arraylike of elements (jQuery, arguments, Array) + if ( typeof qualifier !== "string" ) { + return jQuery.grep( elements, function( elem ) { + return ( indexOf.call( qualifier, elem ) > -1 ) !== not; + } ); + } + + // Filtered directly for both simple and complex selectors + return jQuery.filter( qualifier, elements, not ); +} + +jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + if ( elems.length === 1 && elem.nodeType === 1 ) { + return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; + } + + return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + } ) ); +}; + +jQuery.fn.extend( { + find: function( selector ) { + var i, ret, + len = this.length, + self = this; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter( function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + } ) ); + } + + ret = this.pushStack( [] ); + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + return len > 1 ? jQuery.uniqueSort( ret ) : ret; + }, + filter: function( selector ) { + return this.pushStack( winnow( this, selector || [], false ) ); + }, + not: function( selector ) { + return this.pushStack( winnow( this, selector || [], true ) ); + }, + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + } +} ); + + +// Initialize a jQuery object + + +// A central reference to the root jQuery(document) +var rootjQuery, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + // Shortcut simple #id case for speed + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, + + init = jQuery.fn.init = function( selector, context, root ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Method init() accepts an alternate rootjQuery + // so migrate can support jQuery.sub (gh-2101) + root = root || rootjQuery; + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector[ 0 ] === "<" && + selector[ selector.length - 1 ] === ">" && + selector.length >= 3 ) { + + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && ( match[ 1 ] || !context ) ) { + + // HANDLE: $(html) -> $(array) + if ( match[ 1 ] ) { + context = context instanceof jQuery ? context[ 0 ] : context; + + // Option to run scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[ 1 ], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + + // Properties of context are called as methods if possible + if ( isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[ 2 ] ); + + if ( elem ) { + + // Inject the element directly into the jQuery object + this[ 0 ] = elem; + this.length = 1; + } + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || root ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this[ 0 ] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( isFunction( selector ) ) { + return root.ready !== undefined ? + root.ready( selector ) : + + // Execute immediately if ready is not present + selector( jQuery ); + } + + return jQuery.makeArray( selector, this ); + }; + +// Give the init function the jQuery prototype for later instantiation +init.prototype = jQuery.fn; + +// Initialize central reference +rootjQuery = jQuery( document ); + + +var rparentsprev = /^(?:parents|prev(?:Until|All))/, + + // Methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend( { + has: function( target ) { + var targets = jQuery( target, this ), + l = targets.length; + + return this.filter( function() { + var i = 0; + for ( ; i < l; i++ ) { + if ( jQuery.contains( this, targets[ i ] ) ) { + return true; + } + } + } ); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + targets = typeof selectors !== "string" && jQuery( selectors ); + + // Positional selectors never match, since there's no _selection_ context + if ( !rneedsContext.test( selectors ) ) { + for ( ; i < l; i++ ) { + for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { + + // Always skip document fragments + if ( cur.nodeType < 11 && ( targets ? + targets.index( cur ) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector( cur, selectors ) ) ) { + + matched.push( cur ); + break; + } + } + } + } + + return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); + }, + + // Determine the position of an element within the set + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; + } + + // Index in selector + if ( typeof elem === "string" ) { + return indexOf.call( jQuery( elem ), this[ 0 ] ); + } + + // Locate the position of the desired element + return indexOf.call( this, + + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem + ); + }, + + add: function( selector, context ) { + return this.pushStack( + jQuery.uniqueSort( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + } +} ); + +function sibling( cur, dir ) { + while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} + return cur; +} + +jQuery.each( { + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, _i, until ) { + return dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, _i, until ) { + return dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, _i, until ) { + return dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return siblings( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return siblings( elem.firstChild ); + }, + contents: function( elem ) { + if ( elem.contentDocument != null && + + // Support: IE 11+ + // elements with no `data` attribute has an object + // `contentDocument` with a `null` prototype. + getProto( elem.contentDocument ) ) { + + return elem.contentDocument; + } + + // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only + // Treat the template element as a regular one in browsers that + // don't support it. + if ( nodeName( elem, "template" ) ) { + elem = elem.content || elem; + } + + return jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var matched = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + matched = jQuery.filter( selector, matched ); + } + + if ( this.length > 1 ) { + + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + jQuery.uniqueSort( matched ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + matched.reverse(); + } + } + + return this.pushStack( matched ); + }; +} ); +var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); + + + +// Convert String-formatted options into Object-formatted ones +function createOptions( options ) { + var object = {}; + jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { + object[ flag ] = true; + } ); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + createOptions( options ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + + // Last fire value for non-forgettable lists + memory, + + // Flag to know if list was already fired + fired, + + // Flag to prevent firing + locked, + + // Actual callback list + list = [], + + // Queue of execution data for repeatable lists + queue = [], + + // Index of currently firing callback (modified by add/remove as needed) + firingIndex = -1, + + // Fire callbacks + fire = function() { + + // Enforce single-firing + locked = locked || options.once; + + // Execute callbacks for all pending executions, + // respecting firingIndex overrides and runtime changes + fired = firing = true; + for ( ; queue.length; firingIndex = -1 ) { + memory = queue.shift(); + while ( ++firingIndex < list.length ) { + + // Run callback and check for early termination + if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && + options.stopOnFalse ) { + + // Jump to end and forget the data so .add doesn't re-fire + firingIndex = list.length; + memory = false; + } + } + } + + // Forget the data if we're done with it + if ( !options.memory ) { + memory = false; + } + + firing = false; + + // Clean up if we're done firing for good + if ( locked ) { + + // Keep an empty list if we have data for future add calls + if ( memory ) { + list = []; + + // Otherwise, this object is spent + } else { + list = ""; + } + } + }, + + // Actual Callbacks object + self = { + + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + + // If we have memory from a past run, we should fire after adding + if ( memory && !firing ) { + firingIndex = list.length - 1; + queue.push( memory ); + } + + ( function add( args ) { + jQuery.each( args, function( _, arg ) { + if ( isFunction( arg ) ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && toType( arg ) !== "string" ) { + + // Inspect recursively + add( arg ); + } + } ); + } )( arguments ); + + if ( memory && !firing ) { + fire(); + } + } + return this; + }, + + // Remove a callback from the list + remove: function() { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + + // Handle firing indexes + if ( index <= firingIndex ) { + firingIndex--; + } + } + } ); + return this; + }, + + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? + jQuery.inArray( fn, list ) > -1 : + list.length > 0; + }, + + // Remove all callbacks from the list + empty: function() { + if ( list ) { + list = []; + } + return this; + }, + + // Disable .fire and .add + // Abort any current/pending executions + // Clear all callbacks and values + disable: function() { + locked = queue = []; + list = memory = ""; + return this; + }, + disabled: function() { + return !list; + }, + + // Disable .fire + // Also disable .add unless we have memory (since it would have no effect) + // Abort any pending executions + lock: function() { + locked = queue = []; + if ( !memory && !firing ) { + list = memory = ""; + } + return this; + }, + locked: function() { + return !!locked; + }, + + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( !locked ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + queue.push( args ); + if ( !firing ) { + fire(); + } + } + return this; + }, + + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + + +function Identity( v ) { + return v; +} +function Thrower( ex ) { + throw ex; +} + +function adoptValue( value, resolve, reject, noValue ) { + var method; + + try { + + // Check for promise aspect first to privilege synchronous behavior + if ( value && isFunction( ( method = value.promise ) ) ) { + method.call( value ).done( resolve ).fail( reject ); + + // Other thenables + } else if ( value && isFunction( ( method = value.then ) ) ) { + method.call( value, resolve, reject ); + + // Other non-thenables + } else { + + // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: + // * false: [ value ].slice( 0 ) => resolve( value ) + // * true: [ value ].slice( 1 ) => resolve() + resolve.apply( undefined, [ value ].slice( noValue ) ); + } + + // For Promises/A+, convert exceptions into rejections + // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in + // Deferred#then to conditionally suppress rejection. + } catch ( value ) { + + // Support: Android 4.0 only + // Strict mode functions invoked without .call/.apply get global-object context + reject.apply( undefined, [ value ] ); + } +} + +jQuery.extend( { + + Deferred: function( func ) { + var tuples = [ + + // action, add listener, callbacks, + // ... .then handlers, argument index, [final state] + [ "notify", "progress", jQuery.Callbacks( "memory" ), + jQuery.Callbacks( "memory" ), 2 ], + [ "resolve", "done", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 0, "resolved" ], + [ "reject", "fail", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 1, "rejected" ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + "catch": function( fn ) { + return promise.then( null, fn ); + }, + + // Keep pipe for back-compat + pipe: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + + return jQuery.Deferred( function( newDefer ) { + jQuery.each( tuples, function( _i, tuple ) { + + // Map tuples (progress, done, fail) to arguments (done, fail, progress) + var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; + + // deferred.progress(function() { bind to newDefer or newDefer.notify }) + // deferred.done(function() { bind to newDefer or newDefer.resolve }) + // deferred.fail(function() { bind to newDefer or newDefer.reject }) + deferred[ tuple[ 1 ] ]( function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && isFunction( returned.promise ) ) { + returned.promise() + .progress( newDefer.notify ) + .done( newDefer.resolve ) + .fail( newDefer.reject ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( + this, + fn ? [ returned ] : arguments + ); + } + } ); + } ); + fns = null; + } ).promise(); + }, + then: function( onFulfilled, onRejected, onProgress ) { + var maxDepth = 0; + function resolve( depth, deferred, handler, special ) { + return function() { + var that = this, + args = arguments, + mightThrow = function() { + var returned, then; + + // Support: Promises/A+ section 2.3.3.3.3 + // https://promisesaplus.com/#point-59 + // Ignore double-resolution attempts + if ( depth < maxDepth ) { + return; + } + + returned = handler.apply( that, args ); + + // Support: Promises/A+ section 2.3.1 + // https://promisesaplus.com/#point-48 + if ( returned === deferred.promise() ) { + throw new TypeError( "Thenable self-resolution" ); + } + + // Support: Promises/A+ sections 2.3.3.1, 3.5 + // https://promisesaplus.com/#point-54 + // https://promisesaplus.com/#point-75 + // Retrieve `then` only once + then = returned && + + // Support: Promises/A+ section 2.3.4 + // https://promisesaplus.com/#point-64 + // Only check objects and functions for thenability + ( typeof returned === "object" || + typeof returned === "function" ) && + returned.then; + + // Handle a returned thenable + if ( isFunction( then ) ) { + + // Special processors (notify) just wait for resolution + if ( special ) { + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ) + ); + + // Normal processors (resolve) also hook into progress + } else { + + // ...and disregard older resolution values + maxDepth++; + + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ), + resolve( maxDepth, deferred, Identity, + deferred.notifyWith ) + ); + } + + // Handle all other returned values + } else { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Identity ) { + that = undefined; + args = [ returned ]; + } + + // Process the value(s) + // Default process is resolve + ( special || deferred.resolveWith )( that, args ); + } + }, + + // Only normal processors (resolve) catch and reject exceptions + process = special ? + mightThrow : + function() { + try { + mightThrow(); + } catch ( e ) { + + if ( jQuery.Deferred.exceptionHook ) { + jQuery.Deferred.exceptionHook( e, + process.stackTrace ); + } + + // Support: Promises/A+ section 2.3.3.3.4.1 + // https://promisesaplus.com/#point-61 + // Ignore post-resolution exceptions + if ( depth + 1 >= maxDepth ) { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Thrower ) { + that = undefined; + args = [ e ]; + } + + deferred.rejectWith( that, args ); + } + } + }; + + // Support: Promises/A+ section 2.3.3.3.1 + // https://promisesaplus.com/#point-57 + // Re-resolve promises immediately to dodge false rejection from + // subsequent errors + if ( depth ) { + process(); + } else { + + // Call an optional hook to record the stack, in case of exception + // since it's otherwise lost when execution goes async + if ( jQuery.Deferred.getStackHook ) { + process.stackTrace = jQuery.Deferred.getStackHook(); + } + window.setTimeout( process ); + } + }; + } + + return jQuery.Deferred( function( newDefer ) { + + // progress_handlers.add( ... ) + tuples[ 0 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onProgress ) ? + onProgress : + Identity, + newDefer.notifyWith + ) + ); + + // fulfilled_handlers.add( ... ) + tuples[ 1 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onFulfilled ) ? + onFulfilled : + Identity + ) + ); + + // rejected_handlers.add( ... ) + tuples[ 2 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onRejected ) ? + onRejected : + Thrower + ) + ); + } ).promise(); + }, + + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 5 ]; + + // promise.progress = list.add + // promise.done = list.add + // promise.fail = list.add + promise[ tuple[ 1 ] ] = list.add; + + // Handle state + if ( stateString ) { + list.add( + function() { + + // state = "resolved" (i.e., fulfilled) + // state = "rejected" + state = stateString; + }, + + // rejected_callbacks.disable + // fulfilled_callbacks.disable + tuples[ 3 - i ][ 2 ].disable, + + // rejected_handlers.disable + // fulfilled_handlers.disable + tuples[ 3 - i ][ 3 ].disable, + + // progress_callbacks.lock + tuples[ 0 ][ 2 ].lock, + + // progress_handlers.lock + tuples[ 0 ][ 3 ].lock + ); + } + + // progress_handlers.fire + // fulfilled_handlers.fire + // rejected_handlers.fire + list.add( tuple[ 3 ].fire ); + + // deferred.notify = function() { deferred.notifyWith(...) } + // deferred.resolve = function() { deferred.resolveWith(...) } + // deferred.reject = function() { deferred.rejectWith(...) } + deferred[ tuple[ 0 ] ] = function() { + deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); + return this; + }; + + // deferred.notifyWith = list.fireWith + // deferred.resolveWith = list.fireWith + // deferred.rejectWith = list.fireWith + deferred[ tuple[ 0 ] + "With" ] = list.fireWith; + } ); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( singleValue ) { + var + + // count of uncompleted subordinates + remaining = arguments.length, + + // count of unprocessed arguments + i = remaining, + + // subordinate fulfillment data + resolveContexts = Array( i ), + resolveValues = slice.call( arguments ), + + // the primary Deferred + primary = jQuery.Deferred(), + + // subordinate callback factory + updateFunc = function( i ) { + return function( value ) { + resolveContexts[ i ] = this; + resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( !( --remaining ) ) { + primary.resolveWith( resolveContexts, resolveValues ); + } + }; + }; + + // Single- and empty arguments are adopted like Promise.resolve + if ( remaining <= 1 ) { + adoptValue( singleValue, primary.done( updateFunc( i ) ).resolve, primary.reject, + !remaining ); + + // Use .then() to unwrap secondary thenables (cf. gh-3000) + if ( primary.state() === "pending" || + isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { + + return primary.then(); + } + } + + // Multiple arguments are aggregated like Promise.all array elements + while ( i-- ) { + adoptValue( resolveValues[ i ], updateFunc( i ), primary.reject ); + } + + return primary.promise(); + } +} ); + + +// These usually indicate a programmer mistake during development, +// warn about them ASAP rather than swallowing them by default. +var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; + +jQuery.Deferred.exceptionHook = function( error, stack ) { + + // Support: IE 8 - 9 only + // Console exists when dev tools are open, which can happen at any time + if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { + window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack ); + } +}; + + + + +jQuery.readyException = function( error ) { + window.setTimeout( function() { + throw error; + } ); +}; + + + + +// The deferred used on DOM ready +var readyList = jQuery.Deferred(); + +jQuery.fn.ready = function( fn ) { + + readyList + .then( fn ) + + // Wrap jQuery.readyException in a function so that the lookup + // happens at the time of error handling instead of callback + // registration. + .catch( function( error ) { + jQuery.readyException( error ); + } ); + + return this; +}; + +jQuery.extend( { + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + } +} ); + +jQuery.ready.then = readyList.then; + +// The ready event handler and self cleanup method +function completed() { + document.removeEventListener( "DOMContentLoaded", completed ); + window.removeEventListener( "load", completed ); + jQuery.ready(); +} + +// Catch cases where $(document).ready() is called +// after the browser event has already occurred. +// Support: IE <=9 - 10 only +// Older IE sometimes signals "interactive" too soon +if ( document.readyState === "complete" || + ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { + + // Handle it asynchronously to allow scripts the opportunity to delay ready + window.setTimeout( jQuery.ready ); + +} else { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed ); +} + + + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + len = elems.length, + bulk = key == null; + + // Sets many values + if ( toType( key ) === "object" ) { + chainable = true; + for ( i in key ) { + access( elems, fn, i, key[ i ], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, _key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < len; i++ ) { + fn( + elems[ i ], key, raw ? + value : + value.call( elems[ i ], i, fn( elems[ i ], key ) ) + ); + } + } + } + + if ( chainable ) { + return elems; + } + + // Gets + if ( bulk ) { + return fn.call( elems ); + } + + return len ? fn( elems[ 0 ], key ) : emptyGet; +}; + + +// Matches dashed string for camelizing +var rmsPrefix = /^-ms-/, + rdashAlpha = /-([a-z])/g; + +// Used by camelCase as callback to replace() +function fcamelCase( _all, letter ) { + return letter.toUpperCase(); +} + +// Convert dashed to camelCase; used by the css and data modules +// Support: IE <=9 - 11, Edge 12 - 15 +// Microsoft forgot to hump their vendor prefix (#9572) +function camelCase( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); +} +var acceptData = function( owner ) { + + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); +}; + + + + +function Data() { + this.expando = jQuery.expando + Data.uid++; +} + +Data.uid = 1; + +Data.prototype = { + + cache: function( owner ) { + + // Check if the owner object already has a cache + var value = owner[ this.expando ]; + + // If not, create one + if ( !value ) { + value = {}; + + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return an empty object. + if ( acceptData( owner ) ) { + + // If it is a node unlikely to be stringify-ed or looped over + // use plain assignment + if ( owner.nodeType ) { + owner[ this.expando ] = value; + + // Otherwise secure it in a non-enumerable property + // configurable must be true to allow the property to be + // deleted when data is removed + } else { + Object.defineProperty( owner, this.expando, { + value: value, + configurable: true + } ); + } + } + } + + return value; + }, + set: function( owner, data, value ) { + var prop, + cache = this.cache( owner ); + + // Handle: [ owner, key, value ] args + // Always use camelCase key (gh-2257) + if ( typeof data === "string" ) { + cache[ camelCase( data ) ] = value; + + // Handle: [ owner, { properties } ] args + } else { + + // Copy the properties one-by-one to the cache object + for ( prop in data ) { + cache[ camelCase( prop ) ] = data[ prop ]; + } + } + return cache; + }, + get: function( owner, key ) { + return key === undefined ? + this.cache( owner ) : + + // Always use camelCase key (gh-2257) + owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ]; + }, + access: function( owner, key, value ) { + + // In cases where either: + // + // 1. No key was specified + // 2. A string key was specified, but no value provided + // + // Take the "read" path and allow the get method to determine + // which value to return, respectively either: + // + // 1. The entire cache object + // 2. The data stored at the key + // + if ( key === undefined || + ( ( key && typeof key === "string" ) && value === undefined ) ) { + + return this.get( owner, key ); + } + + // When the key is not a string, or both a key and value + // are specified, set or extend (existing objects) with either: + // + // 1. An object of properties + // 2. A key and value + // + this.set( owner, key, value ); + + // Since the "set" path can have two possible entry points + // return the expected data based on which path was taken[*] + return value !== undefined ? value : key; + }, + remove: function( owner, key ) { + var i, + cache = owner[ this.expando ]; + + if ( cache === undefined ) { + return; + } + + if ( key !== undefined ) { + + // Support array or space separated string of keys + if ( Array.isArray( key ) ) { + + // If key is an array of keys... + // We always set camelCase keys, so remove that. + key = key.map( camelCase ); + } else { + key = camelCase( key ); + + // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + key = key in cache ? + [ key ] : + ( key.match( rnothtmlwhite ) || [] ); + } + + i = key.length; + + while ( i-- ) { + delete cache[ key[ i ] ]; + } + } + + // Remove the expando if there's no more data + if ( key === undefined || jQuery.isEmptyObject( cache ) ) { + + // Support: Chrome <=35 - 45 + // Webkit & Blink performance suffers when deleting properties + // from DOM nodes, so set to undefined instead + // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) + if ( owner.nodeType ) { + owner[ this.expando ] = undefined; + } else { + delete owner[ this.expando ]; + } + } + }, + hasData: function( owner ) { + var cache = owner[ this.expando ]; + return cache !== undefined && !jQuery.isEmptyObject( cache ); + } +}; +var dataPriv = new Data(); + +var dataUser = new Data(); + + + +// Implementation Summary +// +// 1. Enforce API surface and semantic compatibility with 1.9.x branch +// 2. Improve the module's maintainability by reducing the storage +// paths to a single mechanism. +// 3. Use the same single mechanism to support "private" and "user" data. +// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) +// 5. Avoid exposing implementation details on user objects (eg. expando properties) +// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 + +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /[A-Z]/g; + +function getData( data ) { + if ( data === "true" ) { + return true; + } + + if ( data === "false" ) { + return false; + } + + if ( data === "null" ) { + return null; + } + + // Only convert to a number if it doesn't change the string + if ( data === +data + "" ) { + return +data; + } + + if ( rbrace.test( data ) ) { + return JSON.parse( data ); + } + + return data; +} + +function dataAttr( elem, key, data ) { + var name; + + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = getData( data ); + } catch ( e ) {} + + // Make sure we set the data so it isn't changed later + dataUser.set( elem, key, data ); + } else { + data = undefined; + } + } + return data; +} + +jQuery.extend( { + hasData: function( elem ) { + return dataUser.hasData( elem ) || dataPriv.hasData( elem ); + }, + + data: function( elem, name, data ) { + return dataUser.access( elem, name, data ); + }, + + removeData: function( elem, name ) { + dataUser.remove( elem, name ); + }, + + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to dataPriv methods, these can be deprecated. + _data: function( elem, name, data ) { + return dataPriv.access( elem, name, data ); + }, + + _removeData: function( elem, name ) { + dataPriv.remove( elem, name ); + } +} ); + +jQuery.fn.extend( { + data: function( key, value ) { + var i, name, data, + elem = this[ 0 ], + attrs = elem && elem.attributes; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = dataUser.get( elem ); + + if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE 11 only + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = camelCase( name.slice( 5 ) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + dataPriv.set( elem, "hasDataAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each( function() { + dataUser.set( this, key ); + } ); + } + + return access( this, function( value ) { + var data; + + // The calling jQuery object (element matches) is not empty + // (and therefore has an element appears at this[ 0 ]) and the + // `value` parameter was not undefined. An empty jQuery object + // will result in `undefined` for elem = this[ 0 ] which will + // throw an exception if an attempt to read a data cache is made. + if ( elem && value === undefined ) { + + // Attempt to get data from the cache + // The key will always be camelCased in Data + data = dataUser.get( elem, key ); + if ( data !== undefined ) { + return data; + } + + // Attempt to "discover" the data in + // HTML5 custom data-* attrs + data = dataAttr( elem, key ); + if ( data !== undefined ) { + return data; + } + + // We tried really hard, but the data doesn't exist. + return; + } + + // Set the data... + this.each( function() { + + // We always store the camelCased key + dataUser.set( this, key, value ); + } ); + }, null, value, arguments.length > 1, null, true ); + }, + + removeData: function( key ) { + return this.each( function() { + dataUser.remove( this, key ); + } ); + } +} ); + + +jQuery.extend( { + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = dataPriv.get( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || Array.isArray( data ) ) { + queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // Clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // Not public - generate a queueHooks object, or return the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { + empty: jQuery.Callbacks( "once memory" ).add( function() { + dataPriv.remove( elem, [ type + "queue", key ] ); + } ) + } ); + } +} ); + +jQuery.fn.extend( { + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[ 0 ], type ); + } + + return data === undefined ? + this : + this.each( function() { + var queue = jQuery.queue( this, type, data ); + + // Ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + } ); + }, + dequeue: function( type ) { + return this.each( function() { + jQuery.dequeue( this, type ); + } ); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while ( i-- ) { + tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +} ); +var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; + +var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); + + +var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; + +var documentElement = document.documentElement; + + + + var isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ); + }, + composed = { composed: true }; + + // Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only + // Check attachment across shadow DOM boundaries when possible (gh-3504) + // Support: iOS 10.0-10.2 only + // Early iOS 10 versions support `attachShadow` but not `getRootNode`, + // leading to errors. We need to check for `getRootNode`. + if ( documentElement.getRootNode ) { + isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ) || + elem.getRootNode( composed ) === elem.ownerDocument; + }; + } +var isHiddenWithinTree = function( elem, el ) { + + // isHiddenWithinTree might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + + // Inline style trumps all + return elem.style.display === "none" || + elem.style.display === "" && + + // Otherwise, check computed style + // Support: Firefox <=43 - 45 + // Disconnected elements can have computed display: none, so first confirm that elem is + // in the document. + isAttached( elem ) && + + jQuery.css( elem, "display" ) === "none"; + }; + + + +function adjustCSS( elem, prop, valueParts, tween ) { + var adjusted, scale, + maxIterations = 20, + currentValue = tween ? + function() { + return tween.cur(); + } : + function() { + return jQuery.css( elem, prop, "" ); + }, + initial = currentValue(), + unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), + + // Starting value computation is required for potential unit mismatches + initialInUnit = elem.nodeType && + ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && + rcssNum.exec( jQuery.css( elem, prop ) ); + + if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { + + // Support: Firefox <=54 + // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144) + initial = initial / 2; + + // Trust units reported by jQuery.css + unit = unit || initialInUnit[ 3 ]; + + // Iteratively approximate from a nonzero starting point + initialInUnit = +initial || 1; + + while ( maxIterations-- ) { + + // Evaluate and update our best guess (doubling guesses that zero out). + // Finish if the scale equals or crosses 1 (making the old*new product non-positive). + jQuery.style( elem, prop, initialInUnit + unit ); + if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) { + maxIterations = 0; + } + initialInUnit = initialInUnit / scale; + + } + + initialInUnit = initialInUnit * 2; + jQuery.style( elem, prop, initialInUnit + unit ); + + // Make sure we update the tween properties later on + valueParts = valueParts || []; + } + + if ( valueParts ) { + initialInUnit = +initialInUnit || +initial || 0; + + // Apply relative offset (+=/-=) if specified + adjusted = valueParts[ 1 ] ? + initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : + +valueParts[ 2 ]; + if ( tween ) { + tween.unit = unit; + tween.start = initialInUnit; + tween.end = adjusted; + } + } + return adjusted; +} + + +var defaultDisplayMap = {}; + +function getDefaultDisplay( elem ) { + var temp, + doc = elem.ownerDocument, + nodeName = elem.nodeName, + display = defaultDisplayMap[ nodeName ]; + + if ( display ) { + return display; + } + + temp = doc.body.appendChild( doc.createElement( nodeName ) ); + display = jQuery.css( temp, "display" ); + + temp.parentNode.removeChild( temp ); + + if ( display === "none" ) { + display = "block"; + } + defaultDisplayMap[ nodeName ] = display; + + return display; +} + +function showHide( elements, show ) { + var display, elem, + values = [], + index = 0, + length = elements.length; + + // Determine new display value for elements that need to change + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + + display = elem.style.display; + if ( show ) { + + // Since we force visibility upon cascade-hidden elements, an immediate (and slow) + // check is required in this first loop unless we have a nonempty display value (either + // inline or about-to-be-restored) + if ( display === "none" ) { + values[ index ] = dataPriv.get( elem, "display" ) || null; + if ( !values[ index ] ) { + elem.style.display = ""; + } + } + if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { + values[ index ] = getDefaultDisplay( elem ); + } + } else { + if ( display !== "none" ) { + values[ index ] = "none"; + + // Remember what we're overwriting + dataPriv.set( elem, "display", display ); + } + } + } + + // Set the display of the elements in a second loop to avoid constant reflow + for ( index = 0; index < length; index++ ) { + if ( values[ index ] != null ) { + elements[ index ].style.display = values[ index ]; + } + } + + return elements; +} + +jQuery.fn.extend( { + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state ) { + if ( typeof state === "boolean" ) { + return state ? this.show() : this.hide(); + } + + return this.each( function() { + if ( isHiddenWithinTree( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + } ); + } +} ); +var rcheckableType = ( /^(?:checkbox|radio)$/i ); + +var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i ); + +var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i ); + + + +( function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); + + // Support: Android 4.0 - 4.3 only + // Check state lost if the name is set (#11217) + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (#14901) + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // Support: Android <=4.1 only + // Older WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE <=11 only + // Make sure textarea (and checkbox) defaultValue is properly cloned + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; + + // Support: IE <=9 only + // IE <=9 replaces "; + support.option = !!div.lastChild; +} )(); + + +// We have to close these tags to support XHTML (#13200) +var wrapMap = { + + // XHTML parsers do not magically insert elements in the + // same way that tag soup parsers do. So we cannot shorten + // this by omitting or other required elements. + thead: [ 1, "", "
" ], + col: [ 2, "", "
" ], + tr: [ 2, "", "
" ], + td: [ 3, "", "
" ], + + _default: [ 0, "", "" ] +}; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// Support: IE <=9 only +if ( !support.option ) { + wrapMap.optgroup = wrapMap.option = [ 1, "" ]; +} + + +function getAll( context, tag ) { + + // Support: IE <=9 - 11 only + // Use typeof to avoid zero-argument method invocation on host objects (#15151) + var ret; + + if ( typeof context.getElementsByTagName !== "undefined" ) { + ret = context.getElementsByTagName( tag || "*" ); + + } else if ( typeof context.querySelectorAll !== "undefined" ) { + ret = context.querySelectorAll( tag || "*" ); + + } else { + ret = []; + } + + if ( tag === undefined || tag && nodeName( context, tag ) ) { + return jQuery.merge( [ context ], ret ); + } + + return ret; +} + + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + dataPriv.set( + elems[ i ], + "globalEval", + !refElements || dataPriv.get( refElements[ i ], "globalEval" ) + ); + } +} + + +var rhtml = /<|&#?\w+;/; + +function buildFragment( elems, context, scripts, selection, ignored ) { + var elem, tmp, tag, wrap, attached, j, + fragment = context.createDocumentFragment(), + nodes = [], + i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( toType( elem ) === "object" ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; + + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, tmp.childNodes ); + + // Remember the top-level container + tmp = fragment.firstChild; + + // Ensure the created nodes are orphaned (#12392) + tmp.textContent = ""; + } + } + } + + // Remove wrapper from fragment + fragment.textContent = ""; + + i = 0; + while ( ( elem = nodes[ i++ ] ) ) { + + // Skip elements already in the context collection (trac-4087) + if ( selection && jQuery.inArray( elem, selection ) > -1 ) { + if ( ignored ) { + ignored.push( elem ); + } + continue; + } + + attached = isAttached( elem ); + + // Append to fragment + tmp = getAll( fragment.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( attached ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( ( elem = tmp[ j++ ] ) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + return fragment; +} + + +var rtypenamespace = /^([^.]*)(?:\.(.+)|)/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +// Support: IE <=9 - 11+ +// focus() and blur() are asynchronous, except when they are no-op. +// So expect focus to be synchronous when the element is already active, +// and blur to be synchronous when the element is not already active. +// (focus and blur are always synchronous in other supported browsers, +// this just defines when we can count on it). +function expectSync( elem, type ) { + return ( elem === safeActiveElement() ) === ( type === "focus" ); +} + +// Support: IE <=9 only +// Accessing document.activeElement can throw unexpectedly +// https://bugs.jquery.com/ticket/13393 +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +function on( elem, types, selector, data, fn, one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + on( elem, type, selector, data, types[ type ], one ); + } + return elem; + } + + if ( data == null && fn == null ) { + + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return elem; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return elem.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + } ); +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + + var handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.get( elem ); + + // Only attach events to objects that accept data + if ( !acceptData( elem ) ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Ensure that invalid selectors throw exceptions at attach time + // Evaluate against documentElement in case elem is a non-element node (e.g., document) + if ( selector ) { + jQuery.find.matchesSelector( documentElement, selector ); + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !( events = elemData.events ) ) { + events = elemData.events = Object.create( null ); + } + if ( !( eventHandle = elemData.handle ) ) { + eventHandle = elemData.handle = function( e ) { + + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; + }; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend( { + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join( "." ) + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !( handlers = events[ type ] ) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener if the special events handler returns false + if ( !special.setup || + special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); + + if ( !elemData || !( events = elemData.events ) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[ 2 ] && + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || + selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || + special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove data and the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + dataPriv.remove( elem, "handle events" ); + } + }, + + dispatch: function( nativeEvent ) { + + var i, j, ret, matched, handleObj, handlerQueue, + args = new Array( arguments.length ), + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( nativeEvent ), + + handlers = ( + dataPriv.get( this, "events" ) || Object.create( null ) + )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[ 0 ] = event; + + for ( i = 1; i < arguments.length; i++ ) { + args[ i ] = arguments[ i ]; + } + + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( ( handleObj = matched.handlers[ j++ ] ) && + !event.isImmediatePropagationStopped() ) { + + // If the event is namespaced, then each handler is only invoked if it is + // specially universal or its namespaces are a superset of the event's. + if ( !event.rnamespace || handleObj.namespace === false || + event.rnamespace.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || + handleObj.handler ).apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( ( event.result = ret ) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var i, handleObj, sel, matchedHandlers, matchedSelectors, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + if ( delegateCount && + + // Support: IE <=9 + // Black-hole SVG instance trees (trac-13180) + cur.nodeType && + + // Support: Firefox <=42 + // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) + // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click + // Support: IE 11 only + // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) + !( event.type === "click" && event.button >= 1 ) ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { + matchedHandlers = []; + matchedSelectors = {}; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matchedSelectors[ sel ] === undefined ) { + matchedSelectors[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) > -1 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matchedSelectors[ sel ] ) { + matchedHandlers.push( handleObj ); + } + } + if ( matchedHandlers.length ) { + handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); + } + } + } + } + + // Add the remaining (directly-bound) handlers + cur = this; + if ( delegateCount < handlers.length ) { + handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); + } + + return handlerQueue; + }, + + addProp: function( name, hook ) { + Object.defineProperty( jQuery.Event.prototype, name, { + enumerable: true, + configurable: true, + + get: isFunction( hook ) ? + function() { + if ( this.originalEvent ) { + return hook( this.originalEvent ); + } + } : + function() { + if ( this.originalEvent ) { + return this.originalEvent[ name ]; + } + }, + + set: function( value ) { + Object.defineProperty( this, name, { + enumerable: true, + configurable: true, + writable: true, + value: value + } ); + } + } ); + }, + + fix: function( originalEvent ) { + return originalEvent[ jQuery.expando ] ? + originalEvent : + new jQuery.Event( originalEvent ); + }, + + special: { + load: { + + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + click: { + + // Utilize native event to ensure correct state for checkable inputs + setup: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Claim the first handler + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + // dataPriv.set( el, "click", ... ) + leverageNative( el, "click", returnTrue ); + } + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Force setup before triggering a click + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + leverageNative( el, "click" ); + } + + // Return non-false to allow normal event-path propagation + return true; + }, + + // For cross-browser consistency, suppress native .click() on links + // Also prevent it if we're currently inside a leveraged native-event stack + _default: function( event ) { + var target = event.target; + return rcheckableType.test( target.type ) && + target.click && nodeName( target, "input" ) && + dataPriv.get( target, "click" ) || + nodeName( target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; + } + } + } + } +}; + +// Ensure the presence of an event listener that handles manually-triggered +// synthetic events by interrupting progress until reinvoked in response to +// *native* events that it fires directly, ensuring that state changes have +// already occurred before other listeners are invoked. +function leverageNative( el, type, expectSync ) { + + // Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add + if ( !expectSync ) { + if ( dataPriv.get( el, type ) === undefined ) { + jQuery.event.add( el, type, returnTrue ); + } + return; + } + + // Register the controller as a special universal handler for all event namespaces + dataPriv.set( el, type, false ); + jQuery.event.add( el, type, { + namespace: false, + handler: function( event ) { + var notAsync, result, + saved = dataPriv.get( this, type ); + + if ( ( event.isTrigger & 1 ) && this[ type ] ) { + + // Interrupt processing of the outer synthetic .trigger()ed event + // Saved data should be false in such cases, but might be a leftover capture object + // from an async native handler (gh-4350) + if ( !saved.length ) { + + // Store arguments for use when handling the inner native event + // There will always be at least one argument (an event object), so this array + // will not be confused with a leftover capture object. + saved = slice.call( arguments ); + dataPriv.set( this, type, saved ); + + // Trigger the native event and capture its result + // Support: IE <=9 - 11+ + // focus() and blur() are asynchronous + notAsync = expectSync( this, type ); + this[ type ](); + result = dataPriv.get( this, type ); + if ( saved !== result || notAsync ) { + dataPriv.set( this, type, false ); + } else { + result = {}; + } + if ( saved !== result ) { + + // Cancel the outer synthetic event + event.stopImmediatePropagation(); + event.preventDefault(); + + // Support: Chrome 86+ + // In Chrome, if an element having a focusout handler is blurred by + // clicking outside of it, it invokes the handler synchronously. If + // that handler calls `.remove()` on the element, the data is cleared, + // leaving `result` undefined. We need to guard against this. + return result && result.value; + } + + // If this is an inner synthetic event for an event with a bubbling surrogate + // (focus or blur), assume that the surrogate already propagated from triggering the + // native event and prevent that from happening again here. + // This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the + // bubbling surrogate propagates *after* the non-bubbling base), but that seems + // less bad than duplication. + } else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) { + event.stopPropagation(); + } + + // If this is a native event triggered above, everything is now in order + // Fire an inner synthetic event with the original arguments + } else if ( saved.length ) { + + // ...and capture the result + dataPriv.set( this, type, { + value: jQuery.event.trigger( + + // Support: IE <=9 - 11+ + // Extend with the prototype to reset the above stopImmediatePropagation() + jQuery.extend( saved[ 0 ], jQuery.Event.prototype ), + saved.slice( 1 ), + this + ) + } ); + + // Abort handling of the native event + event.stopImmediatePropagation(); + } + } + } ); +} + +jQuery.removeEvent = function( elem, type, handle ) { + + // This "if" is needed for plain objects + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle ); + } +}; + +jQuery.Event = function( src, props ) { + + // Allow instantiation without the 'new' keyword + if ( !( this instanceof jQuery.Event ) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && + + // Support: Android <=2.3 only + src.returnValue === false ? + returnTrue : + returnFalse; + + // Create target properties + // Support: Safari <=6 - 7 only + // Target should not be a text node (#504, #13143) + this.target = ( src.target && src.target.nodeType === 3 ) ? + src.target.parentNode : + src.target; + + this.currentTarget = src.currentTarget; + this.relatedTarget = src.relatedTarget; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || Date.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + constructor: jQuery.Event, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + isSimulated: false, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + + if ( e && !this.isSimulated ) { + e.preventDefault(); + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopPropagation(); + } + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } +}; + +// Includes all common event props including KeyEvent and MouseEvent specific props +jQuery.each( { + altKey: true, + bubbles: true, + cancelable: true, + changedTouches: true, + ctrlKey: true, + detail: true, + eventPhase: true, + metaKey: true, + pageX: true, + pageY: true, + shiftKey: true, + view: true, + "char": true, + code: true, + charCode: true, + key: true, + keyCode: true, + button: true, + buttons: true, + clientX: true, + clientY: true, + offsetX: true, + offsetY: true, + pointerId: true, + pointerType: true, + screenX: true, + screenY: true, + targetTouches: true, + toElement: true, + touches: true, + which: true +}, jQuery.event.addProp ); + +jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) { + jQuery.event.special[ type ] = { + + // Utilize native event if possible so blur/focus sequence is correct + setup: function() { + + // Claim the first handler + // dataPriv.set( this, "focus", ... ) + // dataPriv.set( this, "blur", ... ) + leverageNative( this, type, expectSync ); + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function() { + + // Force setup before trigger + leverageNative( this, type ); + + // Return non-false to allow normal event-path propagation + return true; + }, + + // Suppress native focus or blur as it's already being fired + // in leverageNative. + _default: function() { + return true; + }, + + delegateType: delegateType + }; +} ); + +// Create mouseenter/leave events using mouseover/out and event-time checks +// so that event delegation works in jQuery. +// Do the same for pointerenter/pointerleave and pointerover/pointerout +// +// Support: Safari 7 only +// Safari sends mouseenter too often; see: +// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 +// for the description of the bug (it existed in older Chrome versions as well). +jQuery.each( { + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mouseenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +} ); + +jQuery.fn.extend( { + + on: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn ); + }, + one: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? + handleObj.origType + "." + handleObj.namespace : + handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each( function() { + jQuery.event.remove( this, types, fn, selector ); + } ); + } +} ); + + +var + + // Support: IE <=10 - 11, Edge 12 - 13 only + // In IE/Edge using regex groups here causes severe slowdowns. + // See https://connect.microsoft.com/IE/feedback/details/1736512/ + rnoInnerhtml = /\s*$/g; + +// Prefer a tbody over its parent table for containing new rows +function manipulationTarget( elem, content ) { + if ( nodeName( elem, "table" ) && + nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { + + return jQuery( elem ).children( "tbody" )[ 0 ] || elem; + } + + return elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) { + elem.type = elem.type.slice( 5 ); + } else { + elem.removeAttribute( "type" ); + } + + return elem; +} + +function cloneCopyEvent( src, dest ) { + var i, l, type, pdataOld, udataOld, udataCur, events; + + if ( dest.nodeType !== 1 ) { + return; + } + + // 1. Copy private data: events, handlers, etc. + if ( dataPriv.hasData( src ) ) { + pdataOld = dataPriv.get( src ); + events = pdataOld.events; + + if ( events ) { + dataPriv.remove( dest, "handle events" ); + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + } + + // 2. Copy user data + if ( dataUser.hasData( src ) ) { + udataOld = dataUser.access( src ); + udataCur = jQuery.extend( {}, udataOld ); + + dataUser.set( dest, udataCur ); + } +} + +// Fix IE bugs, see support tests +function fixInput( src, dest ) { + var nodeName = dest.nodeName.toLowerCase(); + + // Fails to persist the checked state of a cloned checkbox or radio button. + if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + dest.checked = src.checked; + + // Fails to return the selected option to the default selected state when cloning options + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +function domManip( collection, args, callback, ignored ) { + + // Flatten any nested arrays + args = flat( args ); + + var fragment, first, scripts, hasScripts, node, doc, + i = 0, + l = collection.length, + iNoClone = l - 1, + value = args[ 0 ], + valueIsFunction = isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( valueIsFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return collection.each( function( index ) { + var self = collection.eq( index ); + if ( valueIsFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + domManip( self, args, callback, ignored ); + } ); + } + + if ( l ) { + fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + // Require either new content or an interest in ignored elements to invoke the callback + if ( first || ignored ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item + // instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( collection[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !dataPriv.access( node, "globalEval" ) && + jQuery.contains( doc, node ) ) { + + if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) { + + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl && !node.noModule ) { + jQuery._evalUrl( node.src, { + nonce: node.nonce || node.getAttribute( "nonce" ) + }, doc ); + } + } else { + DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc ); + } + } + } + } + } + } + + return collection; +} + +function remove( elem, selector, keepData ) { + var node, + nodes = selector ? jQuery.filter( selector, elem ) : elem, + i = 0; + + for ( ; ( node = nodes[ i ] ) != null; i++ ) { + if ( !keepData && node.nodeType === 1 ) { + jQuery.cleanData( getAll( node ) ); + } + + if ( node.parentNode ) { + if ( keepData && isAttached( node ) ) { + setGlobalEval( getAll( node, "script" ) ); + } + node.parentNode.removeChild( node ); + } + } + + return elem; +} + +jQuery.extend( { + htmlPrefilter: function( html ) { + return html; + }, + + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var i, l, srcElements, destElements, + clone = elem.cloneNode( true ), + inPage = isAttached( elem ); + + // Fix IE cloning issues + if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && + !jQuery.isXMLDoc( elem ) ) { + + // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + fixInput( srcElements[ i ], destElements[ i ] ); + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + cloneCopyEvent( srcElements[ i ], destElements[ i ] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + // Return the cloned set + return clone; + }, + + cleanData: function( elems ) { + var data, elem, type, + special = jQuery.event.special, + i = 0; + + for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { + if ( acceptData( elem ) ) { + if ( ( data = elem[ dataPriv.expando ] ) ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataPriv.expando ] = undefined; + } + if ( elem[ dataUser.expando ] ) { + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataUser.expando ] = undefined; + } + } + } + } +} ); + +jQuery.fn.extend( { + detach: function( selector ) { + return remove( this, selector, true ); + }, + + remove: function( selector ) { + return remove( this, selector ); + }, + + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().each( function() { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.textContent = value; + } + } ); + }, null, value, arguments.length ); + }, + + append: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + } ); + }, + + prepend: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + } ); + }, + + before: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + } ); + }, + + after: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + } ); + }, + + empty: function() { + var elem, + i = 0; + + for ( ; ( elem = this[ i ] ) != null; i++ ) { + if ( elem.nodeType === 1 ) { + + // Prevent memory leaks + jQuery.cleanData( getAll( elem, false ) ); + + // Remove any remaining nodes + elem.textContent = ""; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + } ); + }, + + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined && elem.nodeType === 1 ) { + return elem.innerHTML; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { + + value = jQuery.htmlPrefilter( value ); + + try { + for ( ; i < l; i++ ) { + elem = this[ i ] || {}; + + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch ( e ) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var ignored = []; + + // Make the changes, replacing each non-ignored context element with the new content + return domManip( this, arguments, function( elem ) { + var parent = this.parentNode; + + if ( jQuery.inArray( this, ignored ) < 0 ) { + jQuery.cleanData( getAll( this ) ); + if ( parent ) { + parent.replaceChild( elem, this ); + } + } + + // Force callback invocation + }, ignored ); + } +} ); + +jQuery.each( { + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1, + i = 0; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); + + // Support: Android <=4.0 only, PhantomJS 1 only + // .get() because push.apply(_, arraylike) throws on ancient WebKit + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +} ); +var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); + +var getStyles = function( elem ) { + + // Support: IE <=11 only, Firefox <=30 (#15098, #14150) + // IE throws on elements created in popups + // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" + var view = elem.ownerDocument.defaultView; + + if ( !view || !view.opener ) { + view = window; + } + + return view.getComputedStyle( elem ); + }; + +var swap = function( elem, options, callback ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.call( elem ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; +}; + + +var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" ); + + + +( function() { + + // Executing both pixelPosition & boxSizingReliable tests require only one layout + // so they're executed at the same time to save the second computation. + function computeStyleTests() { + + // This is a singleton, we need to execute it only once + if ( !div ) { + return; + } + + container.style.cssText = "position:absolute;left:-11111px;width:60px;" + + "margin-top:1px;padding:0;border:0"; + div.style.cssText = + "position:relative;display:block;box-sizing:border-box;overflow:scroll;" + + "margin:auto;border:1px;padding:1px;" + + "width:60%;top:1%"; + documentElement.appendChild( container ).appendChild( div ); + + var divStyle = window.getComputedStyle( div ); + pixelPositionVal = divStyle.top !== "1%"; + + // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 + reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12; + + // Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3 + // Some styles come back with percentage values, even though they shouldn't + div.style.right = "60%"; + pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36; + + // Support: IE 9 - 11 only + // Detect misreporting of content dimensions for box-sizing:border-box elements + boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36; + + // Support: IE 9 only + // Detect overflow:scroll screwiness (gh-3699) + // Support: Chrome <=64 + // Don't get tricked when zoom affects offsetWidth (gh-4029) + div.style.position = "absolute"; + scrollboxSizeVal = roundPixelMeasures( div.offsetWidth / 3 ) === 12; + + documentElement.removeChild( container ); + + // Nullify the div so it wouldn't be stored in the memory and + // it will also be a sign that checks already performed + div = null; + } + + function roundPixelMeasures( measure ) { + return Math.round( parseFloat( measure ) ); + } + + var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal, + reliableTrDimensionsVal, reliableMarginLeftVal, + container = document.createElement( "div" ), + div = document.createElement( "div" ); + + // Finish early in limited (non-browser) environments + if ( !div.style ) { + return; + } + + // Support: IE <=9 - 11 only + // Style of cloned element affects source element cloned (#8908) + div.style.backgroundClip = "content-box"; + div.cloneNode( true ).style.backgroundClip = ""; + support.clearCloneStyle = div.style.backgroundClip === "content-box"; + + jQuery.extend( support, { + boxSizingReliable: function() { + computeStyleTests(); + return boxSizingReliableVal; + }, + pixelBoxStyles: function() { + computeStyleTests(); + return pixelBoxStylesVal; + }, + pixelPosition: function() { + computeStyleTests(); + return pixelPositionVal; + }, + reliableMarginLeft: function() { + computeStyleTests(); + return reliableMarginLeftVal; + }, + scrollboxSize: function() { + computeStyleTests(); + return scrollboxSizeVal; + }, + + // Support: IE 9 - 11+, Edge 15 - 18+ + // IE/Edge misreport `getComputedStyle` of table rows with width/height + // set in CSS while `offset*` properties report correct values. + // Behavior in IE 9 is more subtle than in newer versions & it passes + // some versions of this test; make sure not to make it pass there! + // + // Support: Firefox 70+ + // Only Firefox includes border widths + // in computed dimensions. (gh-4529) + reliableTrDimensions: function() { + var table, tr, trChild, trStyle; + if ( reliableTrDimensionsVal == null ) { + table = document.createElement( "table" ); + tr = document.createElement( "tr" ); + trChild = document.createElement( "div" ); + + table.style.cssText = "position:absolute;left:-11111px;border-collapse:separate"; + tr.style.cssText = "border:1px solid"; + + // Support: Chrome 86+ + // Height set through cssText does not get applied. + // Computed height then comes back as 0. + tr.style.height = "1px"; + trChild.style.height = "9px"; + + // Support: Android 8 Chrome 86+ + // In our bodyBackground.html iframe, + // display for all div elements is set to "inline", + // which causes a problem only in Android 8 Chrome 86. + // Ensuring the div is display: block + // gets around this issue. + trChild.style.display = "block"; + + documentElement + .appendChild( table ) + .appendChild( tr ) + .appendChild( trChild ); + + trStyle = window.getComputedStyle( tr ); + reliableTrDimensionsVal = ( parseInt( trStyle.height, 10 ) + + parseInt( trStyle.borderTopWidth, 10 ) + + parseInt( trStyle.borderBottomWidth, 10 ) ) === tr.offsetHeight; + + documentElement.removeChild( table ); + } + return reliableTrDimensionsVal; + } + } ); +} )(); + + +function curCSS( elem, name, computed ) { + var width, minWidth, maxWidth, ret, + + // Support: Firefox 51+ + // Retrieving style before computed somehow + // fixes an issue with getting wrong values + // on detached elements + style = elem.style; + + computed = computed || getStyles( elem ); + + // getPropertyValue is needed for: + // .css('filter') (IE 9 only, #12537) + // .css('--customProperty) (#3144) + if ( computed ) { + ret = computed.getPropertyValue( name ) || computed[ name ]; + + if ( ret === "" && !isAttached( elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Android Browser returns percentage for some values, + // but width seems to be reliably pixels. + // This is against the CSSOM draft spec: + // https://drafts.csswg.org/cssom/#resolved-values + if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) { + + // Remember the original values + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + // Put in the new values to get a computed value out + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + // Revert the changed values + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret !== undefined ? + + // Support: IE <=9 - 11 only + // IE returns zIndex value as an integer. + ret + "" : + ret; +} + + +function addGetHookIf( conditionFn, hookFn ) { + + // Define the hook, we'll check on the first run if it's really needed. + return { + get: function() { + if ( conditionFn() ) { + + // Hook not needed (or it's not possible to use it due + // to missing dependency), remove it. + delete this.get; + return; + } + + // Hook needed; redefine it so that the support test is not executed again. + return ( this.get = hookFn ).apply( this, arguments ); + } + }; +} + + +var cssPrefixes = [ "Webkit", "Moz", "ms" ], + emptyStyle = document.createElement( "div" ).style, + vendorProps = {}; + +// Return a vendor-prefixed property or undefined +function vendorPropName( name ) { + + // Check for vendor prefixed names + var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in emptyStyle ) { + return name; + } + } +} + +// Return a potentially-mapped jQuery.cssProps or vendor prefixed property +function finalPropName( name ) { + var final = jQuery.cssProps[ name ] || vendorProps[ name ]; + + if ( final ) { + return final; + } + if ( name in emptyStyle ) { + return name; + } + return vendorProps[ name ] = vendorPropName( name ) || name; +} + + +var + + // Swappable if display is none or starts with table + // except "table", "table-cell", or "table-caption" + // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rcustomProp = /^--/, + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: "0", + fontWeight: "400" + }; + +function setPositiveNumber( _elem, value, subtract ) { + + // Any relative (+/-) values have already been + // normalized at this point + var matches = rcssNum.exec( value ); + return matches ? + + // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : + value; +} + +function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) { + var i = dimension === "width" ? 1 : 0, + extra = 0, + delta = 0; + + // Adjustment may not be necessary + if ( box === ( isBorderBox ? "border" : "content" ) ) { + return 0; + } + + for ( ; i < 4; i += 2 ) { + + // Both box models exclude margin + if ( box === "margin" ) { + delta += jQuery.css( elem, box + cssExpand[ i ], true, styles ); + } + + // If we get here with a content-box, we're seeking "padding" or "border" or "margin" + if ( !isBorderBox ) { + + // Add padding + delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + + // For "border" or "margin", add border + if ( box !== "padding" ) { + delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + + // But still keep track of it otherwise + } else { + extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + + // If we get here with a border-box (content + padding + border), we're seeking "content" or + // "padding" or "margin" + } else { + + // For "content", subtract padding + if ( box === "content" ) { + delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + } + + // For "content" or "padding", subtract border + if ( box !== "margin" ) { + delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } + } + + // Account for positive content-box scroll gutter when requested by providing computedVal + if ( !isBorderBox && computedVal >= 0 ) { + + // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border + // Assuming integer scroll gutter, subtract the rest and round down + delta += Math.max( 0, Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + computedVal - + delta - + extra - + 0.5 + + // If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter + // Use an explicit zero to avoid NaN (gh-3964) + ) ) || 0; + } + + return delta; +} + +function getWidthOrHeight( elem, dimension, extra ) { + + // Start with computed style + var styles = getStyles( elem ), + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322). + // Fake content-box until we know it's needed to know the true value. + boxSizingNeeded = !support.boxSizingReliable() || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + valueIsBorderBox = isBorderBox, + + val = curCSS( elem, dimension, styles ), + offsetProp = "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ); + + // Support: Firefox <=54 + // Return a confounding non-pixel value or feign ignorance, as appropriate. + if ( rnumnonpx.test( val ) ) { + if ( !extra ) { + return val; + } + val = "auto"; + } + + + // Support: IE 9 - 11 only + // Use offsetWidth/offsetHeight for when box sizing is unreliable. + // In those cases, the computed value can be trusted to be border-box. + if ( ( !support.boxSizingReliable() && isBorderBox || + + // Support: IE 10 - 11+, Edge 15 - 18+ + // IE/Edge misreport `getComputedStyle` of table rows with width/height + // set in CSS while `offset*` properties report correct values. + // Interestingly, in some cases IE 9 doesn't suffer from this issue. + !support.reliableTrDimensions() && nodeName( elem, "tr" ) || + + // Fall back to offsetWidth/offsetHeight when value is "auto" + // This happens for inline elements with no explicit setting (gh-3571) + val === "auto" || + + // Support: Android <=4.1 - 4.3 only + // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602) + !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) && + + // Make sure the element is visible & connected + elem.getClientRects().length ) { + + isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; + + // Where available, offsetWidth/offsetHeight approximate border box dimensions. + // Where not available (e.g., SVG), assume unreliable box-sizing and interpret the + // retrieved value as a content box dimension. + valueIsBorderBox = offsetProp in elem; + if ( valueIsBorderBox ) { + val = elem[ offsetProp ]; + } + } + + // Normalize "" and auto + val = parseFloat( val ) || 0; + + // Adjust for the element's box model + return ( val + + boxModelAdjustment( + elem, + dimension, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox, + styles, + + // Provide the current computed size to request scroll gutter calculation (gh-3589) + val + ) + ) + "px"; +} + +jQuery.extend( { + + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + } + } + } + }, + + // Don't automatically add "px" to these possibly-unitless properties + cssNumber: { + "animationIterationCount": true, + "columnCount": true, + "fillOpacity": true, + "flexGrow": true, + "flexShrink": true, + "fontWeight": true, + "gridArea": true, + "gridColumn": true, + "gridColumnEnd": true, + "gridColumnStart": true, + "gridRow": true, + "gridRowEnd": true, + "gridRowStart": true, + "lineHeight": true, + "opacity": true, + "order": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: {}, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ), + style = elem.style; + + // Make sure that we're working with the right name. We don't + // want to query the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Gets hook for the prefixed version, then unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // Convert "+=" or "-=" to relative numbers (#7345) + if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { + value = adjustCSS( elem, name, ret ); + + // Fixes bug #9237 + type = "number"; + } + + // Make sure that null and NaN values aren't set (#7116) + if ( value == null || value !== value ) { + return; + } + + // If a number was passed in, add the unit (except for certain CSS properties) + // The isCustomProp check can be removed in jQuery 4.0 when we only auto-append + // "px" to a few hardcoded values. + if ( type === "number" && !isCustomProp ) { + value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); + } + + // background-* props affect original clone's values + if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { + style[ name ] = "inherit"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !( "set" in hooks ) || + ( value = hooks.set( elem, value, extra ) ) !== undefined ) { + + if ( isCustomProp ) { + style.setProperty( name, value ); + } else { + style[ name ] = value; + } + } + + } else { + + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && + ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { + + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, extra, styles ) { + var val, num, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ); + + // Make sure that we're working with the right name. We don't + // want to modify the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Try prefixed name followed by the unprefixed name + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name, styles ); + } + + // Convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Make numeric if forced or a qualifier was provided and val looks numeric + if ( extra === "" || extra ) { + num = parseFloat( val ); + return extra === true || isFinite( num ) ? num || 0 : val; + } + + return val; + } +} ); + +jQuery.each( [ "height", "width" ], function( _i, dimension ) { + jQuery.cssHooks[ dimension ] = { + get: function( elem, computed, extra ) { + if ( computed ) { + + // Certain elements can have dimension info if we invisibly show them + // but it must have a current display style that would benefit + return rdisplayswap.test( jQuery.css( elem, "display" ) ) && + + // Support: Safari 8+ + // Table columns in Safari have non-zero offsetWidth & zero + // getBoundingClientRect().width unless display is changed. + // Support: IE <=11 only + // Running getBoundingClientRect on a disconnected node + // in IE throws an error. + ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? + swap( elem, cssShow, function() { + return getWidthOrHeight( elem, dimension, extra ); + } ) : + getWidthOrHeight( elem, dimension, extra ); + } + }, + + set: function( elem, value, extra ) { + var matches, + styles = getStyles( elem ), + + // Only read styles.position if the test has a chance to fail + // to avoid forcing a reflow. + scrollboxSizeBuggy = !support.scrollboxSize() && + styles.position === "absolute", + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991) + boxSizingNeeded = scrollboxSizeBuggy || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + subtract = extra ? + boxModelAdjustment( + elem, + dimension, + extra, + isBorderBox, + styles + ) : + 0; + + // Account for unreliable border-box dimensions by comparing offset* to computed and + // faking a content-box to get border and padding (gh-3699) + if ( isBorderBox && scrollboxSizeBuggy ) { + subtract -= Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + parseFloat( styles[ dimension ] ) - + boxModelAdjustment( elem, dimension, "border", false, styles ) - + 0.5 + ); + } + + // Convert to pixels if value adjustment is needed + if ( subtract && ( matches = rcssNum.exec( value ) ) && + ( matches[ 3 ] || "px" ) !== "px" ) { + + elem.style[ dimension ] = value; + value = jQuery.css( elem, dimension ); + } + + return setPositiveNumber( elem, value, subtract ); + } + }; +} ); + +jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, + function( elem, computed ) { + if ( computed ) { + return ( parseFloat( curCSS( elem, "marginLeft" ) ) || + elem.getBoundingClientRect().left - + swap( elem, { marginLeft: 0 }, function() { + return elem.getBoundingClientRect().left; + } ) + ) + "px"; + } + } +); + +// These hooks are used by animate to expand properties +jQuery.each( { + margin: "", + padding: "", + border: "Width" +}, function( prefix, suffix ) { + jQuery.cssHooks[ prefix + suffix ] = { + expand: function( value ) { + var i = 0, + expanded = {}, + + // Assumes a single number if not a string + parts = typeof value === "string" ? value.split( " " ) : [ value ]; + + for ( ; i < 4; i++ ) { + expanded[ prefix + cssExpand[ i ] + suffix ] = + parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; + } + + return expanded; + } + }; + + if ( prefix !== "margin" ) { + jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; + } +} ); + +jQuery.fn.extend( { + css: function( name, value ) { + return access( this, function( elem, name, value ) { + var styles, len, + map = {}, + i = 0; + + if ( Array.isArray( name ) ) { + styles = getStyles( elem ); + len = name.length; + + for ( ; i < len; i++ ) { + map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); + } + + return map; + } + + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + } +} ); + + +function Tween( elem, options, prop, end, easing ) { + return new Tween.prototype.init( elem, options, prop, end, easing ); +} +jQuery.Tween = Tween; + +Tween.prototype = { + constructor: Tween, + init: function( elem, options, prop, end, easing, unit ) { + this.elem = elem; + this.prop = prop; + this.easing = easing || jQuery.easing._default; + this.options = options; + this.start = this.now = this.cur(); + this.end = end; + this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); + }, + cur: function() { + var hooks = Tween.propHooks[ this.prop ]; + + return hooks && hooks.get ? + hooks.get( this ) : + Tween.propHooks._default.get( this ); + }, + run: function( percent ) { + var eased, + hooks = Tween.propHooks[ this.prop ]; + + if ( this.options.duration ) { + this.pos = eased = jQuery.easing[ this.easing ]( + percent, this.options.duration * percent, 0, 1, this.options.duration + ); + } else { + this.pos = eased = percent; + } + this.now = ( this.end - this.start ) * eased + this.start; + + if ( this.options.step ) { + this.options.step.call( this.elem, this.now, this ); + } + + if ( hooks && hooks.set ) { + hooks.set( this ); + } else { + Tween.propHooks._default.set( this ); + } + return this; + } +}; + +Tween.prototype.init.prototype = Tween.prototype; + +Tween.propHooks = { + _default: { + get: function( tween ) { + var result; + + // Use a property on the element directly when it is not a DOM element, + // or when there is no matching style property that exists. + if ( tween.elem.nodeType !== 1 || + tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { + return tween.elem[ tween.prop ]; + } + + // Passing an empty string as a 3rd parameter to .css will automatically + // attempt a parseFloat and fallback to a string if the parse fails. + // Simple values such as "10px" are parsed to Float; + // complex values such as "rotate(1rad)" are returned as-is. + result = jQuery.css( tween.elem, tween.prop, "" ); + + // Empty strings, null, undefined and "auto" are converted to 0. + return !result || result === "auto" ? 0 : result; + }, + set: function( tween ) { + + // Use step hook for back compat. + // Use cssHook if its there. + // Use .style if available and use plain properties where available. + if ( jQuery.fx.step[ tween.prop ] ) { + jQuery.fx.step[ tween.prop ]( tween ); + } else if ( tween.elem.nodeType === 1 && ( + jQuery.cssHooks[ tween.prop ] || + tween.elem.style[ finalPropName( tween.prop ) ] != null ) ) { + jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); + } else { + tween.elem[ tween.prop ] = tween.now; + } + } + } +}; + +// Support: IE <=9 only +// Panic based approach to setting things on disconnected nodes +Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { + set: function( tween ) { + if ( tween.elem.nodeType && tween.elem.parentNode ) { + tween.elem[ tween.prop ] = tween.now; + } + } +}; + +jQuery.easing = { + linear: function( p ) { + return p; + }, + swing: function( p ) { + return 0.5 - Math.cos( p * Math.PI ) / 2; + }, + _default: "swing" +}; + +jQuery.fx = Tween.prototype.init; + +// Back compat <1.8 extension point +jQuery.fx.step = {}; + + + + +var + fxNow, inProgress, + rfxtypes = /^(?:toggle|show|hide)$/, + rrun = /queueHooks$/; + +function schedule() { + if ( inProgress ) { + if ( document.hidden === false && window.requestAnimationFrame ) { + window.requestAnimationFrame( schedule ); + } else { + window.setTimeout( schedule, jQuery.fx.interval ); + } + + jQuery.fx.tick(); + } +} + +// Animations created synchronously will run synchronously +function createFxNow() { + window.setTimeout( function() { + fxNow = undefined; + } ); + return ( fxNow = Date.now() ); +} + +// Generate parameters to create a standard animation +function genFx( type, includeWidth ) { + var which, + i = 0, + attrs = { height: type }; + + // If we include width, step value is 1 to do all cssExpand values, + // otherwise step value is 2 to skip over Left and Right + includeWidth = includeWidth ? 1 : 0; + for ( ; i < 4; i += 2 - includeWidth ) { + which = cssExpand[ i ]; + attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; + } + + if ( includeWidth ) { + attrs.opacity = attrs.width = type; + } + + return attrs; +} + +function createTween( value, prop, animation ) { + var tween, + collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), + index = 0, + length = collection.length; + for ( ; index < length; index++ ) { + if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { + + // We're done with this property + return tween; + } + } +} + +function defaultPrefilter( elem, props, opts ) { + var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, + isBox = "width" in props || "height" in props, + anim = this, + orig = {}, + style = elem.style, + hidden = elem.nodeType && isHiddenWithinTree( elem ), + dataShow = dataPriv.get( elem, "fxshow" ); + + // Queue-skipping animations hijack the fx hooks + if ( !opts.queue ) { + hooks = jQuery._queueHooks( elem, "fx" ); + if ( hooks.unqueued == null ) { + hooks.unqueued = 0; + oldfire = hooks.empty.fire; + hooks.empty.fire = function() { + if ( !hooks.unqueued ) { + oldfire(); + } + }; + } + hooks.unqueued++; + + anim.always( function() { + + // Ensure the complete handler is called before this completes + anim.always( function() { + hooks.unqueued--; + if ( !jQuery.queue( elem, "fx" ).length ) { + hooks.empty.fire(); + } + } ); + } ); + } + + // Detect show/hide animations + for ( prop in props ) { + value = props[ prop ]; + if ( rfxtypes.test( value ) ) { + delete props[ prop ]; + toggle = toggle || value === "toggle"; + if ( value === ( hidden ? "hide" : "show" ) ) { + + // Pretend to be hidden if this is a "show" and + // there is still data from a stopped show/hide + if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { + hidden = true; + + // Ignore all other no-op show/hide data + } else { + continue; + } + } + orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); + } + } + + // Bail out if this is a no-op like .hide().hide() + propTween = !jQuery.isEmptyObject( props ); + if ( !propTween && jQuery.isEmptyObject( orig ) ) { + return; + } + + // Restrict "overflow" and "display" styles during box animations + if ( isBox && elem.nodeType === 1 ) { + + // Support: IE <=9 - 11, Edge 12 - 15 + // Record all 3 overflow attributes because IE does not infer the shorthand + // from identically-valued overflowX and overflowY and Edge just mirrors + // the overflowX value there. + opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; + + // Identify a display type, preferring old show/hide data over the CSS cascade + restoreDisplay = dataShow && dataShow.display; + if ( restoreDisplay == null ) { + restoreDisplay = dataPriv.get( elem, "display" ); + } + display = jQuery.css( elem, "display" ); + if ( display === "none" ) { + if ( restoreDisplay ) { + display = restoreDisplay; + } else { + + // Get nonempty value(s) by temporarily forcing visibility + showHide( [ elem ], true ); + restoreDisplay = elem.style.display || restoreDisplay; + display = jQuery.css( elem, "display" ); + showHide( [ elem ] ); + } + } + + // Animate inline elements as inline-block + if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { + if ( jQuery.css( elem, "float" ) === "none" ) { + + // Restore the original display value at the end of pure show/hide animations + if ( !propTween ) { + anim.done( function() { + style.display = restoreDisplay; + } ); + if ( restoreDisplay == null ) { + display = style.display; + restoreDisplay = display === "none" ? "" : display; + } + } + style.display = "inline-block"; + } + } + } + + if ( opts.overflow ) { + style.overflow = "hidden"; + anim.always( function() { + style.overflow = opts.overflow[ 0 ]; + style.overflowX = opts.overflow[ 1 ]; + style.overflowY = opts.overflow[ 2 ]; + } ); + } + + // Implement show/hide animations + propTween = false; + for ( prop in orig ) { + + // General show/hide setup for this element animation + if ( !propTween ) { + if ( dataShow ) { + if ( "hidden" in dataShow ) { + hidden = dataShow.hidden; + } + } else { + dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); + } + + // Store hidden/visible for toggle so `.stop().toggle()` "reverses" + if ( toggle ) { + dataShow.hidden = !hidden; + } + + // Show elements before animating them + if ( hidden ) { + showHide( [ elem ], true ); + } + + /* eslint-disable no-loop-func */ + + anim.done( function() { + + /* eslint-enable no-loop-func */ + + // The final step of a "hide" animation is actually hiding the element + if ( !hidden ) { + showHide( [ elem ] ); + } + dataPriv.remove( elem, "fxshow" ); + for ( prop in orig ) { + jQuery.style( elem, prop, orig[ prop ] ); + } + } ); + } + + // Per-property setup + propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); + if ( !( prop in dataShow ) ) { + dataShow[ prop ] = propTween.start; + if ( hidden ) { + propTween.end = propTween.start; + propTween.start = 0; + } + } + } +} + +function propFilter( props, specialEasing ) { + var index, name, easing, value, hooks; + + // camelCase, specialEasing and expand cssHook pass + for ( index in props ) { + name = camelCase( index ); + easing = specialEasing[ name ]; + value = props[ index ]; + if ( Array.isArray( value ) ) { + easing = value[ 1 ]; + value = props[ index ] = value[ 0 ]; + } + + if ( index !== name ) { + props[ name ] = value; + delete props[ index ]; + } + + hooks = jQuery.cssHooks[ name ]; + if ( hooks && "expand" in hooks ) { + value = hooks.expand( value ); + delete props[ name ]; + + // Not quite $.extend, this won't overwrite existing keys. + // Reusing 'index' because we have the correct "name" + for ( index in value ) { + if ( !( index in props ) ) { + props[ index ] = value[ index ]; + specialEasing[ index ] = easing; + } + } + } else { + specialEasing[ name ] = easing; + } + } +} + +function Animation( elem, properties, options ) { + var result, + stopped, + index = 0, + length = Animation.prefilters.length, + deferred = jQuery.Deferred().always( function() { + + // Don't match elem in the :animated selector + delete tick.elem; + } ), + tick = function() { + if ( stopped ) { + return false; + } + var currentTime = fxNow || createFxNow(), + remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), + + // Support: Android 2.3 only + // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497) + temp = remaining / animation.duration || 0, + percent = 1 - temp, + index = 0, + length = animation.tweens.length; + + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( percent ); + } + + deferred.notifyWith( elem, [ animation, percent, remaining ] ); + + // If there's more to do, yield + if ( percent < 1 && length ) { + return remaining; + } + + // If this was an empty animation, synthesize a final progress notification + if ( !length ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + } + + // Resolve the animation and report its conclusion + deferred.resolveWith( elem, [ animation ] ); + return false; + }, + animation = deferred.promise( { + elem: elem, + props: jQuery.extend( {}, properties ), + opts: jQuery.extend( true, { + specialEasing: {}, + easing: jQuery.easing._default + }, options ), + originalProperties: properties, + originalOptions: options, + startTime: fxNow || createFxNow(), + duration: options.duration, + tweens: [], + createTween: function( prop, end ) { + var tween = jQuery.Tween( elem, animation.opts, prop, end, + animation.opts.specialEasing[ prop ] || animation.opts.easing ); + animation.tweens.push( tween ); + return tween; + }, + stop: function( gotoEnd ) { + var index = 0, + + // If we are going to the end, we want to run all the tweens + // otherwise we skip this part + length = gotoEnd ? animation.tweens.length : 0; + if ( stopped ) { + return this; + } + stopped = true; + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( 1 ); + } + + // Resolve when we played the last frame; otherwise, reject + if ( gotoEnd ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + deferred.resolveWith( elem, [ animation, gotoEnd ] ); + } else { + deferred.rejectWith( elem, [ animation, gotoEnd ] ); + } + return this; + } + } ), + props = animation.props; + + propFilter( props, animation.opts.specialEasing ); + + for ( ; index < length; index++ ) { + result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); + if ( result ) { + if ( isFunction( result.stop ) ) { + jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = + result.stop.bind( result ); + } + return result; + } + } + + jQuery.map( props, createTween, animation ); + + if ( isFunction( animation.opts.start ) ) { + animation.opts.start.call( elem, animation ); + } + + // Attach callbacks from options + animation + .progress( animation.opts.progress ) + .done( animation.opts.done, animation.opts.complete ) + .fail( animation.opts.fail ) + .always( animation.opts.always ); + + jQuery.fx.timer( + jQuery.extend( tick, { + elem: elem, + anim: animation, + queue: animation.opts.queue + } ) + ); + + return animation; +} + +jQuery.Animation = jQuery.extend( Animation, { + + tweeners: { + "*": [ function( prop, value ) { + var tween = this.createTween( prop, value ); + adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); + return tween; + } ] + }, + + tweener: function( props, callback ) { + if ( isFunction( props ) ) { + callback = props; + props = [ "*" ]; + } else { + props = props.match( rnothtmlwhite ); + } + + var prop, + index = 0, + length = props.length; + + for ( ; index < length; index++ ) { + prop = props[ index ]; + Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; + Animation.tweeners[ prop ].unshift( callback ); + } + }, + + prefilters: [ defaultPrefilter ], + + prefilter: function( callback, prepend ) { + if ( prepend ) { + Animation.prefilters.unshift( callback ); + } else { + Animation.prefilters.push( callback ); + } + } +} ); + +jQuery.speed = function( speed, easing, fn ) { + var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { + complete: fn || !fn && easing || + isFunction( speed ) && speed, + duration: speed, + easing: fn && easing || easing && !isFunction( easing ) && easing + }; + + // Go to the end state if fx are off + if ( jQuery.fx.off ) { + opt.duration = 0; + + } else { + if ( typeof opt.duration !== "number" ) { + if ( opt.duration in jQuery.fx.speeds ) { + opt.duration = jQuery.fx.speeds[ opt.duration ]; + + } else { + opt.duration = jQuery.fx.speeds._default; + } + } + } + + // Normalize opt.queue - true/undefined/null -> "fx" + if ( opt.queue == null || opt.queue === true ) { + opt.queue = "fx"; + } + + // Queueing + opt.old = opt.complete; + + opt.complete = function() { + if ( isFunction( opt.old ) ) { + opt.old.call( this ); + } + + if ( opt.queue ) { + jQuery.dequeue( this, opt.queue ); + } + }; + + return opt; +}; + +jQuery.fn.extend( { + fadeTo: function( speed, to, easing, callback ) { + + // Show any hidden elements after setting opacity to 0 + return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() + + // Animate to the value specified + .end().animate( { opacity: to }, speed, easing, callback ); + }, + animate: function( prop, speed, easing, callback ) { + var empty = jQuery.isEmptyObject( prop ), + optall = jQuery.speed( speed, easing, callback ), + doAnimation = function() { + + // Operate on a copy of prop so per-property easing won't be lost + var anim = Animation( this, jQuery.extend( {}, prop ), optall ); + + // Empty animations, or finishing resolves immediately + if ( empty || dataPriv.get( this, "finish" ) ) { + anim.stop( true ); + } + }; + + doAnimation.finish = doAnimation; + + return empty || optall.queue === false ? + this.each( doAnimation ) : + this.queue( optall.queue, doAnimation ); + }, + stop: function( type, clearQueue, gotoEnd ) { + var stopQueue = function( hooks ) { + var stop = hooks.stop; + delete hooks.stop; + stop( gotoEnd ); + }; + + if ( typeof type !== "string" ) { + gotoEnd = clearQueue; + clearQueue = type; + type = undefined; + } + if ( clearQueue ) { + this.queue( type || "fx", [] ); + } + + return this.each( function() { + var dequeue = true, + index = type != null && type + "queueHooks", + timers = jQuery.timers, + data = dataPriv.get( this ); + + if ( index ) { + if ( data[ index ] && data[ index ].stop ) { + stopQueue( data[ index ] ); + } + } else { + for ( index in data ) { + if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { + stopQueue( data[ index ] ); + } + } + } + + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && + ( type == null || timers[ index ].queue === type ) ) { + + timers[ index ].anim.stop( gotoEnd ); + dequeue = false; + timers.splice( index, 1 ); + } + } + + // Start the next in the queue if the last step wasn't forced. + // Timers currently will call their complete callbacks, which + // will dequeue but only if they were gotoEnd. + if ( dequeue || !gotoEnd ) { + jQuery.dequeue( this, type ); + } + } ); + }, + finish: function( type ) { + if ( type !== false ) { + type = type || "fx"; + } + return this.each( function() { + var index, + data = dataPriv.get( this ), + queue = data[ type + "queue" ], + hooks = data[ type + "queueHooks" ], + timers = jQuery.timers, + length = queue ? queue.length : 0; + + // Enable finishing flag on private data + data.finish = true; + + // Empty the queue first + jQuery.queue( this, type, [] ); + + if ( hooks && hooks.stop ) { + hooks.stop.call( this, true ); + } + + // Look for any active animations, and finish them + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && timers[ index ].queue === type ) { + timers[ index ].anim.stop( true ); + timers.splice( index, 1 ); + } + } + + // Look for any animations in the old queue and finish them + for ( index = 0; index < length; index++ ) { + if ( queue[ index ] && queue[ index ].finish ) { + queue[ index ].finish.call( this ); + } + } + + // Turn off finishing flag + delete data.finish; + } ); + } +} ); + +jQuery.each( [ "toggle", "show", "hide" ], function( _i, name ) { + var cssFn = jQuery.fn[ name ]; + jQuery.fn[ name ] = function( speed, easing, callback ) { + return speed == null || typeof speed === "boolean" ? + cssFn.apply( this, arguments ) : + this.animate( genFx( name, true ), speed, easing, callback ); + }; +} ); + +// Generate shortcuts for custom animations +jQuery.each( { + slideDown: genFx( "show" ), + slideUp: genFx( "hide" ), + slideToggle: genFx( "toggle" ), + fadeIn: { opacity: "show" }, + fadeOut: { opacity: "hide" }, + fadeToggle: { opacity: "toggle" } +}, function( name, props ) { + jQuery.fn[ name ] = function( speed, easing, callback ) { + return this.animate( props, speed, easing, callback ); + }; +} ); + +jQuery.timers = []; +jQuery.fx.tick = function() { + var timer, + i = 0, + timers = jQuery.timers; + + fxNow = Date.now(); + + for ( ; i < timers.length; i++ ) { + timer = timers[ i ]; + + // Run the timer and safely remove it when done (allowing for external removal) + if ( !timer() && timers[ i ] === timer ) { + timers.splice( i--, 1 ); + } + } + + if ( !timers.length ) { + jQuery.fx.stop(); + } + fxNow = undefined; +}; + +jQuery.fx.timer = function( timer ) { + jQuery.timers.push( timer ); + jQuery.fx.start(); +}; + +jQuery.fx.interval = 13; +jQuery.fx.start = function() { + if ( inProgress ) { + return; + } + + inProgress = true; + schedule(); +}; + +jQuery.fx.stop = function() { + inProgress = null; +}; + +jQuery.fx.speeds = { + slow: 600, + fast: 200, + + // Default speed + _default: 400 +}; + + +// Based off of the plugin by Clint Helfers, with permission. +// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/ +jQuery.fn.delay = function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = window.setTimeout( next, time ); + hooks.stop = function() { + window.clearTimeout( timeout ); + }; + } ); +}; + + +( function() { + var input = document.createElement( "input" ), + select = document.createElement( "select" ), + opt = select.appendChild( document.createElement( "option" ) ); + + input.type = "checkbox"; + + // Support: Android <=4.3 only + // Default value for a checkbox should be "on" + support.checkOn = input.value !== ""; + + // Support: IE <=11 only + // Must access selectedIndex to make default options select + support.optSelected = opt.selected; + + // Support: IE <=11 only + // An input loses its value after becoming a radio + input = document.createElement( "input" ); + input.value = "t"; + input.type = "radio"; + support.radioValue = input.value === "t"; +} )(); + + +var boolHook, + attrHandle = jQuery.expr.attrHandle; + +jQuery.fn.extend( { + attr: function( name, value ) { + return access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each( function() { + jQuery.removeAttr( this, name ); + } ); + } +} ); + +jQuery.extend( { + attr: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set attributes on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + // Attribute hooks are determined by the lowercase version + // Grab necessary hook if one is defined + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + hooks = jQuery.attrHooks[ name.toLowerCase() ] || + ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); + } + + if ( value !== undefined ) { + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return; + } + + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + elem.setAttribute( name, value + "" ); + return value; + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + ret = jQuery.find.attr( elem, name ); + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? undefined : ret; + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !support.radioValue && value === "radio" && + nodeName( elem, "input" ) ) { + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + }, + + removeAttr: function( elem, value ) { + var name, + i = 0, + + // Attribute names can contain non-HTML whitespace characters + // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 + attrNames = value && value.match( rnothtmlwhite ); + + if ( attrNames && elem.nodeType === 1 ) { + while ( ( name = attrNames[ i++ ] ) ) { + elem.removeAttribute( name ); + } + } + } +} ); + +// Hooks for boolean attributes +boolHook = { + set: function( elem, value, name ) { + if ( value === false ) { + + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + elem.setAttribute( name, name ); + } + return name; + } +}; + +jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( _i, name ) { + var getter = attrHandle[ name ] || jQuery.find.attr; + + attrHandle[ name ] = function( elem, name, isXML ) { + var ret, handle, + lowercaseName = name.toLowerCase(); + + if ( !isXML ) { + + // Avoid an infinite loop by temporarily removing this function from the getter + handle = attrHandle[ lowercaseName ]; + attrHandle[ lowercaseName ] = ret; + ret = getter( elem, name, isXML ) != null ? + lowercaseName : + null; + attrHandle[ lowercaseName ] = handle; + } + return ret; + }; +} ); + + + + +var rfocusable = /^(?:input|select|textarea|button)$/i, + rclickable = /^(?:a|area)$/i; + +jQuery.fn.extend( { + prop: function( name, value ) { + return access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + return this.each( function() { + delete this[ jQuery.propFix[ name ] || name ]; + } ); + } +} ); + +jQuery.extend( { + prop: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set properties on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + return ( elem[ name ] = value ); + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + return elem[ name ]; + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + + // Support: IE <=9 - 11 only + // elem.tabIndex doesn't always return the + // correct value when it hasn't been explicitly set + // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + // Use proper attribute retrieval(#12072) + var tabindex = jQuery.find.attr( elem, "tabindex" ); + + if ( tabindex ) { + return parseInt( tabindex, 10 ); + } + + if ( + rfocusable.test( elem.nodeName ) || + rclickable.test( elem.nodeName ) && + elem.href + ) { + return 0; + } + + return -1; + } + } + }, + + propFix: { + "for": "htmlFor", + "class": "className" + } +} ); + +// Support: IE <=11 only +// Accessing the selectedIndex property +// forces the browser to respect setting selected +// on the option +// The getter ensures a default option is selected +// when in an optgroup +// eslint rule "no-unused-expressions" is disabled for this code +// since it considers such accessions noop +if ( !support.optSelected ) { + jQuery.propHooks.selected = { + get: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent && parent.parentNode ) { + parent.parentNode.selectedIndex; + } + return null; + }, + set: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent ) { + parent.selectedIndex; + + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + } + }; +} + +jQuery.each( [ + "tabIndex", + "readOnly", + "maxLength", + "cellSpacing", + "cellPadding", + "rowSpan", + "colSpan", + "useMap", + "frameBorder", + "contentEditable" +], function() { + jQuery.propFix[ this.toLowerCase() ] = this; +} ); + + + + + // Strip and collapse whitespace according to HTML spec + // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace + function stripAndCollapse( value ) { + var tokens = value.match( rnothtmlwhite ) || []; + return tokens.join( " " ); + } + + +function getClass( elem ) { + return elem.getAttribute && elem.getAttribute( "class" ) || ""; +} + +function classesToArray( value ) { + if ( Array.isArray( value ) ) { + return value; + } + if ( typeof value === "string" ) { + return value.match( rnothtmlwhite ) || []; + } + return []; +} + +jQuery.fn.extend( { + addClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + if ( cur.indexOf( " " + clazz + " " ) < 0 ) { + cur += clazz + " "; + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + if ( !arguments.length ) { + return this.attr( "class", "" ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + + // This expression is here for better compressibility (see addClass) + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) > -1 ) { + cur = cur.replace( " " + clazz + " ", " " ); + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isValidValue = type === "string" || Array.isArray( value ); + + if ( typeof stateVal === "boolean" && isValidValue ) { + return stateVal ? this.addClass( value ) : this.removeClass( value ); + } + + if ( isFunction( value ) ) { + return this.each( function( i ) { + jQuery( this ).toggleClass( + value.call( this, i, getClass( this ), stateVal ), + stateVal + ); + } ); + } + + return this.each( function() { + var className, i, self, classNames; + + if ( isValidValue ) { + + // Toggle individual class names + i = 0; + self = jQuery( this ); + classNames = classesToArray( value ); + + while ( ( className = classNames[ i++ ] ) ) { + + // Check each className given, space separated list + if ( self.hasClass( className ) ) { + self.removeClass( className ); + } else { + self.addClass( className ); + } + } + + // Toggle whole class name + } else if ( value === undefined || type === "boolean" ) { + className = getClass( this ); + if ( className ) { + + // Store className if set + dataPriv.set( this, "__className__", className ); + } + + // If the element has a class name or if we're passed `false`, + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + if ( this.setAttribute ) { + this.setAttribute( "class", + className || value === false ? + "" : + dataPriv.get( this, "__className__" ) || "" + ); + } + } + } ); + }, + + hasClass: function( selector ) { + var className, elem, + i = 0; + + className = " " + selector + " "; + while ( ( elem = this[ i++ ] ) ) { + if ( elem.nodeType === 1 && + ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { + return true; + } + } + + return false; + } +} ); + + + + +var rreturn = /\r/g; + +jQuery.fn.extend( { + val: function( value ) { + var hooks, ret, valueIsFunction, + elem = this[ 0 ]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || + jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && + "get" in hooks && + ( ret = hooks.get( elem, "value" ) ) !== undefined + ) { + return ret; + } + + ret = elem.value; + + // Handle most common string cases + if ( typeof ret === "string" ) { + return ret.replace( rreturn, "" ); + } + + // Handle cases where value is null/undef or number + return ret == null ? "" : ret; + } + + return; + } + + valueIsFunction = isFunction( value ); + + return this.each( function( i ) { + var val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( valueIsFunction ) { + val = value.call( this, i, jQuery( this ).val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + + } else if ( typeof val === "number" ) { + val += ""; + + } else if ( Array.isArray( val ) ) { + val = jQuery.map( val, function( value ) { + return value == null ? "" : value + ""; + } ); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + } ); + } +} ); + +jQuery.extend( { + valHooks: { + option: { + get: function( elem ) { + + var val = jQuery.find.attr( elem, "value" ); + return val != null ? + val : + + // Support: IE <=10 - 11 only + // option.text throws exceptions (#14686, #14858) + // Strip and collapse whitespace + // https://html.spec.whatwg.org/#strip-and-collapse-whitespace + stripAndCollapse( jQuery.text( elem ) ); + } + }, + select: { + get: function( elem ) { + var value, option, i, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one", + values = one ? null : [], + max = one ? index + 1 : options.length; + + if ( index < 0 ) { + i = max; + + } else { + i = one ? index : 0; + } + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // Support: IE <=9 only + // IE8-9 doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + + // Don't return options that are disabled or in a disabled optgroup + !option.disabled && + ( !option.parentNode.disabled || + !nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var optionSet, option, + options = elem.options, + values = jQuery.makeArray( value ), + i = options.length; + + while ( i-- ) { + option = options[ i ]; + + /* eslint-disable no-cond-assign */ + + if ( option.selected = + jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 + ) { + optionSet = true; + } + + /* eslint-enable no-cond-assign */ + } + + // Force browsers to behave consistently when non-matching value is set + if ( !optionSet ) { + elem.selectedIndex = -1; + } + return values; + } + } + } +} ); + +// Radios and checkboxes getter/setter +jQuery.each( [ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + set: function( elem, value ) { + if ( Array.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); + } + } + }; + if ( !support.checkOn ) { + jQuery.valHooks[ this ].get = function( elem ) { + return elem.getAttribute( "value" ) === null ? "on" : elem.value; + }; + } +} ); + + + + +// Return jQuery for attributes-only inclusion + + +support.focusin = "onfocusin" in window; + + +var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + stopPropagationCallback = function( e ) { + e.stopPropagation(); + }; + +jQuery.extend( jQuery.event, { + + trigger: function( event, data, elem, onlyHandlers ) { + + var i, cur, tmp, bubbleType, ontype, handle, special, lastElement, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; + + cur = lastElement = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "." ) > -1 ) { + + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split( "." ); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf( ":" ) < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join( "." ); + event.rnamespace = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === ( elem.ownerDocument || document ) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { + lastElement = cur; + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( dataPriv.get( cur, "events" ) || Object.create( null ) )[ event.type ] && + dataPriv.get( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && acceptData( cur ) ) { + event.result = handle.apply( cur, data ); + if ( event.result === false ) { + event.preventDefault(); + } + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( ( !special._default || + special._default.apply( eventPath.pop(), data ) === false ) && + acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name as the event. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + + if ( event.isPropagationStopped() ) { + lastElement.addEventListener( type, stopPropagationCallback ); + } + + elem[ type ](); + + if ( event.isPropagationStopped() ) { + lastElement.removeEventListener( type, stopPropagationCallback ); + } + + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + // Piggyback on a donor event to simulate a different one + // Used only for `focus(in | out)` events + simulate: function( type, elem, event ) { + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true + } + ); + + jQuery.event.trigger( e, null, elem ); + } + +} ); + +jQuery.fn.extend( { + + trigger: function( type, data ) { + return this.each( function() { + jQuery.event.trigger( type, data, this ); + } ); + }, + triggerHandler: function( type, data ) { + var elem = this[ 0 ]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +} ); + + +// Support: Firefox <=44 +// Firefox doesn't have focus(in | out) events +// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 +// +// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 +// focus(in | out) events fire after focus & blur events, +// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order +// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 +if ( !support.focusin ) { + jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler on the document while someone wants focusin/focusout + var handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + + // Handle: regular nodes (via `this.ownerDocument`), window + // (via `this.document`) & document (via `this`). + var doc = this.ownerDocument || this.document || this, + attaches = dataPriv.access( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + dataPriv.access( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this.document || this, + attaches = dataPriv.access( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + dataPriv.remove( doc, fix ); + + } else { + dataPriv.access( doc, fix, attaches ); + } + } + }; + } ); +} +var location = window.location; + +var nonce = { guid: Date.now() }; + +var rquery = ( /\?/ ); + + + +// Cross-browser xml parsing +jQuery.parseXML = function( data ) { + var xml, parserErrorElem; + if ( !data || typeof data !== "string" ) { + return null; + } + + // Support: IE 9 - 11 only + // IE throws on parseFromString with invalid input. + try { + xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); + } catch ( e ) {} + + parserErrorElem = xml && xml.getElementsByTagName( "parsererror" )[ 0 ]; + if ( !xml || parserErrorElem ) { + jQuery.error( "Invalid XML: " + ( + parserErrorElem ? + jQuery.map( parserErrorElem.childNodes, function( el ) { + return el.textContent; + } ).join( "\n" ) : + data + ) ); + } + return xml; +}; + + +var + rbracket = /\[\]$/, + rCRLF = /\r?\n/g, + rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, + rsubmittable = /^(?:input|select|textarea|keygen)/i; + +function buildParams( prefix, obj, traditional, add ) { + var name; + + if ( Array.isArray( obj ) ) { + + // Serialize array item. + jQuery.each( obj, function( i, v ) { + if ( traditional || rbracket.test( prefix ) ) { + + // Treat each array item as a scalar. + add( prefix, v ); + + } else { + + // Item is non-scalar (array or object), encode its numeric index. + buildParams( + prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", + v, + traditional, + add + ); + } + } ); + + } else if ( !traditional && toType( obj ) === "object" ) { + + // Serialize object item. + for ( name in obj ) { + buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); + } + + } else { + + // Serialize scalar item. + add( prefix, obj ); + } +} + +// Serialize an array of form elements or a set of +// key/values into a query string +jQuery.param = function( a, traditional ) { + var prefix, + s = [], + add = function( key, valueOrFunction ) { + + // If value is a function, invoke it and use its return value + var value = isFunction( valueOrFunction ) ? + valueOrFunction() : + valueOrFunction; + + s[ s.length ] = encodeURIComponent( key ) + "=" + + encodeURIComponent( value == null ? "" : value ); + }; + + if ( a == null ) { + return ""; + } + + // If an array was passed in, assume that it is an array of form elements. + if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { + + // Serialize the form elements + jQuery.each( a, function() { + add( this.name, this.value ); + } ); + + } else { + + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for ( prefix in a ) { + buildParams( prefix, a[ prefix ], traditional, add ); + } + } + + // Return the resulting serialization + return s.join( "&" ); +}; + +jQuery.fn.extend( { + serialize: function() { + return jQuery.param( this.serializeArray() ); + }, + serializeArray: function() { + return this.map( function() { + + // Can add propHook for "elements" to filter or add form elements + var elements = jQuery.prop( this, "elements" ); + return elements ? jQuery.makeArray( elements ) : this; + } ).filter( function() { + var type = this.type; + + // Use .is( ":disabled" ) so that fieldset[disabled] works + return this.name && !jQuery( this ).is( ":disabled" ) && + rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && + ( this.checked || !rcheckableType.test( type ) ); + } ).map( function( _i, elem ) { + var val = jQuery( this ).val(); + + if ( val == null ) { + return null; + } + + if ( Array.isArray( val ) ) { + return jQuery.map( val, function( val ) { + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ); + } + + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ).get(); + } +} ); + + +var + r20 = /%20/g, + rhash = /#.*$/, + rantiCache = /([?&])_=[^&]*/, + rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, + + // #7653, #8125, #8152: local protocol detection + rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, + rnoContent = /^(?:GET|HEAD)$/, + rprotocol = /^\/\//, + + /* Prefilters + * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) + * 2) These are called: + * - BEFORE asking for a transport + * - AFTER param serialization (s.data is a string if s.processData is true) + * 3) key is the dataType + * 4) the catchall symbol "*" can be used + * 5) execution will start with transport dataType and THEN continue down to "*" if needed + */ + prefilters = {}, + + /* Transports bindings + * 1) key is the dataType + * 2) the catchall symbol "*" can be used + * 3) selection will start with transport dataType and THEN go to "*" if needed + */ + transports = {}, + + // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression + allTypes = "*/".concat( "*" ), + + // Anchor tag for parsing the document origin + originAnchor = document.createElement( "a" ); + +originAnchor.href = location.href; + +// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport +function addToPrefiltersOrTransports( structure ) { + + // dataTypeExpression is optional and defaults to "*" + return function( dataTypeExpression, func ) { + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + var dataType, + i = 0, + dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || []; + + if ( isFunction( func ) ) { + + // For each dataType in the dataTypeExpression + while ( ( dataType = dataTypes[ i++ ] ) ) { + + // Prepend if requested + if ( dataType[ 0 ] === "+" ) { + dataType = dataType.slice( 1 ) || "*"; + ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); + + // Otherwise append + } else { + ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); + } + } + } + }; +} + +// Base inspection function for prefilters and transports +function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { + + var inspected = {}, + seekingTransport = ( structure === transports ); + + function inspect( dataType ) { + var selected; + inspected[ dataType ] = true; + jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { + var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); + if ( typeof dataTypeOrTransport === "string" && + !seekingTransport && !inspected[ dataTypeOrTransport ] ) { + + options.dataTypes.unshift( dataTypeOrTransport ); + inspect( dataTypeOrTransport ); + return false; + } else if ( seekingTransport ) { + return !( selected = dataTypeOrTransport ); + } + } ); + return selected; + } + + return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); +} + +// A special extend for ajax options +// that takes "flat" options (not to be deep extended) +// Fixes #9887 +function ajaxExtend( target, src ) { + var key, deep, + flatOptions = jQuery.ajaxSettings.flatOptions || {}; + + for ( key in src ) { + if ( src[ key ] !== undefined ) { + ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; + } + } + if ( deep ) { + jQuery.extend( true, target, deep ); + } + + return target; +} + +/* Handles responses to an ajax request: + * - finds the right dataType (mediates between content-type and expected dataType) + * - returns the corresponding response + */ +function ajaxHandleResponses( s, jqXHR, responses ) { + + var ct, type, finalDataType, firstDataType, + contents = s.contents, + dataTypes = s.dataTypes; + + // Remove auto dataType and get content-type in the process + while ( dataTypes[ 0 ] === "*" ) { + dataTypes.shift(); + if ( ct === undefined ) { + ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); + } + } + + // Check if we're dealing with a known content-type + if ( ct ) { + for ( type in contents ) { + if ( contents[ type ] && contents[ type ].test( ct ) ) { + dataTypes.unshift( type ); + break; + } + } + } + + // Check to see if we have a response for the expected dataType + if ( dataTypes[ 0 ] in responses ) { + finalDataType = dataTypes[ 0 ]; + } else { + + // Try convertible dataTypes + for ( type in responses ) { + if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { + finalDataType = type; + break; + } + if ( !firstDataType ) { + firstDataType = type; + } + } + + // Or just use first one + finalDataType = finalDataType || firstDataType; + } + + // If we found a dataType + // We add the dataType to the list if needed + // and return the corresponding response + if ( finalDataType ) { + if ( finalDataType !== dataTypes[ 0 ] ) { + dataTypes.unshift( finalDataType ); + } + return responses[ finalDataType ]; + } +} + +/* Chain conversions given the request and the original response + * Also sets the responseXXX fields on the jqXHR instance + */ +function ajaxConvert( s, response, jqXHR, isSuccess ) { + var conv2, current, conv, tmp, prev, + converters = {}, + + // Work with a copy of dataTypes in case we need to modify it for conversion + dataTypes = s.dataTypes.slice(); + + // Create converters map with lowercased keys + if ( dataTypes[ 1 ] ) { + for ( conv in s.converters ) { + converters[ conv.toLowerCase() ] = s.converters[ conv ]; + } + } + + current = dataTypes.shift(); + + // Convert to each sequential dataType + while ( current ) { + + if ( s.responseFields[ current ] ) { + jqXHR[ s.responseFields[ current ] ] = response; + } + + // Apply the dataFilter if provided + if ( !prev && isSuccess && s.dataFilter ) { + response = s.dataFilter( response, s.dataType ); + } + + prev = current; + current = dataTypes.shift(); + + if ( current ) { + + // There's only work to do if current dataType is non-auto + if ( current === "*" ) { + + current = prev; + + // Convert response if prev dataType is non-auto and differs from current + } else if ( prev !== "*" && prev !== current ) { + + // Seek a direct converter + conv = converters[ prev + " " + current ] || converters[ "* " + current ]; + + // If none found, seek a pair + if ( !conv ) { + for ( conv2 in converters ) { + + // If conv2 outputs current + tmp = conv2.split( " " ); + if ( tmp[ 1 ] === current ) { + + // If prev can be converted to accepted input + conv = converters[ prev + " " + tmp[ 0 ] ] || + converters[ "* " + tmp[ 0 ] ]; + if ( conv ) { + + // Condense equivalence converters + if ( conv === true ) { + conv = converters[ conv2 ]; + + // Otherwise, insert the intermediate dataType + } else if ( converters[ conv2 ] !== true ) { + current = tmp[ 0 ]; + dataTypes.unshift( tmp[ 1 ] ); + } + break; + } + } + } + } + + // Apply converter (if not an equivalence) + if ( conv !== true ) { + + // Unless errors are allowed to bubble, catch and return them + if ( conv && s.throws ) { + response = conv( response ); + } else { + try { + response = conv( response ); + } catch ( e ) { + return { + state: "parsererror", + error: conv ? e : "No conversion from " + prev + " to " + current + }; + } + } + } + } + } + } + + return { state: "success", data: response }; +} + +jQuery.extend( { + + // Counter for holding the number of active queries + active: 0, + + // Last-Modified header cache for next request + lastModified: {}, + etag: {}, + + ajaxSettings: { + url: location.href, + type: "GET", + isLocal: rlocalProtocol.test( location.protocol ), + global: true, + processData: true, + async: true, + contentType: "application/x-www-form-urlencoded; charset=UTF-8", + + /* + timeout: 0, + data: null, + dataType: null, + username: null, + password: null, + cache: null, + throws: false, + traditional: false, + headers: {}, + */ + + accepts: { + "*": allTypes, + text: "text/plain", + html: "text/html", + xml: "application/xml, text/xml", + json: "application/json, text/javascript" + }, + + contents: { + xml: /\bxml\b/, + html: /\bhtml/, + json: /\bjson\b/ + }, + + responseFields: { + xml: "responseXML", + text: "responseText", + json: "responseJSON" + }, + + // Data converters + // Keys separate source (or catchall "*") and destination types with a single space + converters: { + + // Convert anything to text + "* text": String, + + // Text to html (true = no transformation) + "text html": true, + + // Evaluate text as a json expression + "text json": JSON.parse, + + // Parse text as xml + "text xml": jQuery.parseXML + }, + + // For options that shouldn't be deep extended: + // you can add your own custom options here if + // and when you create one that shouldn't be + // deep extended (see ajaxExtend) + flatOptions: { + url: true, + context: true + } + }, + + // Creates a full fledged settings object into target + // with both ajaxSettings and settings fields. + // If target is omitted, writes into ajaxSettings. + ajaxSetup: function( target, settings ) { + return settings ? + + // Building a settings object + ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : + + // Extending ajaxSettings + ajaxExtend( jQuery.ajaxSettings, target ); + }, + + ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), + ajaxTransport: addToPrefiltersOrTransports( transports ), + + // Main method + ajax: function( url, options ) { + + // If url is an object, simulate pre-1.5 signature + if ( typeof url === "object" ) { + options = url; + url = undefined; + } + + // Force options to be an object + options = options || {}; + + var transport, + + // URL without anti-cache param + cacheURL, + + // Response headers + responseHeadersString, + responseHeaders, + + // timeout handle + timeoutTimer, + + // Url cleanup var + urlAnchor, + + // Request state (becomes false upon send and true upon completion) + completed, + + // To know if global events are to be dispatched + fireGlobals, + + // Loop variable + i, + + // uncached part of the url + uncached, + + // Create the final options object + s = jQuery.ajaxSetup( {}, options ), + + // Callbacks context + callbackContext = s.context || s, + + // Context for global events is callbackContext if it is a DOM node or jQuery collection + globalEventContext = s.context && + ( callbackContext.nodeType || callbackContext.jquery ) ? + jQuery( callbackContext ) : + jQuery.event, + + // Deferreds + deferred = jQuery.Deferred(), + completeDeferred = jQuery.Callbacks( "once memory" ), + + // Status-dependent callbacks + statusCode = s.statusCode || {}, + + // Headers (they are sent all at once) + requestHeaders = {}, + requestHeadersNames = {}, + + // Default abort message + strAbort = "canceled", + + // Fake xhr + jqXHR = { + readyState: 0, + + // Builds headers hashtable if needed + getResponseHeader: function( key ) { + var match; + if ( completed ) { + if ( !responseHeaders ) { + responseHeaders = {}; + while ( ( match = rheaders.exec( responseHeadersString ) ) ) { + responseHeaders[ match[ 1 ].toLowerCase() + " " ] = + ( responseHeaders[ match[ 1 ].toLowerCase() + " " ] || [] ) + .concat( match[ 2 ] ); + } + } + match = responseHeaders[ key.toLowerCase() + " " ]; + } + return match == null ? null : match.join( ", " ); + }, + + // Raw string + getAllResponseHeaders: function() { + return completed ? responseHeadersString : null; + }, + + // Caches the header + setRequestHeader: function( name, value ) { + if ( completed == null ) { + name = requestHeadersNames[ name.toLowerCase() ] = + requestHeadersNames[ name.toLowerCase() ] || name; + requestHeaders[ name ] = value; + } + return this; + }, + + // Overrides response content-type header + overrideMimeType: function( type ) { + if ( completed == null ) { + s.mimeType = type; + } + return this; + }, + + // Status-dependent callbacks + statusCode: function( map ) { + var code; + if ( map ) { + if ( completed ) { + + // Execute the appropriate callbacks + jqXHR.always( map[ jqXHR.status ] ); + } else { + + // Lazy-add the new callbacks in a way that preserves old ones + for ( code in map ) { + statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; + } + } + } + return this; + }, + + // Cancel the request + abort: function( statusText ) { + var finalText = statusText || strAbort; + if ( transport ) { + transport.abort( finalText ); + } + done( 0, finalText ); + return this; + } + }; + + // Attach deferreds + deferred.promise( jqXHR ); + + // Add protocol if not provided (prefilters might expect it) + // Handle falsy url in the settings object (#10093: consistency with old signature) + // We also use the url parameter if available + s.url = ( ( url || s.url || location.href ) + "" ) + .replace( rprotocol, location.protocol + "//" ); + + // Alias method option to type as per ticket #12004 + s.type = options.method || options.type || s.method || s.type; + + // Extract dataTypes list + s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ]; + + // A cross-domain request is in order when the origin doesn't match the current origin. + if ( s.crossDomain == null ) { + urlAnchor = document.createElement( "a" ); + + // Support: IE <=8 - 11, Edge 12 - 15 + // IE throws exception on accessing the href property if url is malformed, + // e.g. http://example.com:80x/ + try { + urlAnchor.href = s.url; + + // Support: IE <=8 - 11 only + // Anchor's host property isn't correctly set when s.url is relative + urlAnchor.href = urlAnchor.href; + s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== + urlAnchor.protocol + "//" + urlAnchor.host; + } catch ( e ) { + + // If there is an error parsing the URL, assume it is crossDomain, + // it can be rejected by the transport if it is invalid + s.crossDomain = true; + } + } + + // Convert data if not already a string + if ( s.data && s.processData && typeof s.data !== "string" ) { + s.data = jQuery.param( s.data, s.traditional ); + } + + // Apply prefilters + inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); + + // If request was aborted inside a prefilter, stop there + if ( completed ) { + return jqXHR; + } + + // We can fire global events as of now if asked to + // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) + fireGlobals = jQuery.event && s.global; + + // Watch for a new set of requests + if ( fireGlobals && jQuery.active++ === 0 ) { + jQuery.event.trigger( "ajaxStart" ); + } + + // Uppercase the type + s.type = s.type.toUpperCase(); + + // Determine if request has content + s.hasContent = !rnoContent.test( s.type ); + + // Save the URL in case we're toying with the If-Modified-Since + // and/or If-None-Match header later on + // Remove hash to simplify url manipulation + cacheURL = s.url.replace( rhash, "" ); + + // More options handling for requests with no content + if ( !s.hasContent ) { + + // Remember the hash so we can put it back + uncached = s.url.slice( cacheURL.length ); + + // If data is available and should be processed, append data to url + if ( s.data && ( s.processData || typeof s.data === "string" ) ) { + cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; + + // #9682: remove data so that it's not used in an eventual retry + delete s.data; + } + + // Add or update anti-cache param if needed + if ( s.cache === false ) { + cacheURL = cacheURL.replace( rantiCache, "$1" ); + uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce.guid++ ) + + uncached; + } + + // Put hash and anti-cache on the URL that will be requested (gh-1732) + s.url = cacheURL + uncached; + + // Change '%20' to '+' if this is encoded form body content (gh-2658) + } else if ( s.data && s.processData && + ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { + s.data = s.data.replace( r20, "+" ); + } + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + if ( jQuery.lastModified[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); + } + if ( jQuery.etag[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); + } + } + + // Set the correct header, if data is being sent + if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { + jqXHR.setRequestHeader( "Content-Type", s.contentType ); + } + + // Set the Accepts header for the server, depending on the dataType + jqXHR.setRequestHeader( + "Accept", + s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? + s.accepts[ s.dataTypes[ 0 ] ] + + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : + s.accepts[ "*" ] + ); + + // Check for headers option + for ( i in s.headers ) { + jqXHR.setRequestHeader( i, s.headers[ i ] ); + } + + // Allow custom headers/mimetypes and early abort + if ( s.beforeSend && + ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { + + // Abort if not done already and return + return jqXHR.abort(); + } + + // Aborting is no longer a cancellation + strAbort = "abort"; + + // Install callbacks on deferreds + completeDeferred.add( s.complete ); + jqXHR.done( s.success ); + jqXHR.fail( s.error ); + + // Get transport + transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); + + // If no transport, we auto-abort + if ( !transport ) { + done( -1, "No Transport" ); + } else { + jqXHR.readyState = 1; + + // Send global event + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); + } + + // If request was aborted inside ajaxSend, stop there + if ( completed ) { + return jqXHR; + } + + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = window.setTimeout( function() { + jqXHR.abort( "timeout" ); + }, s.timeout ); + } + + try { + completed = false; + transport.send( requestHeaders, done ); + } catch ( e ) { + + // Rethrow post-completion exceptions + if ( completed ) { + throw e; + } + + // Propagate others as results + done( -1, e ); + } + } + + // Callback for when everything is done + function done( status, nativeStatusText, responses, headers ) { + var isSuccess, success, error, response, modified, + statusText = nativeStatusText; + + // Ignore repeat invocations + if ( completed ) { + return; + } + + completed = true; + + // Clear timeout if it exists + if ( timeoutTimer ) { + window.clearTimeout( timeoutTimer ); + } + + // Dereference transport for early garbage collection + // (no matter how long the jqXHR object will be used) + transport = undefined; + + // Cache response headers + responseHeadersString = headers || ""; + + // Set readyState + jqXHR.readyState = status > 0 ? 4 : 0; + + // Determine if successful + isSuccess = status >= 200 && status < 300 || status === 304; + + // Get response data + if ( responses ) { + response = ajaxHandleResponses( s, jqXHR, responses ); + } + + // Use a noop converter for missing script but not if jsonp + if ( !isSuccess && + jQuery.inArray( "script", s.dataTypes ) > -1 && + jQuery.inArray( "json", s.dataTypes ) < 0 ) { + s.converters[ "text script" ] = function() {}; + } + + // Convert no matter what (that way responseXXX fields are always set) + response = ajaxConvert( s, response, jqXHR, isSuccess ); + + // If successful, handle type chaining + if ( isSuccess ) { + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + modified = jqXHR.getResponseHeader( "Last-Modified" ); + if ( modified ) { + jQuery.lastModified[ cacheURL ] = modified; + } + modified = jqXHR.getResponseHeader( "etag" ); + if ( modified ) { + jQuery.etag[ cacheURL ] = modified; + } + } + + // if no content + if ( status === 204 || s.type === "HEAD" ) { + statusText = "nocontent"; + + // if not modified + } else if ( status === 304 ) { + statusText = "notmodified"; + + // If we have data, let's convert it + } else { + statusText = response.state; + success = response.data; + error = response.error; + isSuccess = !error; + } + } else { + + // Extract error from statusText and normalize for non-aborts + error = statusText; + if ( status || !statusText ) { + statusText = "error"; + if ( status < 0 ) { + status = 0; + } + } + } + + // Set data for the fake xhr object + jqXHR.status = status; + jqXHR.statusText = ( nativeStatusText || statusText ) + ""; + + // Success/Error + if ( isSuccess ) { + deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); + } else { + deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); + } + + // Status-dependent callbacks + jqXHR.statusCode( statusCode ); + statusCode = undefined; + + if ( fireGlobals ) { + globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", + [ jqXHR, s, isSuccess ? success : error ] ); + } + + // Complete + completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); + + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); + + // Handle the global AJAX counter + if ( !( --jQuery.active ) ) { + jQuery.event.trigger( "ajaxStop" ); + } + } + } + + return jqXHR; + }, + + getJSON: function( url, data, callback ) { + return jQuery.get( url, data, callback, "json" ); + }, + + getScript: function( url, callback ) { + return jQuery.get( url, undefined, callback, "script" ); + } +} ); + +jQuery.each( [ "get", "post" ], function( _i, method ) { + jQuery[ method ] = function( url, data, callback, type ) { + + // Shift arguments if data argument was omitted + if ( isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + // The url can be an options object (which then must have .url) + return jQuery.ajax( jQuery.extend( { + url: url, + type: method, + dataType: type, + data: data, + success: callback + }, jQuery.isPlainObject( url ) && url ) ); + }; +} ); + +jQuery.ajaxPrefilter( function( s ) { + var i; + for ( i in s.headers ) { + if ( i.toLowerCase() === "content-type" ) { + s.contentType = s.headers[ i ] || ""; + } + } +} ); + + +jQuery._evalUrl = function( url, options, doc ) { + return jQuery.ajax( { + url: url, + + // Make this explicit, since user can override this through ajaxSetup (#11264) + type: "GET", + dataType: "script", + cache: true, + async: false, + global: false, + + // Only evaluate the response if it is successful (gh-4126) + // dataFilter is not invoked for failure responses, so using it instead + // of the default converter is kludgy but it works. + converters: { + "text script": function() {} + }, + dataFilter: function( response ) { + jQuery.globalEval( response, options, doc ); + } + } ); +}; + + +jQuery.fn.extend( { + wrapAll: function( html ) { + var wrap; + + if ( this[ 0 ] ) { + if ( isFunction( html ) ) { + html = html.call( this[ 0 ] ); + } + + // The elements to wrap the target around + wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); + + if ( this[ 0 ].parentNode ) { + wrap.insertBefore( this[ 0 ] ); + } + + wrap.map( function() { + var elem = this; + + while ( elem.firstElementChild ) { + elem = elem.firstElementChild; + } + + return elem; + } ).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( isFunction( html ) ) { + return this.each( function( i ) { + jQuery( this ).wrapInner( html.call( this, i ) ); + } ); + } + + return this.each( function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + } ); + }, + + wrap: function( html ) { + var htmlIsFunction = isFunction( html ); + + return this.each( function( i ) { + jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html ); + } ); + }, + + unwrap: function( selector ) { + this.parent( selector ).not( "body" ).each( function() { + jQuery( this ).replaceWith( this.childNodes ); + } ); + return this; + } +} ); + + +jQuery.expr.pseudos.hidden = function( elem ) { + return !jQuery.expr.pseudos.visible( elem ); +}; +jQuery.expr.pseudos.visible = function( elem ) { + return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); +}; + + + + +jQuery.ajaxSettings.xhr = function() { + try { + return new window.XMLHttpRequest(); + } catch ( e ) {} +}; + +var xhrSuccessStatus = { + + // File protocol always yields status code 0, assume 200 + 0: 200, + + // Support: IE <=9 only + // #1450: sometimes IE returns 1223 when it should be 204 + 1223: 204 + }, + xhrSupported = jQuery.ajaxSettings.xhr(); + +support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); +support.ajax = xhrSupported = !!xhrSupported; + +jQuery.ajaxTransport( function( options ) { + var callback, errorCallback; + + // Cross domain only allowed if supported through XMLHttpRequest + if ( support.cors || xhrSupported && !options.crossDomain ) { + return { + send: function( headers, complete ) { + var i, + xhr = options.xhr(); + + xhr.open( + options.type, + options.url, + options.async, + options.username, + options.password + ); + + // Apply custom fields if provided + if ( options.xhrFields ) { + for ( i in options.xhrFields ) { + xhr[ i ] = options.xhrFields[ i ]; + } + } + + // Override mime type if needed + if ( options.mimeType && xhr.overrideMimeType ) { + xhr.overrideMimeType( options.mimeType ); + } + + // X-Requested-With header + // For cross-domain requests, seeing as conditions for a preflight are + // akin to a jigsaw puzzle, we simply never set it to be sure. + // (it can always be set on a per-request basis or even using ajaxSetup) + // For same-domain requests, won't change header if already provided. + if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { + headers[ "X-Requested-With" ] = "XMLHttpRequest"; + } + + // Set headers + for ( i in headers ) { + xhr.setRequestHeader( i, headers[ i ] ); + } + + // Callback + callback = function( type ) { + return function() { + if ( callback ) { + callback = errorCallback = xhr.onload = + xhr.onerror = xhr.onabort = xhr.ontimeout = + xhr.onreadystatechange = null; + + if ( type === "abort" ) { + xhr.abort(); + } else if ( type === "error" ) { + + // Support: IE <=9 only + // On a manual native abort, IE9 throws + // errors on any property access that is not readyState + if ( typeof xhr.status !== "number" ) { + complete( 0, "error" ); + } else { + complete( + + // File: protocol always yields status 0; see #8605, #14207 + xhr.status, + xhr.statusText + ); + } + } else { + complete( + xhrSuccessStatus[ xhr.status ] || xhr.status, + xhr.statusText, + + // Support: IE <=9 only + // IE9 has no XHR2 but throws on binary (trac-11426) + // For XHR2 non-text, let the caller handle it (gh-2498) + ( xhr.responseType || "text" ) !== "text" || + typeof xhr.responseText !== "string" ? + { binary: xhr.response } : + { text: xhr.responseText }, + xhr.getAllResponseHeaders() + ); + } + } + }; + }; + + // Listen to events + xhr.onload = callback(); + errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" ); + + // Support: IE 9 only + // Use onreadystatechange to replace onabort + // to handle uncaught aborts + if ( xhr.onabort !== undefined ) { + xhr.onabort = errorCallback; + } else { + xhr.onreadystatechange = function() { + + // Check readyState before timeout as it changes + if ( xhr.readyState === 4 ) { + + // Allow onerror to be called first, + // but that will not handle a native abort + // Also, save errorCallback to a variable + // as xhr.onerror cannot be accessed + window.setTimeout( function() { + if ( callback ) { + errorCallback(); + } + } ); + } + }; + } + + // Create the abort callback + callback = callback( "abort" ); + + try { + + // Do send the request (this may raise an exception) + xhr.send( options.hasContent && options.data || null ); + } catch ( e ) { + + // #14683: Only rethrow if this hasn't been notified as an error yet + if ( callback ) { + throw e; + } + } + }, + + abort: function() { + if ( callback ) { + callback(); + } + } + }; + } +} ); + + + + +// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) +jQuery.ajaxPrefilter( function( s ) { + if ( s.crossDomain ) { + s.contents.script = false; + } +} ); + +// Install script dataType +jQuery.ajaxSetup( { + accepts: { + script: "text/javascript, application/javascript, " + + "application/ecmascript, application/x-ecmascript" + }, + contents: { + script: /\b(?:java|ecma)script\b/ + }, + converters: { + "text script": function( text ) { + jQuery.globalEval( text ); + return text; + } + } +} ); + +// Handle cache's special case and crossDomain +jQuery.ajaxPrefilter( "script", function( s ) { + if ( s.cache === undefined ) { + s.cache = false; + } + if ( s.crossDomain ) { + s.type = "GET"; + } +} ); + +// Bind script tag hack transport +jQuery.ajaxTransport( "script", function( s ) { + + // This transport only deals with cross domain or forced-by-attrs requests + if ( s.crossDomain || s.scriptAttrs ) { + var script, callback; + return { + send: function( _, complete ) { + script = jQuery( " +{% endmacro %} + +{% macro body_post() %} + + + +{% endmacro %} \ No newline at end of file diff --git a/api/api.html b/api/api.html new file mode 100644 index 00000000..f2e90580 --- /dev/null +++ b/api/api.html @@ -0,0 +1,739 @@ + + + + + + + + + + + + API — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

API#

+
+
+
+

Metacatalog API Overview#

+
+

Importing#

+

The metacatalog.api submoule contains all API functions that are +available for Python. The CLI also relies on the API and therefore using the +Api is the recommended way to use metacatalog.

+

It is recommended to import the API like:

+
from metacatalog import api
+
+
+

If you have other modules present that are called api, it is recommended +to import the API like:

+
from metacatalog import api as mc_api
+
+
+
+
+

Functions#

+

There are three functions that are used to connect to the database and +create the necessary tables to setup metacatalog These are:

+ +

All other API functions follow a similar pattern. Each of them expect a +Session as the first argument +(See connect_database()).

+

The names of all functions in metacatalog.api are prefixed with an +action identifier:

+
    +
  • find_* to find entities on exact matches and filters

  • +
  • add_* to add new entitites

  • +
  • show_* to inspect raw table records

  • +
+
+
+
+

Command Overview#

+ +
+
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/api/api_add_entry.html b/api/api_add_entry.html new file mode 100644 index 00000000..dd3bbad2 --- /dev/null +++ b/api/api_add_entry.html @@ -0,0 +1,767 @@ + + + + + + + + + + + + Add Entry — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Add Entry#

+
+

Entry#

+
+
+metacatalog.api.add_entry(session: Session, title: str, author: Union[int, str], location: Union[str, Tuple[int], Tuple[float]], variable: Union[int, str], abstract: str = None, external_id: str = None, license: Union[str, int] = None, embargo: bool = False, is_partial: bool = False, **kwargs) Entry#
+

Add new Entry

+

Adds a new metadata Entry to the database. This method will create the core +entry. Usually, more steps are necessary, which will need the newly created +database ID. Such steps are:

+
    +
  • adding contributors (mandatory)

  • +
  • adding data (extremly useful)

  • +
  • adding keywords (recommended)

  • +
+
+
Parameters:
+
    +
  • session (sqlalchemy.Session) – SQLAlchemy session connected to the database.

  • +
  • title (str) – Title of the Entry

  • +
  • author (int, str) –

    +

    New in version 0.2.6.

    +
    +

    First author of the Entry. The Person record has to exist already in the +database and can be found by exact match on id (int) or last_name (str). +Organisations can also be added as an author, can be found by exact match +on id (int) or organisation_name (str).

    +

  • +
  • location (str, tuple) –

    Can be either a WKT of a EPSG:4326 location, or the coordinates as a +tuple. It has to be (X,Y), to (longitude, latitude).

    +
    +

    Changed in version 0.6.1: A POINT location should be specified here if there is a physical measurement +point that is different from the centroid of the spatial extent (e.g., +discharge measurement with the extent of the catchment). +Otherwise, Datasource.spatial_scale.extent should be used to specify the +location of the measured data.

    +
    +

  • +
  • variable (int, str) – Full variable name (str) or ID (int) of the data described by the Entry.

  • +
  • abstract (str) – Description of the data. Be as detailed as possible

  • +
  • external_id (str) – If the data described by Entry has another unique identifier, +usually supplied by the data provider, it can be stored for reference reasons.

  • +
  • comment (str) – General purpose comment that should not contain any vital information to +understand the entry. If it’s vital, it should go into the abstract.

  • +
  • license (str, int) – Either the id or full name of the license to be linked to this Entry.

  • +
  • embargo (bool) – If True, this Entry will not be publicly available until the embargo ends +The embargo period is usually 2 years but can be modified using the kwargs.

  • +
  • is_partial (bool) –

    +

    New in version 0.7.6.

    +
    +

    Flag for marking a Entry as partial. +Partial entries have to be embedded into a Composite +EntryGroup(type='Composite'). +This means, that an entry is not self-contained and needs another entry +to be complete.

    +

  • +
+
+
Returns:
+

entry – Entry instance of the added entry entity

+
+
Return type:
+

metacatalog.Entry

+
+
+
+ +
+
+

Add Entry details#

+
+
+metacatalog.api.add_details_to_entries(session: Session, entries: List[Union[int, str, Entry]], details: List[dict] = None, **kwargs) None#
+

Associate detail(s) to entrie(s). +Add key-value pair details to one, or many Entry(s). +The Entry(s) have to already exist in the database.

+
+
Parameters:
+
    +
  • session (sqlalchemy.Session) – SQLAlchemy session connected to the database.

  • +
  • entries (list) – List of identifier or single identifier to load entries. +If int, the Entry.id is assumed. If str, title is assumed. +Can also pass a metacatalog.Entry object.

  • +
  • details (list, None) –

    +

    New in version 0.1.8.

    +
    +

    List of dict of structure:

    +
    [{
    +    'key': '',
    +    'value': '',
    +    'description': ''
    +}]
    +
    +
    +

    where the description is optional and can be omitted. +If no descriptions are passed at all, you can also use **kwargs +to pass key=value pairs. You can mix details and kwargs

    +

  • +
+
+
+
+

Note

+

Each keyword argument will be added as a py:class:Detail <metacatalog.models.Detail> +and linked to each entry.

+
+
+ +
+
+

Add keywords to entries#

+
+
+metacatalog.api.add_keywords_to_entries(session: Session, entries: List[Union[int, str, Entry]], keywords: List[Union[int, str, Keyword]], alias=None) None
+

Associate keyword(s) to entrie(s)

+

Adds associations between entries and keywords. The Entry and Keyword +instances have to already exist in the database. Keywords are usually +prepopulated. You might want to alias an keyword or associate a value to +it. Use the alias and value lists for this.

+
+
Parameters:
+
    +
  • session (sqlalchemy.Session) – SQLAlchemy session connected to the database.

  • +
  • entries (list) – List of identifier or single identifier to load entries. +If int, the Entry.id is assumed. If str, title is assumed. +Can also pass a metacatalog.Entry object.

  • +
  • keywords (list) – List of identifier or single identifier to load keywords. +If int, Keyword.id is assumed, If str, Keyword.value is assumed. +Can also pass a metacatalog.Keyword object.

  • +
  • alias (list) –

    List of, or single alias names. The shape has to match the +keywords parameter. These alias will rename the keywords on +association. In case one instance should not recive an alias, +pass None instead.

    +
    +

    Deprecated since version 0.4.5: ‘alias’ will be removed with a future release

    +
    +

  • +
+
+
Return type:
+

void

+
+
+
+

See also

+

metacatalog.Entry, metacatalog.Keyword

+
+
+ +
+
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/api/api_add_entry.ipynb b/api/api_add_entry.ipynb new file mode 100644 index 00000000..2ce1cd9d --- /dev/null +++ b/api/api_add_entry.ipynb @@ -0,0 +1,82 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Add Entry" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Entry" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.add_entry" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Add Entry details" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.add_details_to_entries" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Add keywords to entries" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.add_keywords_to_entries\n", + " :noindex:" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/api/api_add_group.html b/api/api_add_group.html new file mode 100644 index 00000000..b24768c1 --- /dev/null +++ b/api/api_add_group.html @@ -0,0 +1,685 @@ + + + + + + + + + + + + Add EntryGroup — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Add EntryGroup#

+
+

Group#

+

The grouping of existing Entries can be done by the api.add_group endpoint. This function also takes a specification of the type of group. Additionally, there is a api.add_project function for the special case of adding a group of type 'Project'.

+
+

Note

+

'Composite' entries are best added by the +model interface of one of the entries. +Use the Entry.make_composite function for that.

+
+
+
+metacatalog.api.add_group(session: Session, group_type: Union[int, str], entry_ids: List[int], title: str = None, description: str = None) EntryGroup#
+

Adds a new EntryGroup to the database. The Entry(s) have to exist in the +database to be associated correctly. +.. versionadded:: 0.2

+
+
Parameters:
+
    +
  • session (sqlalchemy.Session) – SQLAlchemy session connected to the database.

  • +
  • group_type (int, str) – Either EntryGroupType +id or name to be used.

  • +
  • entry_ids (list of int) – List of Entry.id to be associated +tp this Group.

  • +
  • title (str) – Optional title of this Group. Mandatory, if the type is a ‘Project’

  • +
  • description (str) – Optional description of this Group. Mandatory is the type is a +‘Project’.

  • +
+
+
Returns:
+

entry – EntryGroup instance of the added group

+
+
Return type:
+

metacatalog.models.EntryGroup

+
+
+
+ +
+
+

Project#

+
+
+metacatalog.api.add_project(session: Session, entry_ids: List[int], title: str = None, description: str = None) EntryGroup#
+

Adds a new Project EntryGroup to the database. +The Entry(s) have to exist in the database to be associated correctly.

+
+

New in version 0.2.

+
+
+
Parameters:
+
    +
  • session (sqlalchemy.Session) – SQLAlchemy session connected to the database.

  • +
  • entry_ids (list of int) – List of Entry.id to be associated +tp this Project.

  • +
  • title (str) – Project title.

  • +
  • description (str) – Project description.

  • +
+
+
Returns:
+

entry – EntryGroup instance of the added group

+
+
Return type:
+

metacatalog.models.EntryGroup

+
+
+
+ +
+
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/api/api_add_group.ipynb b/api/api_add_group.ipynb new file mode 100644 index 00000000..438cc764 --- /dev/null +++ b/api/api_add_group.ipynb @@ -0,0 +1,80 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Add EntryGroup" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Group\n", + "\n", + "The grouping of existing Entries can be done by the `api.add_group` endpoint. This function also takes a specification of the type of group. Additionally, there is a `api.add_project` function for the special case of adding a group of type `'Project'`. " + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. note::\n", + "\n", + " ``'Composite'`` entries are best added by the \n", + " `model interface `_ of one of the entries.\n", + " Use the ``Entry.make_composite`` function for that." + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.add_group" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Project" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.add_project" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/api/api_add_keyword.html b/api/api_add_keyword.html new file mode 100644 index 00000000..ab13ba0a --- /dev/null +++ b/api/api_add_keyword.html @@ -0,0 +1,707 @@ + + + + + + + + + + + + Add keyword — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Add keyword#

+
+

Keyword#

+
+
+metacatalog.api.add_keyword(session: Session, path: str, thesaurus: Union[int, dict]) List[Keyword]#
+

Add a new keyword to the database. The keyword is +added by the full path.

+
+
Parameters:
+
    +
  • session (sqlalchemy.Session) – SQLAlchemy session connected to the database.

  • +
  • path (str) – A full path to the keyword, each element connected +by a ‘ > ‘ sequence. E.g.: +Topic > Term > Variable_level_1 etc.

  • +
  • thesaurus (dict, int) –

    +

    New in version 0.1.10.

    +
    +

    Either a thesaurus entity, that distributes the +controlled keywords, or an id of the an already +existing thesaurusName. It is highly recommended +to use an existing ID.

    +

  • +
+
+
+
+

Note

+

This API endpoint is designed to add custom keywords to +metacatalog. It will use the full path keywords and split +them automatically for convenience.

+
+

Warning

+

Each keyword part will receive a new UUID, thus you +have to use the metacatalog.models.Keyword interface +to add existing keywords, that already contain a +UUID.

+
+
+
+
Returns:
+

List of the deconstructed Keyword entities

+
+
Return type:
+

keywords, list of metacatalog.Keywords

+
+
+
+ +
+
+

Add keywords to entries#

+
+
+metacatalog.api.add_keywords_to_entries(session: Session, entries: List[Union[int, str, Entry]], keywords: List[Union[int, str, Keyword]], alias=None) None#
+

Associate keyword(s) to entrie(s)

+

Adds associations between entries and keywords. The Entry and Keyword +instances have to already exist in the database. Keywords are usually +prepopulated. You might want to alias an keyword or associate a value to +it. Use the alias and value lists for this.

+
+
Parameters:
+
    +
  • session (sqlalchemy.Session) – SQLAlchemy session connected to the database.

  • +
  • entries (list) – List of identifier or single identifier to load entries. +If int, the Entry.id is assumed. If str, title is assumed. +Can also pass a metacatalog.Entry object.

  • +
  • keywords (list) – List of identifier or single identifier to load keywords. +If int, Keyword.id is assumed, If str, Keyword.value is assumed. +Can also pass a metacatalog.Keyword object.

  • +
  • alias (list) –

    List of, or single alias names. The shape has to match the +keywords parameter. These alias will rename the keywords on +association. In case one instance should not recive an alias, +pass None instead.

    +
    +

    Deprecated since version 0.4.5: ‘alias’ will be removed with a future release

    +
    +

  • +
+
+
Return type:
+

void

+
+
+
+

See also

+

metacatalog.Entry, metacatalog.Keyword

+
+
+ +
+
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/api/api_add_keyword.ipynb b/api/api_add_keyword.ipynb new file mode 100644 index 00000000..203bb8d9 --- /dev/null +++ b/api/api_add_keyword.ipynb @@ -0,0 +1,65 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Add keyword" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Keyword" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.add_keyword" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Add keywords to entries" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.add_keywords_to_entries" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/api/api_add_license.html b/api/api_add_license.html new file mode 100644 index 00000000..e06cde8e --- /dev/null +++ b/api/api_add_license.html @@ -0,0 +1,641 @@ + + + + + + + + + + + + Add license — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Add license#

+
+
+metacatalog.api.add_license(session: Session, short_title: str, title: str, **kwargs) License#
+

Add a new License record to the database.

+
+
Parameters:
+
    +
  • session (sqlalchemy.Session) – SQLAlchemy session connected to the database.

  • +
  • short_title (str) – Short title of the License, max 40 letters.

  • +
  • title (str) – Full title of the License.

  • +
  • summary (str) – Gives a short summary of the key points for +the given license.

  • +
  • full_text (str) – Full legal code of the license, if available.

  • +
  • link (str) – Link to an online resource of the license.

  • +
  • by_attribution (bool) – Does the license require author attribution on +distribution? Defaults to True.

  • +
  • share_alike (bool) – Does the license require redistributions to be +licensed under the same license? Defaults to True.

  • +
  • commercial_use (bool) – Does the license permit commercial use of the +resource? Defaults to True

  • +
+
+
Returns:
+

license – License instance of the added license entity.

+
+
Return type:
+

metacatalog.License

+
+
+
+ +
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/api/api_add_license.ipynb b/api/api_add_license.ipynb new file mode 100644 index 00000000..c28c349c --- /dev/null +++ b/api/api_add_license.ipynb @@ -0,0 +1,42 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Add license" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.add_license" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/api/api_add_person.html b/api/api_add_person.html new file mode 100644 index 00000000..e1477d9d --- /dev/null +++ b/api/api_add_person.html @@ -0,0 +1,708 @@ + + + + + + + + + + + + Add Person — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Add Person#

+
+

Person#

+
+
+metacatalog.api.add_person(session: Session, first_name: str, last_name: str, organisation_name: str = None, organisation_abbrev: str = None, affiliation: str = None, attribution: str = None, uuid: str = None) Person#
+

Add a new Person to the database. A person can be a real Person +or an institution. Then, the institution name goes into the +last_name column and first_name can actively be set to None. +An affiliation can be added to both as a single string.

+
+
Parameters:
+
    +
  • session (sqlalchemy.Session) – SQLAlchemy session connected to the database.

  • +
  • uuid (str) –

    An UUID version 4 to globally identify the person list. +If you add a new person, do not set the UUID as a new +UUID will be set.

    +

  • +
  • first_name (str) – A real persons first name. If omitted, the ‘Person’ is +assumed to be an institution

  • +
  • last_name (str) – A real persons last name. If first_name is NULL, +last_name is assumed to be an institution.

  • +
  • organisation_name (str) –

    +

    New in version 0.1.10.

    +
    +

    Optional, but highly_recommended if applicable. Name of +the head institution, whithout department.

    +

  • +
  • organisation_abbrev (str) –

    +

    New in version 0.2.6.

    +
    +

    Optional, abbreviated version of the Institution. I.e. the +famous Karlsruhe Institute of Technology is better known as ‘KIT’

    +

  • +
  • affiliation (str) – Affiliation if applicable. Has to go into a single string +of 1024 bytes. Full attribution including department and group name.

  • +
  • attribution (str.) – Optional. Attribution recommondation for all datasets +this Person is associated to as a first author.

  • +
+
+
Returns:
+

entry – Entry instance of the added Person entity

+
+
Return type:
+

metacatalog.Person

+
+
+
+

See also

+

add_organisation

+
+
+ +
+
+

Add persons to entries#

+
+
+metacatalog.api.add_persons_to_entries(session: Session, entries: List[Union[int, str, Entry]], persons: List[Union[int, str, Person]], roles: List[Union[int, str, PersonRole]], order: List[int]) None#
+

Add person(s) to entrie(s)

+

Adds associations between entries and persons. The Entry and Person +instances have to already exist in the database. Each association +has to further define the role of the person for the respective entry.

+
+
Parameters:
+
    +
  • session (sqlalchemy.Session) – SQLAlchemy session connected to the database.

  • +
  • entries (list) – List of identifier or single identifier to load entries. +If int, the Entry.id is assumed. If str, title is assumed. +Can also pass a metacatalog.Entry object.

  • +
  • persons (list) – List of identifier or single identifier to load persons. +If int, Person.id is assumed, If str, Person.last_name is assumed. +Can also pass a metacatalog.Person object.

  • +
  • roles (list) – List of, or single role. The shape has to match the +persons parameter. The role has to be identifies by id (int) or +role name (str).

  • +
  • order (list) – List of, or single order. The shape has to match the +persons parameter. The order gives the ascending order of +contributors on the respecive entry (after the author).

  • +
+
+
Return type:
+

void

+
+
+
+

See also

+

metacatalog.Entry, metacatalog.Person, metacatalog.PersonRole

+
+
+ +
+
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/api/api_add_person.ipynb b/api/api_add_person.ipynb new file mode 100644 index 00000000..d5ca80b4 --- /dev/null +++ b/api/api_add_person.ipynb @@ -0,0 +1,65 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Add Person" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Person" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.add_person" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Add persons to entries" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.add_persons_to_entries" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/api/api_add_thesaurus.html b/api/api_add_thesaurus.html new file mode 100644 index 00000000..8facf34b --- /dev/null +++ b/api/api_add_thesaurus.html @@ -0,0 +1,728 @@ + + + + + + + + + + + + Add Thesaurus — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Add Thesaurus#

+
+
+metacatalog.api.add_thesaurus(session: Session, name: str, title: str, organisation: str, url: str, description: str = None, uuid: str = None) Thesaurus#
+
+

New in version 0.1.10.

+
+

Add a new thesaurus to reference new keywords.

+
+

Warning

+

If you want to add existing Thesaurii for reference, +you have to to add their existing UUID as well, +otherwise this function will assign new ones.

+
+
+
+metacatalog.api.session#
+

SQLAlchemy session connected to the database.

+
+
Type:
+

sqlalchemy.Session

+
+
+
+ +
+
+metacatalog.api.uuid#
+

An UUID version 4 to globally identify the keyword list. +If you add a new thesaurus, do not set the UUID as a new +UUID will be set.

+
+
Type:
+

str

+
+
+
+ +
+
+metacatalog.api.name#
+

Short name of the keyword list. Do not use the full +qualified name here, just the short version used for +filtering.

+
+
Type:
+

str

+
+
+
+ +
+
+metacatalog.api.title#
+

Full qualified name used by the distributor to identify +the keyword list globally.

+
+
Type:
+

str

+
+
+
+ +
+
+metacatalog.api.organisation#
+

The full organisation name to identify the authority +maintaining the thesaurus.

+
+
Type:
+

str

+
+
+
+ +
+
+metacatalog.api.description#
+

Optional. Futher details about the thesaurus, like scope or +implementation details.

+
+
Type:
+

str

+
+
+
+ +
+
+metacatalog.api.url#
+

The permanent URL at which the full keyword list can be +found. Do not link the organisation or an overview page here. +Usually, this points to an XML representation of the +thesaurus. It may contain a placeholder called UUID to +load specific keyword objects only.

+
+
Type:
+

str

+
+
+
+ +
+
Returns:
+

thesaurus – Thesaurus instance of the added thesaurus entity.

+
+
Return type:
+

metacatalog.models.Thesaurus

+
+
+
+ +
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/api/api_add_thesaurus.ipynb b/api/api_add_thesaurus.ipynb new file mode 100644 index 00000000..bce1ee33 --- /dev/null +++ b/api/api_add_thesaurus.ipynb @@ -0,0 +1,42 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Add Thesaurus" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.add_thesaurus" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/api/api_add_variable.html b/api/api_add_variable.html new file mode 100644 index 00000000..7807e563 --- /dev/null +++ b/api/api_add_variable.html @@ -0,0 +1,685 @@ + + + + + + + + + + + + Add variable and unit — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Add variable and unit#

+
+

variable#

+
+
+metacatalog.api.add_variable(session: Session, name: str, symbol: str, column_names: List[str], unit: Union[int, str], keyword: Union[int, str] = None) Variable#
+

Add a new variable to the database.

+
+
Parameters:
+
    +
  • session (sqlalchemy.Session) – SQLAlchemy session connected to the database.

  • +
  • name (str) – The variable name. Max 64 letters.

  • +
  • symbol (str) – The variable symbol. Try to use the correct +physical variable symbols and avoid dublicates.

  • +
  • column_names (list) –

    +

    New in version 0.3.0.

    +
    +

    List of default column names that will be displayed when exporting the data. +The columns are named in the same order as they appear in the list.

    +

  • +
  • unit (int, str) – Either the id or full name of the unit to be +linked to this variable.

  • +
  • keyword (int, str) –

    +

    New in version 0.8.4.

    +
    +

    Either the id or full path of the keyword to be +linked to this variable. +It is strongly recommended to add a keyword from a controlled thesaurus to a +newly created variable to improve the findability of the variable.

    +

  • +
+
+
Returns:
+

variable – Variable instance of the added variable entity

+
+
Return type:
+

metacatalog.models.Variable

+
+
+
+ +
+
+

unit#

+
+
+metacatalog.api.add_unit(session: Session, name: str, symbol: str, si: str = None) Unit#
+

Add a new unit to the database

+
+
Parameters:
+
    +
  • session (sqlalchemy.Session) – SQLAlchemy session connected to the database.

  • +
  • name (str) – The unit name. Max 64 letters.

  • +
  • symbol (str) – The unit symbol. Try to use the correct +physical unit symbols. Max 12 letters.

  • +
  • si (str) – SI representation of the unit. Can be +omitted.

  • +
+
+
Returns:
+

unit – Unit instance of the added unit entity

+
+
Return type:
+

metacatalog.Unit

+
+
+
+ +
+
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/api/api_add_variable.ipynb b/api/api_add_variable.ipynb new file mode 100644 index 00000000..1f8aea8d --- /dev/null +++ b/api/api_add_variable.ipynb @@ -0,0 +1,65 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Add variable and unit" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## variable" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.add_variable" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## unit" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.add_unit" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/api/api_connect_database.html b/api/api_connect_database.html new file mode 100644 index 00000000..7d59bec5 --- /dev/null +++ b/api/api_connect_database.html @@ -0,0 +1,735 @@ + + + + + + + + + + + + Connect database — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Connect database#

+
+
+metacatalog.api.connect_database(*args, **kwargs) Session#
+

Connect to database and returns a Session +to the database.

+

You can either pass args and kwargs as accepted by sqlachemy’s +create_engine method or pass a name of a stored connection string. +Connection strings can be stored using save_connection method. +Empty arguments will load the default connection string, if there is any.

+
+
Parameters:
+
    +
  • *args – See sqlalchemy create_engine for a full list. +The only additional argument is a stored connection string, if any. The function +will first check for a stored connection of given name and only if none is found, pass +*args down to create_engine. +If len(args)==0, a stored connection of name 'default' will be loaded.

  • +
  • **kwargs – Only used if no stored connection is loaded.

  • +
+
+
+
+

See also

+

save_connection

+
+
+ +
+

Example#

+
+

Stored default connection#

+
+
[1]:
+
+
+
from metacatalog import api
+session = api.connect_database()
+
+print(session)
+print(session.bind)
+
+
+
+
+
+
+
+
+<sqlalchemy.orm.session.Session object at 0x7f97b40b0250>
+Engine(postgresql://postgres:***@localhost:5432/metacatalog)
+
+
+
+
+

Connect without storing passwords#

+

In many productions scenarios you don’t want to store a password in cleartext to any kind of file, even not in a trusted environment. Usually, you will set the password as an environment variable. In Windows you do that like:

+
set POSTGRES_PASSWORD=notmyrealpassword
+
+
+

In Linux like:

+
export POSTGRES_PASSWORD=notmyrealpassword
+
+
+

In CIs like Travis CI, CirleCI or Github actions you can usually set them in the repositories settings.

+
+

Warning

+

If you are using a CI, remember to not set the password environment variable into any kind of yaml config files, which usually also define a section on envirnment variables. These yaml configs are often public!

+
+
+
[5]:
+
+
+
%env POSTGRES_PASSWORD=notmyrealpassword
+
+
+
+
+
+
+
+
+env: POSTGRES_PASSWORD=notmyrealpassword
+
+
+

Then, istead of loading stored connection strings from the ~/.metacatalog/config.json, you can pass the connection URI directly to connect_database:

+
+
[6]:
+
+
+
import os
+session = api.connect_database(
+    'postgresql://dbuser:{pw}@localhost:5432/metacatalg'.format(pw=os.environ['POSTGRES_PASSWORD'])
+)
+
+print(session)
+print(session.bind)
+
+
+
+
+
+
+
+
+<sqlalchemy.orm.session.Session object at 0x7f9785755090>
+Engine(postgresql://dbuser:***@localhost:5432/metacatalg)
+
+
+
+

Warning

+

Remember that IPython consoles cache all commands into a sqlite database. If you store the password first into a variable like:

+
password = 'notmyrealpassword'
+api.connect_database('...'.format(pw=password))
+
+
+

Your password will be in the cache then!

+
+
+
+
+ + +
+ + + + + + + +
+ + + + + + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/api/api_connect_database.ipynb b/api/api_connect_database.ipynb new file mode 100644 index 00000000..97c013c4 --- /dev/null +++ b/api/api_connect_database.ipynb @@ -0,0 +1,185 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Connect database" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.connect_database " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Stored default connection" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Engine(postgresql://postgres:***@localhost:5432/metacatalog)\n" + ] + } + ], + "source": [ + "from metacatalog import api\n", + "session = api.connect_database()\n", + "\n", + "print(session)\n", + "print(session.bind)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Connect without storing passwords" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In many productions scenarios you don't want to store a password in cleartext to any kind of file, even not in a trusted environment. Usually, you will set the password as an environment variable. In Windows you do that like:\n", + "\n", + "```powershell\n", + "set POSTGRES_PASSWORD=notmyrealpassword\n", + "```\n", + "\n", + "In Linux like:\n", + "\n", + "```sh\n", + "export POSTGRES_PASSWORD=notmyrealpassword\n", + "```\n", + "\n", + "In CIs like Travis CI, CirleCI or Github actions you can usually set them in the repositories settings. " + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. warning::\n", + " \n", + " If you are using a CI, remember to **not** set the password environment variable into any kind of yaml config files, which usually also define a section on envirnment variables. These yaml configs are often public!" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "env: POSTGRES_PASSWORD=notmyrealpassword\n" + ] + } + ], + "source": [ + "%env POSTGRES_PASSWORD=notmyrealpassword" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Then, istead of loading stored connection strings from the `~/.metacatalog/config.json`, you can pass the connection URI directly to `connect_database`:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Engine(postgresql://dbuser:***@localhost:5432/metacatalg)\n" + ] + } + ], + "source": [ + "import os\n", + "session = api.connect_database(\n", + " 'postgresql://dbuser:{pw}@localhost:5432/metacatalg'.format(pw=os.environ['POSTGRES_PASSWORD'])\n", + ")\n", + "\n", + "print(session)\n", + "print(session.bind)" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. warning::\n", + "\n", + " Remember that IPython consoles cache all commands into a sqlite database. If you store the password first into a variable like: \n", + " \n", + " .. code-block:: python\n", + " \n", + " password = 'notmyrealpassword'\n", + " api.connect_database('...'.format(pw=password))\n", + " \n", + " Your password will be in the cache then!" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "finalized": { + "timestamp": 1590313084911, + "trusted": true + }, + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/api/api_create_tables.html b/api/api_create_tables.html new file mode 100644 index 00000000..e51590ec --- /dev/null +++ b/api/api_create_tables.html @@ -0,0 +1,622 @@ + + + + + + + + + + + + Create Tables — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Create Tables#

+
+
+metacatalog.api.create_tables(session: Session) None#
+

Create all tables in the database using the given +Session <sqlalchemy.Session> instance.

+
+
Parameters:
+

session (sqlalchemy.Session) – Session instance connected to the database.

+
+
+
+ +
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/api/api_create_tables.ipynb b/api/api_create_tables.ipynb new file mode 100644 index 00000000..47cdafa3 --- /dev/null +++ b/api/api_create_tables.ipynb @@ -0,0 +1,42 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Create Tables" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.create_tables" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/api/api_find_entry.html b/api/api_find_entry.html new file mode 100644 index 00000000..7c5f3635 --- /dev/null +++ b/api/api_find_entry.html @@ -0,0 +1,889 @@ + + + + + + + + + + + + Find Entry — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Find Entry#

+

The Entry is the main and most atomic entity in metacatalog. A set of metadata that describes one datasource of unique data type is described as Entry. In cases, where more than one Entry need to be grouped together to provide useful information, a n:m relation from Entry to EntryGroups can be created. Depending on the kind of connection between the entries, EntryGroups are futher described by EntryGroupTypes.

+
+

Entry#

+
+
+metacatalog.api.find_entry(return_iterator: typing_extensions.Literal[False] = False, as_result: typing_extensions.Literal[False] = False) List['Entry']#
+
+metacatalog.api.find_entry(return_iterator: typing_extensions.Literal[True] = False, as_result: bool = False) Query
+
+metacatalog.api.find_entry(return_iterator: typing_extensions.Literal[False] = False, as_result: typing_extensions.Literal[True] = False) List[ImmutableResultSet]
+
+

Find an meta data Entry on exact matches. Entries can be +identified by id, title, external_id and version. The +version can be added to all other matching types, which +are mutually exclusive.

+
+

Changed in version 0.1.8: string matches now allow '%' and '*' wildcards +and can be inverted by prepending !

+
+
+

Changed in version 0.2.14: Can be returned as ImmutableResultSet now.

+
+
+

Changed in version 0.3.9: By setting include_partial to True, the API can now find +partial Entries. This does only make sense in combination +with as_result=True, to lazy-load the complete record.

+
+
+

Changed in version 0.7.4: New arugment coauthor introduced, which is a functional +replacement for the now deprecated ‘contributor’ argument.

+
+
+

Deprecated since version 0.7.4: The contributor keyword is deprecated and will change its +behavior in a future release. Use the new coauthor +argument from now on.

+
+
+
sessionsqlalchemy.Session

SQLAlchemy session connected to the database.

+
+
idinteger

Database unique ID of the requested record. Will +return only one record.

+
+
uuidstr
+

New in version 0.1.13.

+
+

Find by version 4 UUID. If uuid is given, all other options +will be ignored.

+
+
titlestr

Title attribute of the Entry.

+
+
abstractstr
+

New in version 0.1.8.

+
+

Abstract attibute of the Entry.

+
+

Note

+

The abstract is usually a full text and the FIND operation +uses exact matches. Therefore be sure to use a wildcard

+
+
api.find_entry(session: 'Session', abstract='*phrase to find*')
+
+
+
+
licensestr, int
+

New in version 0.2.2.

+
+

The license can be a License, +its id (int) or the short_title (str).

+
+
variablestr, int
+

New in version 0.2.2.

+
+

The variable can be a Variable, +its id (int) or the name (str).

+
+
external_idstr

External id attrinbute of the Entry.

+
+
versionint, str
+

Changed in version 0.2: The default value is now ‘latest’

+
+

Version number of the Entry. Can be combined with +the other matching parameters, as they might not be +different between versions. +If version == ‘latest’, only the latest version will be found. +If None, all version are integrated.

+
+
projectint, str
+

New in version 0.2.2.

+
+
+
+
+
+
`
+

The project can be a EntryGroup of +EntryGroupType.name=='Project', +its id (int) or title (str)

+
+
+
authorint, str
+

New in version 0.2.2.

+
+

The author can be a Person, +his id (int) or name (str). A string argument will match first and last +names. The author is only the first author. For other coauthor see +coauthor.

+
+
coauthorint ,str
+

New in version 0.7.4.

+
+

The coauthor can be a Person, +his id (int) or name (str). A string argument will match first and last +names. A co author is anyone associated as first or co-author. For +first author only, see author.

+
+
contributorint, str
+

New in version 0.2.2.

+
+
+

Deprecated since version 0.7.4: This argument will change its behavior with a future release. +Use coauthor as a repalcement.

+
+

The contributor can be a Person, +his id (int) or name (str). A string argument will match first and last +names. A contributor is anyone associated as first or co-author. For +first author only, see author.

+
+
keywordslist of str, int
+

New in version 0.2.2.

+
+

The entries can be filtered by tagged controlled keywords. The given +keyword or list of keywords will be matched against the value (str) or +id (int). If more than one is given, the entries need to be tagged by +all keywords. An OR search is not possible, through the API.

+
+
detailsdict

..versionadded:: 0.2.2

+

Entries can be filtered by additional details. The details need to be +specified as dictioniares of name=value pairs. If more than one +pair is given, the query will combine the pairs by AND. +An OR search is not possible, through the API. +Search for value only, using a wildcard for the key *=value.

+
+
include_partialbool
+

New in version 0.3.9.

+
+

Include partial entries into the response. Defaults to False.

+
+

Note

+

Partial Entries might not be usefull, as they can miss important +metadata. Thus, it is highly recommended to set as_result=True. +Then, the returned +ImmutableResultSet +will lazy-load the sibling informations and merge them

+
+
+
by_geometrystr, list
+

New in version 0.2.10.

+
+

The passed argument can be a WKT (string) or a list of numbers. If three +numbers are passed, this is interpreted as a center point and a buffer +distance in meter. If four numbers are passed, this is a bounding box. +If a 2D-list of lists is passed (with two numbers), this will be used to +construct a search Polygon. +Finally the constructed geometry is used to apply a spatial filter to the +results.

+
+
as_resultbool

If True, the reuslts will be merged into a +ImmutableResultSet. +Defaults to False. Will be ignored if return_iterator +is True

+
+
return_iteratorbool

If True, an iterator returning the requested objects +instead of the objects themselves is returned.

+
+
+
+
recordslist

List of matched Entry or ImmutableResultSet.

+
+
+
+
+
+ +
+
+

Entry-Group#

+
+
+metacatalog.api.find_group(return_iterator: typing_extensions.Literal[False] = False, as_result: typing_extensions.Literal[False] = False) List['EntryGroup']#
+
+metacatalog.api.find_group(return_iterator: typing_extensions.Literal[True] = False, as_result: bool = False) Query
+
+metacatalog.api.find_group(return_iterator: typing_extensions.Literal[False] = False, as_result: typing_extensions.Literal[True] = False) List[ImmutableResultSet]
+

Find a group of entries on exact matches. Groups can be +identified by id, title or its type.

+
+

Changed in version 0.1.8: string matches now allow ‘%’ and ‘*’ wildcards and can +be inverted by prepending !

+
+
+

Changed in version 0.2.14: Can be returned as ImmutableResultSet now.

+
+
+
Parameters:
+
    +
  • session (sqlalchemy.Session) – SQLAlchemy session connected to the database.

  • +
  • id (integer) – Database unique ID of the requested record. Will +return only one record.

  • +
  • uuid (str) –

    +

    New in version 0.1.13.

    +
    +

    Find by version 4 UUID. If uuid is given, all other options +will be ignored.

    +

  • +
  • title (str) – Title attribute of the group.

  • +
  • type (int, str) – Either the id or name of a group type to exact match.

  • +
  • return_iterator (bool) – If True, an iterator returning the requested objects +instead of the objects themselves is returned.

  • +
  • as_result (bool) – If True, the reuslts will be merged into a +ImmutableResultSet. +Defaults to False. Will be ignored if return_iterator +is True

  • +
+
+
Returns:
+

records – List of matched EntryGroup or ImmutableResultSet.

+
+
Return type:
+

list

+
+
+
+ +
+
+

Group type#

+
+
+metacatalog.api.find_group_type(return_iterator: typing_extensions.Literal[False] = False) List['EntryGroupType']#
+
+metacatalog.api.find_group_type(return_iterator: typing_extensions.Literal[True] = False) Query
+

Find a group type on exact matches. The group types +describes a collection of entries.

+
+

Changed in version 0.1.8: string matches now allow ‘%’ and ‘*’ wildcards and can +be inverted by prepending !

+
+
+
Parameters:
+
    +
  • session (sqlalchemy.Session) – SQLAlchemy session connected to the database.

  • +
  • id (integer) – Database unique ID of the requested record. Will +return only one record.

  • +
  • name (str) – Name attribute of the group type.

  • +
  • return_iterator (bool) – If True, an iterator returning the requested objects +instead of the objects themselves is returned.

  • +
+
+
Returns:
+

records – List of matched EntryGroupType instance.

+
+
Return type:
+

list of metacatalog.EntryGroupType

+
+
+
+ +
+
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/api/api_find_entry.ipynb b/api/api_find_entry.ipynb new file mode 100644 index 00000000..6c30a24a --- /dev/null +++ b/api/api_find_entry.ipynb @@ -0,0 +1,88 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Find Entry" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `Entry` is the main and most atomic entity in metacatalog. A set of metadata that describes one datasource of unique data type is described as `Entry`. In cases, where more than one `Entry` need to be grouped together to provide useful information, a `n:m` relation from `Entry` to `EntryGroup`s can be created. Depending on the kind of connection between the entries, `EntryGroup`s are futher described by `EntryGroupType`s." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Entry" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.find_entry" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Entry-Group" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.find_group" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Group type" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.find_group_type" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/api/api_find_keyword.html b/api/api_find_keyword.html new file mode 100644 index 00000000..932fd95d --- /dev/null +++ b/api/api_find_keyword.html @@ -0,0 +1,743 @@ + + + + + + + + + + + + Find Keyword — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Find Keyword#

+
+

Help#

+
+
+metacatalog.api.find_keyword(return_iterator: typing_extensions.Literal[False] = False) List['Keyword']#
+
+metacatalog.api.find_keyword(return_iterator: typing_extensions.Literal[True] = False) Query
+

Return one or many keyword entries from the database on +exact matches.

+
+
Parameters:
+
    +
  • session (sqlalchemy.Session) – SQLAlchemy session connected to the database.

  • +
  • id (integer) – Database unique ID of the requested record. Will +return only one record.

  • +
  • uuid (str) –

    +

    New in version 0.1.13.

    +
    +

    Find by version 4 UUID. If uuid is given, all other options +will be ignored.

    +

  • +
  • value (str) – Value of the requested keyword(s). Multiple record +return is possible.

  • +
  • full_path (str) –

    +

    New in version 0.8.4.

    +
    +

    Full path of the requested keyword.

    +

  • +
  • thesaurus_name (str) –

    +

    New in version 0.1.10.

    +
    +

    The name of the thesaurus, the keyword originates from. +At the current stage, only ‘GCMD’ science keywords are +implemented.

    +

  • +
  • return_iterator (bool) – If True, an iterator returning the requested objects +instead of the objects themselves is returned.

  • +
+
+
Returns:
+

records – List of matched Keyword instance.

+
+
Return type:
+

list of metacatalog.Keyword

+
+
+
+ +
+
+

Example#

+
+
[1]:
+
+
+
from metacatalog import api
+
+session = api.connect_database()
+
+
+
+
+
+
+
+
+---------------------------------------------------------------------------
+CommandError                              Traceback (most recent call last)
+<ipython-input-1-f0273587aace> in <module>
+      1 from metacatalog import api
+      2
+----> 3 session = api.connect_database()
+
+~/Dropbox/python/metacatalog/metacatalog/api/db.py in connect_database(*args, **kwargs)
+     55     """
+     56     # get session
+---> 57     session = get_session(*args, **kwargs)
+     58
+     59     return session
+
+~/Dropbox/python/metacatalog/metacatalog/db/session.py in get_session(*args, **kwargs)
+     98
+     99     # else build a new engine
+--> 100     engine = get_engine(*args, **kwargs)
+    101
+    102     # create the Session class
+
+~/Dropbox/python/metacatalog/metacatalog/db/session.py in get_engine(*args, **kwargs)
+     81     # check alembic version
+     82     try:
+---> 83         check_database_version(engine=engine)
+     84     except RuntimeError as e:
+     85         # no missmatch allowed
+
+~/Dropbox/python/metacatalog/metacatalog/db/migration.py in check_database_version(engine)
+     31     # get the alembic config file
+     32     config = Config(os.path.join(BASEPATH, '..', 'alembic.ini'))
+---> 33     script_ = script.ScriptDirectory.from_config(config)
+     34
+     35     # connect to database
+
+~/miniconda3/envs/py37/lib/python3.7/site-packages/alembic-1.4.2-py3.7.egg/alembic/script/base.py in from_config(cls, config)
+    147             version_locations=version_locations,
+    148             timezone=config.get_main_option("timezone"),
+--> 149             hook_config=config.get_section("post_write_hooks", {}),
+    150         )
+    151
+
+~/miniconda3/envs/py37/lib/python3.7/site-packages/alembic-1.4.2-py3.7.egg/alembic/script/base.py in __init__(self, dir, file_template, truncate_slug_length, version_locations, sourceless, output_encoding, timezone, hook_config)
+     68                 "Path doesn't exist: %r.  Please use "
+     69                 "the 'init' command to create a new "
+---> 70                 "scripts folder." % os.path.abspath(dir)
+     71             )
+     72
+
+CommandError: Path doesn't exist: '/home/mirko/Dropbox/python/metacatalog/docs/source/api/alembic'.  Please use the 'init' command to create a new scripts folder.
+
+
+
+
[6]:
+
+
+
for kw in api.find_keyword(session, value='*TEMPERATURE*'):
+    print(kw.full_path)
+
+
+
+
+
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/api/api_find_keyword.ipynb b/api/api_find_keyword.ipynb new file mode 100644 index 00000000..30fbde5c --- /dev/null +++ b/api/api_find_keyword.ipynb @@ -0,0 +1,95 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Find Keyword" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Help" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.find_keyword" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "ename": "CommandError", + "evalue": "Path doesn't exist: '/home/mirko/Dropbox/python/metacatalog/docs/source/api/alembic'. Please use the 'init' command to create a new scripts folder.", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mCommandError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mmetacatalog\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mapi\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0msession\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mapi\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconnect_database\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m~/Dropbox/python/metacatalog/metacatalog/api/db.py\u001b[0m in \u001b[0;36mconnect_database\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 55\u001b[0m \"\"\"\n\u001b[1;32m 56\u001b[0m \u001b[0;31m# get session\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 57\u001b[0;31m \u001b[0msession\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mget_session\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 58\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 59\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0msession\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Dropbox/python/metacatalog/metacatalog/db/session.py\u001b[0m in \u001b[0;36mget_session\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 98\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 99\u001b[0m \u001b[0;31m# else build a new engine\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 100\u001b[0;31m \u001b[0mengine\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mget_engine\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 101\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 102\u001b[0m \u001b[0;31m# create the Session class\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Dropbox/python/metacatalog/metacatalog/db/session.py\u001b[0m in \u001b[0;36mget_engine\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 81\u001b[0m \u001b[0;31m# check alembic version\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 82\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 83\u001b[0;31m \u001b[0mcheck_database_version\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mengine\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mengine\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 84\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mRuntimeError\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 85\u001b[0m \u001b[0;31m# no missmatch allowed\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Dropbox/python/metacatalog/metacatalog/db/migration.py\u001b[0m in \u001b[0;36mcheck_database_version\u001b[0;34m(engine)\u001b[0m\n\u001b[1;32m 31\u001b[0m \u001b[0;31m# get the alembic config file\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 32\u001b[0m \u001b[0mconfig\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mConfig\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mos\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mjoin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mBASEPATH\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'..'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'alembic.ini'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 33\u001b[0;31m \u001b[0mscript_\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mscript\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mScriptDirectory\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfrom_config\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mconfig\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 34\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 35\u001b[0m \u001b[0;31m# connect to database\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/miniconda3/envs/py37/lib/python3.7/site-packages/alembic-1.4.2-py3.7.egg/alembic/script/base.py\u001b[0m in \u001b[0;36mfrom_config\u001b[0;34m(cls, config)\u001b[0m\n\u001b[1;32m 147\u001b[0m \u001b[0mversion_locations\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mversion_locations\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 148\u001b[0m \u001b[0mtimezone\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mconfig\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_main_option\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"timezone\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 149\u001b[0;31m \u001b[0mhook_config\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mconfig\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_section\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"post_write_hooks\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m{\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 150\u001b[0m )\n\u001b[1;32m 151\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/miniconda3/envs/py37/lib/python3.7/site-packages/alembic-1.4.2-py3.7.egg/alembic/script/base.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, dir, file_template, truncate_slug_length, version_locations, sourceless, output_encoding, timezone, hook_config)\u001b[0m\n\u001b[1;32m 68\u001b[0m \u001b[0;34m\"Path doesn't exist: %r. Please use \"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 69\u001b[0m \u001b[0;34m\"the 'init' command to create a new \"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 70\u001b[0;31m \u001b[0;34m\"scripts folder.\"\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0mos\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mabspath\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdir\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 71\u001b[0m )\n\u001b[1;32m 72\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mCommandError\u001b[0m: Path doesn't exist: '/home/mirko/Dropbox/python/metacatalog/docs/source/api/alembic'. Please use the 'init' command to create a new scripts folder." + ] + } + ], + "source": [ + "from metacatalog import api\n", + "\n", + "session = api.connect_database()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "for kw in api.find_keyword(session, value='*TEMPERATURE*'):\n", + " print(kw.full_path)" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/api/api_find_license.html b/api/api_find_license.html new file mode 100644 index 00000000..17fb3287 --- /dev/null +++ b/api/api_find_license.html @@ -0,0 +1,655 @@ + + + + + + + + + + + + Find License — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Find License#

+
+
+metacatalog.api.find_license(return_iterator: typing_extensions.Literal[False] = False) List['License']#
+
+metacatalog.api.find_license(return_iterator: typing_extensions.Literal[True] = False) Query
+

Return one or many license entries from the database on +exact matches. You can use ‘%’ and ‘*’ as wildcards +and prepend a str with ! to invert the filter.

+
+

Changed in version 0.1.8: string matches now allow ‘%’ and ‘*’ wildcards and can +be inverted by prepending !

+
+
+
Parameters:
+
    +
  • session (sqlalchemy.Session) – SQLAlchemy session connected to the database.

  • +
  • id (integer) – Database unique ID of the requested record. Will +return only one record.

  • +
  • title (str) –

    +

    New in version 0.1.8.

    +
    +

    Full title attribute of the requested license(s). +Multiple record return is possible.

    +

  • +
  • short_title (str) – short_title attribute of the requested license(s). +Multiple record return is possible.

  • +
  • by_attribution (bool) – by_attribution attribute of the requested license(s). +Multiple record return is possible.

  • +
  • share_alike (bool) – by_attribution attribute of the requested license(s). +Multiple record return is possible.

  • +
  • commercial_use (bool) – by_attribution attribute of the requested license(s). +Multiple record return is possible.

  • +
  • return_iterator (bool) – If True, an iterator returning the requested objects +instead of the objects themselves is returned.

  • +
+
+
Returns:
+

records – List of matched License instance.

+
+
Return type:
+

list of metacatalog.License

+
+
+
+ +
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/api/api_find_license.ipynb b/api/api_find_license.ipynb new file mode 100644 index 00000000..91e7d791 --- /dev/null +++ b/api/api_find_license.ipynb @@ -0,0 +1,42 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Find License" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.find_license" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/api/api_find_person.html b/api/api_find_person.html new file mode 100644 index 00000000..6c3685e5 --- /dev/null +++ b/api/api_find_person.html @@ -0,0 +1,788 @@ + + + + + + + + + + + + Find person — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Find person#

+
+

Person#

+
+
+metacatalog.api.find_person(return_iterator: typing_extensions.Literal[False] = False) List['Person']#
+
+metacatalog.api.find_person(return_iterator: typing_extensions.Literal[True] = False) Query
+

Return person record on exact matches. Persons can be +identified by id, first_name, last_name, organisation details or associated roles. +Since version 0.2.5 only Persons which have a is_organisation==False +will be returned

+
+

Changed in version 0.1.8: string matches now allow ‘%’ and ‘*’ wildcards and can +be inverted by prepending !

+
+
+

Changed in version 0.2.6: organisation_abbrev is now available.

+
+
+
Parameters:
+
    +
  • session (sqlalchemy.Session) – SQLAlchemy session connected to the database.

  • +
  • id (integer) – Database unique ID of the requested record. Will +return only one record.

  • +
  • uuid (str) –

    +

    New in version 0.2.7.

    +
    +

    Find by version 4 UUID. If uuid is given, all other options +will be ignored.

    +

  • +
  • first_name (str) – First name attribute of the requested person.

  • +
  • last_name (str) – Last name attribute of the requested person.

  • +
  • role (int, str) – Role id or name (exact match) that is associated to +a person. Will most likely return many persons.

  • +
  • organisation_name (str) –

    +

    New in version 0.1.10.

    +
    +

    The name of the head organisation, without department +and group specification.

    +
    +

    Note

    +

    Not all Persons may have an organisation_name.

    +
    +

  • +
  • organisation_abbrev (str) –

    +

    New in version 0.2.6.

    +
    +

    A short abbreviation of the head organisation if +applicable.

    +
    +

    Note

    +

    Not all Persons may have a head organisation

    +
    +

  • +
  • attribution (str) –

    +

    New in version 0.2.8.

    +
    +

    Attribtion recommondation, which is associated +to all datasets, the user is first author of

    +

  • +
  • return_iterator (bool) – If True, an iterator returning the requested objects +instead of the objects themselves is returned.

  • +
+
+
Returns:
+

records – List of matched Person instance.

+
+
Return type:
+

list of metacatalog.Person

+
+
+
+

See also

+

find_organisation

+
+
+ +
+
+

Role#

+
+
+metacatalog.api.find_role(return_iterator: typing_extensions.Literal[False] = False) List['PersonRole']#
+
+metacatalog.api.find_role(return_iterator: typing_extensions.Literal[True] = False) Query
+

Return one person role record on exact matches. +Roles can be identified by id or name.

+
+

Changed in version 0.1.8: string matches now allow ‘%’ and ‘*’ wildcards and can +be inverted by prepending !

+
+
+
Parameters:
+
    +
  • session (sqlalchemy.Session) – SQLAlchemy session connected to the database.

  • +
  • id (integer) – Database unique ID of the requested record. Will +return only one record.

  • +
  • name (str) – name attribute of the requested role.

  • +
  • return_iterator (bool) – If True, an iterator returning the requested objects +instead of the objects themselves is returned.

  • +
+
+
Returns:
+

records – List of matched PersonRole instance.

+
+
Return type:
+

list of metacatalog.PersonRole

+
+
+
+ +
+
+

Associations#

+
+

Note

+

Persons are not directly bound to an Entry. They are related by an association that have to be further described by the type of contribution to the Entry. Therefore, if an Entry is deleted, the Person will persist. +Finally, you can’t search for contributions directly, you either have to find a Person and load the associated entries, or you need to find an entry and load the contributors.

+
+
+
[1]:
+
+
+
from metacatalog import api
+session = api.connect_database()
+
+# get the person
+alfred =api.find_person(session, id=2)[0]
+
+for assoc in alfred.entries:
+    print(assoc)
+
+
+
+
+
+
+
+
+Alfred, E. Neumann <ID=2> as author for Entry <ID=18>
+Alfred, E. Neumann <ID=2> as author for Entry <ID=19>
+Alfred, E. Neumann <ID=2> as author for Entry <ID=20>
+
+
+
+
[2]:
+
+
+
# now starting from the entry side
+entry = api.find_entry(session, id=20)[0]
+
+print('First author:', str(entry.author))
+print('all authors: ', ', '.join([str(a) for a in entry.authors]))
+for assoc in entry.contributors:
+    print(assoc)
+
+
+
+
+
+
+
+
+First author: Alfred, E. Neumann <ID=2>
+all authors:  Alfred, E. Neumann <ID=2>
+Alfred, E. Neumann <ID=2> as author for Entry <ID=20>
+
+
+
+
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/api/api_find_person.ipynb b/api/api_find_person.ipynb new file mode 100644 index 00000000..602a5df3 --- /dev/null +++ b/api/api_find_person.ipynb @@ -0,0 +1,135 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Find person" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Person" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.find_person" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Role" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.find_role" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Associations" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. note::\n", + "\n", + " Persons are not directly bound to an :class:`Entry `. They are related by an association that have to be further described by the type of contribution to the :class:`Entry `. Therefore, if an :class:`Entry ` is deleted, the :class:`Person ` will persist.\n", + " Finally, you can't search for contributions directly, you either have to find a Person and load the associated entries, or you need to find an entry and load the contributors." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Alfred, E. Neumann as author for Entry \n", + "Alfred, E. Neumann as author for Entry \n", + "Alfred, E. Neumann as author for Entry \n" + ] + } + ], + "source": [ + "from metacatalog import api\n", + "session = api.connect_database()\n", + "\n", + "# get the person\n", + "alfred =api.find_person(session, id=2)[0]\n", + "\n", + "for assoc in alfred.entries:\n", + " print(assoc)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "First author: Alfred, E. Neumann \n", + "all authors: Alfred, E. Neumann \n", + "Alfred, E. Neumann as author for Entry \n" + ] + } + ], + "source": [ + "# now starting from the entry side\n", + "entry = api.find_entry(session, id=20)[0]\n", + "\n", + "print('First author:', str(entry.author))\n", + "print('all authors: ', ', '.join([str(a) for a in entry.authors]))\n", + "for assoc in entry.contributors:\n", + " print(assoc)" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/api/api_find_thesaurus.html b/api/api_find_thesaurus.html new file mode 100644 index 00000000..a9f873b7 --- /dev/null +++ b/api/api_find_thesaurus.html @@ -0,0 +1,713 @@ + + + + + + + + + + + + Find Thesaurus — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Find Thesaurus#

+
+

Help#

+
+
+metacatalog.api.find_thesaurus(return_iterator: typing_extensions.Literal[False] = False) List['Thesaurus']#
+
+metacatalog.api.find_thesaurus(return_iterator: typing_extensions.Literal[True] = False) Query
+

Retun one or many thesaurii references from the database +on exact matches. You can use ‘%’ and ‘*’ as wildcards +and prepend a str with ! to invert the filter.

+

..versionadded:: 0.1.10

+
+
Parameters:
+
    +
  • session (sqlalchemy.Session) – SQLAlchemy session connected to the database.

  • +
  • id (integer) – Database unique ID of the requested record. Will +return only one record.

  • +
  • uuid (str) –

    +

    New in version 0.6.6.

    +
    +

    Find by version 4 UUID. If uuid is given, all other options +will be ignored.

    +

  • +
  • name (str) – Short name of the Thesaurus. No wildcard use is possible. +Names are unique, thus no multiple thesaurii will be found.

  • +
  • title (str) – Full title attribute of the requested thesaurii. +Multiple record return is possible.

  • +
  • organisation (str) – Organisation name of the requested thesaurii. +Multiple record return is possible.

  • +
  • description (str) – Description of the thesaurus. The decription field +is optional and some records may not be found.

  • +
  • return_iterator (bool) – If True, an iterator returning the requested objects +instead of the objects themselves is returned.

  • +
+
+
Returns:
+

records – List of matched Thesaurus instances.

+
+
Return type:
+

list of metacatalog.models.Thesaurus

+
+
+
+ +
+
+

Example#

+
+
[1]:
+
+
+
from metacatalog import api
+from pprint import pprint
+
+session = api.connect_database()
+
+
+
+
+
[2]:
+
+
+
gcmd = api.find_thesaurus(session, name='GCMD')[0]
+pprint(gcmd.to_dict())
+
+
+
+
+
+
+
+
+{'description': 'NASA Global Clime change Master Dictionary Science Keywords',
+ 'id': 1,
+ 'name': 'GCMD',
+ 'organisation': 'NASA',
+ 'title': 'NASA/GCMD Earth Science Keywords',
+ 'url': 'https://gcmdservices.gsfc.nasa.gov/kms/concepts/concept_scheme/sciencekeywords/?format=xml',
+ 'uuid': '2e54668d-8fae-429f-a511-efe529420b12'}
+
+
+
+
[3]:
+
+
+
print('Associated Keywords found: %d' % len(gcmd.keywords))
+
+
+
+
+
+
+
+
+Associated Keywords found: 3206
+
+
+
+
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/api/api_find_thesaurus.ipynb b/api/api_find_thesaurus.ipynb new file mode 100644 index 00000000..bb4af8bc --- /dev/null +++ b/api/api_find_thesaurus.ipynb @@ -0,0 +1,109 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Find Thesaurus" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Help" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.find_thesaurus" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from metacatalog import api\n", + "from pprint import pprint\n", + "\n", + "session = api.connect_database()" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'description': 'NASA Global Clime change Master Dictionary Science Keywords',\n", + " 'id': 1,\n", + " 'name': 'GCMD',\n", + " 'organisation': 'NASA',\n", + " 'title': 'NASA/GCMD Earth Science Keywords',\n", + " 'url': 'https://gcmdservices.gsfc.nasa.gov/kms/concepts/concept_scheme/sciencekeywords/?format=xml',\n", + " 'uuid': '2e54668d-8fae-429f-a511-efe529420b12'}\n" + ] + } + ], + "source": [ + "gcmd = api.find_thesaurus(session, name='GCMD')[0]\n", + "pprint(gcmd.to_dict())" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Associated Keywords found: 3206\n" + ] + } + ], + "source": [ + "print('Associated Keywords found: %d' % len(gcmd.keywords))" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/api/api_find_variable.html b/api/api_find_variable.html new file mode 100644 index 00000000..4392572f --- /dev/null +++ b/api/api_find_variable.html @@ -0,0 +1,694 @@ + + + + + + + + + + + + Find Variable — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Find Variable#

+
+

Note

+

The terminology of variable might be reworked, as a variable is not taken in a strict sense of physical parameters, but also compound datasets. This might be reworked in a future version.

+
+

An Entry is defined to be the atomic entitiy in metacatalog, by describing the metadata of one dataset of unique Variable. Each variable has one unit. If another dataset of same variable is uploaded, but uses a different unit, it has to be converted to avoid misinterpretations.

+
+

Variable#

+
+
+metacatalog.api.find_variable(return_iterator: typing_extensions.Literal[False] = False) List['Variable']#
+
+metacatalog.api.find_variable(return_iterator: typing_extensions.Literal[True] = False) Query
+

Return one vriable entry from the database on +exact matches. It makes only sense to set one of the +attributes (id, name, symbol).

+
+

Changed in version 0.1.8: string matches now allow ‘%’ and ‘*’ wildcards and can +be inverted by prepending !

+
+
+
Parameters:
+
    +
  • session (sqlalchemy.Session) – SQLAlchemy session connected to the database.

  • +
  • id (integer) – Database unique ID of the requested record. Will +return only one record.

  • +
  • name (str) – name attribute of the requested variable.

  • +
  • symbol (str) – symbol attribute of the requested variable.

  • +
  • return_iterator (bool) – If True, an iterator returning the requested objects +instead of the objects themselves is returned.

  • +
+
+
Returns:
+

records – List of matched Variable instance.

+
+
Return type:
+

list of metacatalog.Variable

+
+
+
+ +
+
+

Unit#

+
+
+metacatalog.api.find_unit(return_iterator: typing_extensions.Literal[False] = False) List['Unit']#
+
+metacatalog.api.find_unit(return_iterator: typing_extensions.Literal[True] = False) Query
+

Return one unit entry from the database on +exact matches. It makes only sense to set one of the +attributes (id, name, symbol).

+
+

Changed in version 0.1.8: string matches now allow ‘%’ and ‘*’ wildcards and can +be inverted by prepending !

+
+
+
Parameters:
+
    +
  • session (sqlalchemy.Session) – SQLAlchemy session connected to the database.

  • +
  • id (integer) – Database unique ID of the requested record. Will +return only one record.

  • +
  • name (str) – name attribute of the requested unit.

  • +
  • symbol (str) – symbol attribute of the requested unit.

  • +
  • return_iterator (bool) – If True, an iterator returning the requested objects +instead of the objects themselves is returned.

  • +
+
+
Returns:
+

records – List of matched Unit instance.

+
+
Return type:
+

list of metacatalog.Unit

+
+
+
+ +
+
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/api/api_find_variable.ipynb b/api/api_find_variable.ipynb new file mode 100644 index 00000000..4e3a31e1 --- /dev/null +++ b/api/api_find_variable.ipynb @@ -0,0 +1,83 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Find Variable" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. note::\n", + " \n", + " The terminology of *variable* might be reworked, as a variable is not taken in a strict sense of physical parameters, but also compound datasets. This might be reworked in a future version." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "An Entry is defined to be the atomic entitiy in metacatalog, by describing the metadata of **one** dataset of unique *Variable*. Each variable has **one** unit. If another dataset of same variable is uploaded, but uses a different unit, **it has to be converted** to avoid misinterpretations. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Variable" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.find_variable" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Unit" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.find_unit" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/api/api_get_logger.html b/api/api_get_logger.html new file mode 100644 index 00000000..a328bfb7 --- /dev/null +++ b/api/api_get_logger.html @@ -0,0 +1,723 @@ + + + + + + + + + + + + Logging — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Logging#

+

With version 0.3.4, a new logging module is available. metacatalog uses the buildin `logging <https://docs.python.org/3/howto/logging.html>`__ module, by exposing a new Handler. Hence, you can use the standard procedures in Python to set the Log level or add as many other handlers as necessary.

+

The logger can be loaded through the API. On first creation, it needs the session object to the database. Any consecutive call to the logger can be done by logging.getLogger, like you would do it with any other logger. The metacatalog logger is simply named 'metacatalog'.

+
+
[1]:
+
+
+
from metacatalog import api
+
+# get a DB session
+session = api.connect_database()
+
+# get the logger
+logger = api.get_logger(session)
+
+
+
+

From here on, you can use the logger just like any other logging.Logger.

+
+
[2]:
+
+
+
# with default settings, this will be ignored
+logger.info('Ignored message')
+
+# only warning and error will be ignored
+logger.warning('Serious warning raised in the documentation')
+
+
+
+

After first creation, it is also possible to load the logger from logging, i.e. in other files, but within the same Python session

+
+
[5]:
+
+
+
import logging
+
+same_logger = logging.getLogger('metacatalog')
+
+print(logger)
+print(same_logger)
+
+same_logger.error('An error raised in the documentation')
+
+
+
+
+
+
+
+
+<Logger metacatalog (WARNING)>
+<Logger metacatalog (WARNING)>
+
+
+

Finally, use the models.Log class to load the last few messages.

+
+
[7]:
+
+
+
from metacatalog import models
+
+for log in models.Log.load_last(session, n=3):
+    print(log)
+
+
+
+
+
+
+
+
+[ERROR]: An error raised in the documentation (2021-06-07T10:06:08.658991)
+[WARNING]: Serious warning raised in the documentation (2021-06-07T10:02:22.334275)
+[MIGRATION]: Migrated database to 7 using metacatalog==0.3.3 (2021-06-07T07:42:08.686526)
+
+
+

As you can see, the error and warning messages were logged into the database, but the info message was ignored, as the log level is set to warning by default.

+
+
[8]:
+
+
+
logger.setLevel(10) # set to debug
+logger.info('Ignored message - not ignored this time')
+
+
+for log in models.Log.load_last(session, n=3):
+    print(log)
+
+
+
+
+
+
+
+
+[INFO]: Ignored message - not ignored this time (2021-06-07T10:09:01.311257)
+[ERROR]: An error raised in the documentation (2021-06-07T10:06:08.658991)
+[WARNING]: Serious warning raised in the documentation (2021-06-07T10:02:22.334275)
+
+
+
+

Function#

+
+
+metacatalog.api.get_logger(session: Session)#
+

Get a logger that persists to database.

+
+ +
+
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/api/api_get_logger.ipynb b/api/api_get_logger.ipynb new file mode 100644 index 00000000..71317806 --- /dev/null +++ b/api/api_get_logger.ipynb @@ -0,0 +1,180 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Logging\n", + "\n", + "With version `0.3.4`, a new logging module is available. `metacatalog` uses the buildin [`logging`](https://docs.python.org/3/howto/logging.html) module, by exposing a [new Handler](https://docs.python.org/3/library/logging.handlers.html). Hence, you can use the standard procedures in Python to set the Log level or add as many other handlers as necessary.\n", + "\n", + "The logger can be loaded through the API. On first creation, it needs the session object to the database. Any consecutive call to the logger can be done by `logging.getLogger`, like you would do it with any other logger. The `metacatalog` logger is simply named `'metacatalog'`." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from metacatalog import api\n", + "\n", + "# get a DB session\n", + "session = api.connect_database()\n", + "\n", + "# get the logger\n", + "logger = api.get_logger(session)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "From here on, you can use the logger just like any other `logging.Logger`." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# with default settings, this will be ignored\n", + "logger.info('Ignored message')\n", + "\n", + "# only warning and error will be ignored\n", + "logger.warning('Serious warning raised in the documentation')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "After first creation, it is also possible to load the logger from `logging`, i.e. in other files, but within the same Python session" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n" + ] + } + ], + "source": [ + "import logging\n", + "\n", + "same_logger = logging.getLogger('metacatalog')\n", + "\n", + "print(logger)\n", + "print(same_logger)\n", + "\n", + "same_logger.error('An error raised in the documentation')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Finally, use the `models.Log` class to load the last few messages." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[ERROR]: An error raised in the documentation (2021-06-07T10:06:08.658991)\n", + "[WARNING]: Serious warning raised in the documentation (2021-06-07T10:02:22.334275)\n", + "[MIGRATION]: Migrated database to 7 using metacatalog==0.3.3 (2021-06-07T07:42:08.686526)\n" + ] + } + ], + "source": [ + "from metacatalog import models\n", + "\n", + "for log in models.Log.load_last(session, n=3):\n", + " print(log)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As you can see, the error and warning messages were logged into the database, but the info message was ignored, as the log level is set to warning by default." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[INFO]: Ignored message - not ignored this time (2021-06-07T10:09:01.311257)\n", + "[ERROR]: An error raised in the documentation (2021-06-07T10:06:08.658991)\n", + "[WARNING]: Serious warning raised in the documentation (2021-06-07T10:02:22.334275)\n" + ] + } + ], + "source": [ + "logger.setLevel(10) # set to debug\n", + "logger.info('Ignored message - not ignored this time')\n", + "\n", + "\n", + "for log in models.Log.load_last(session, n=3):\n", + " print(log)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Function" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.get_logger" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/api/api_get_uuid.html b/api/api_get_uuid.html new file mode 100644 index 00000000..a3840a0a --- /dev/null +++ b/api/api_get_uuid.html @@ -0,0 +1,742 @@ + + + + + + + + + + + + Get UUID — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Get UUID#

+

The api.get_uuid api endpoint is a utility function for finding database entities across database models.

+
+

Example#

+

We can easily find a specific record by UUID. If you populated the default Keyword, you will find a record of UUID 885735f3-121e-4ca0-ac8b-f37dbc972f03. The get_uuid will figure out, what kind of database model has to be loaded.

+
+
[1]:
+
+
+
from metacatalog import api
+
+session = api.connect_database()
+keyword = api.get_uuid(session, uuid='885735f3-121e-4ca0-ac8b-f37dbc972f03')
+
+print(keyword)
+
+
+
+
+
+
+
+
+EARTH SCIENCE > TERRESTRIAL HYDROSPHERE <ID=25>
+
+
+

While Keyword is already capable of loading all child keywords recursively, we can easily rebuild this function using the get_uuid function. This can be very helpful, when a list of objects needs to be loaded from metacatalog.

+
+
[2]:
+
+
+
import json
+kw_dict = keyword.to_dict(deep=False)
+
+print('Parent object\n' + '#' * 40)
+print(json.dumps(kw_dict, indent=4))
+
+# load the children
+print('\nChildren:\n' + '#' * 40)
+for uuid in kw_dict.get('children', []):
+    print(api.get_uuid(session, uuid=uuid).full_path)
+
+
+
+
+
+
+
+
+Parent object
+########################################
+{
+    "id": 25,
+    "uuid": "885735f3-121e-4ca0-ac8b-f37dbc972f03",
+    "value": "TERRESTRIAL HYDROSPHERE",
+    "path": "EARTH SCIENCE > TERRESTRIAL HYDROSPHERE",
+    "children": [
+        "099ab1ae-f4d2-48cc-be2f-86bd58ffc4ca",
+        "734f8f27-6976-4b67-8794-c7fc79d6161e",
+        "50b8fe04-9149-4b7f-a8b2-b33b1e3aa192",
+        "5debb283-51e4-435e-b2a2-e8e2a977220d",
+        "8c02f5d1-ce86-4bf5-84d5-b3496cdba6ad"
+    ],
+    "thesaurus_id": 1
+}
+
+Children:
+########################################
+EARTH SCIENCE > TERRESTRIAL HYDROSPHERE > GLACIERS/ICE SHEETS
+EARTH SCIENCE > TERRESTRIAL HYDROSPHERE > GROUND WATER
+EARTH SCIENCE > TERRESTRIAL HYDROSPHERE > SNOW/ICE
+EARTH SCIENCE > TERRESTRIAL HYDROSPHERE > SURFACE WATER
+EARTH SCIENCE > TERRESTRIAL HYDROSPHERE > WATER QUALITY/WATER CHEMISTRY
+
+
+
+
+

Funtion#

+
+
+metacatalog.api.get_uuid(as_result: typing_extensions.Literal[False] = False) 'Entry' | 'EntryGroup' | 'Person' | 'Keyword' | None#
+
+metacatalog.api.get_uuid(as_result: typing_extensions.Literal[True] = False) ImmutableResultSet
+

Return the Metacatalog object of given +version 4 UUID. The supported objects are:

+
    +
  • Entry

  • +
  • EntryGroup

  • +
  • Keyword

  • +
  • Person

  • +
+
+

New in version 0.1.13.

+
+
+

Changed in version 0.2.7: Now, also Persons can be +found by UUID

+
+
+

Changed in version 0.7.5: Found Entry and EntryGroup can be returned as ImmutableResultSet +now.

+
+
+
Parameters:
+
    +
  • session (sqlalchemy.Session) – SQLAlchemy session connected to the database.

  • +
  • uuid (str) – Find by version 4 UUID.

  • +
  • as_result (bool) –

    +

    New in version 0.7.5.

    +
    +

    If True, the found Entry or Entrygroup will be merged into a +ImmutableResultSet. +Ignored for matched Keyword and Person. +Defaults to False.

    +

  • +
+
+
Returns:
+

record – Matched Entry, EntryGroup, Keyword, Person or ImmutableResultSet. +If no match was found, None is returned.

+
+
Return type:
+

Entry, EntryGroup, Keyword, Person, ImmutableResultSet, None

+
+
+
+ +
+
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/api/api_get_uuid.ipynb b/api/api_get_uuid.ipynb new file mode 100644 index 00000000..89a01815 --- /dev/null +++ b/api/api_get_uuid.ipynb @@ -0,0 +1,138 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Get UUID\n", + "\n", + "The `api.get_uuid` api endpoint is a utility function for finding database entities across database models." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example\n", + "\n", + "We can easily find a specific record by UUID. If you [populated](api_populate_defaults.ipynb) the default `Keyword`, you will find a record of UUID `885735f3-121e-4ca0-ac8b-f37dbc972f03`. The `get_uuid` will figure out, what kind of database model has to be loaded." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "EARTH SCIENCE > TERRESTRIAL HYDROSPHERE \n" + ] + } + ], + "source": [ + "from metacatalog import api\n", + "\n", + "session = api.connect_database()\n", + "keyword = api.get_uuid(session, uuid='885735f3-121e-4ca0-ac8b-f37dbc972f03')\n", + "\n", + "print(keyword)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "While `Keyword` is already capable of loading all child keywords recursively, we can easily rebuild this function using the `get_uuid` function. This can be very helpful, when a list of objects needs to be loaded from `metacatalog`." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Parent object\n", + "########################################\n", + "{\n", + " \"id\": 25,\n", + " \"uuid\": \"885735f3-121e-4ca0-ac8b-f37dbc972f03\",\n", + " \"value\": \"TERRESTRIAL HYDROSPHERE\",\n", + " \"path\": \"EARTH SCIENCE > TERRESTRIAL HYDROSPHERE\",\n", + " \"children\": [\n", + " \"099ab1ae-f4d2-48cc-be2f-86bd58ffc4ca\",\n", + " \"734f8f27-6976-4b67-8794-c7fc79d6161e\",\n", + " \"50b8fe04-9149-4b7f-a8b2-b33b1e3aa192\",\n", + " \"5debb283-51e4-435e-b2a2-e8e2a977220d\",\n", + " \"8c02f5d1-ce86-4bf5-84d5-b3496cdba6ad\"\n", + " ],\n", + " \"thesaurus_id\": 1\n", + "}\n", + "\n", + "Children:\n", + "########################################\n", + "EARTH SCIENCE > TERRESTRIAL HYDROSPHERE > GLACIERS/ICE SHEETS\n", + "EARTH SCIENCE > TERRESTRIAL HYDROSPHERE > GROUND WATER\n", + "EARTH SCIENCE > TERRESTRIAL HYDROSPHERE > SNOW/ICE\n", + "EARTH SCIENCE > TERRESTRIAL HYDROSPHERE > SURFACE WATER\n", + "EARTH SCIENCE > TERRESTRIAL HYDROSPHERE > WATER QUALITY/WATER CHEMISTRY\n" + ] + } + ], + "source": [ + "import json\n", + "kw_dict = keyword.to_dict(deep=False)\n", + "\n", + "print('Parent object\\n' + '#' * 40)\n", + "print(json.dumps(kw_dict, indent=4))\n", + "\n", + "# load the children\n", + "print('\\nChildren:\\n' + '#' * 40)\n", + "for uuid in kw_dict.get('children', []):\n", + " print(api.get_uuid(session, uuid=uuid).full_path)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Funtion" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.get_uuid" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/api/api_populate_defaults.html b/api/api_populate_defaults.html new file mode 100644 index 00000000..80c77c2f --- /dev/null +++ b/api/api_populate_defaults.html @@ -0,0 +1,652 @@ + + + + + + + + + + + + Populate defaults — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Populate defaults#

+
+
+metacatalog.api.populate_defaults(session: Session, ignore_tables: List[str] = [], bump_sequences: int = 10000) None#
+

Populates many lookup and auxiliary tables with useful default +information. The actual data is read from a data subdirectory +inside this module and can therefore easily be adapted. +Any table name supplied in ignore_tables will be omitted.

+
+

Warning

+

Do only ignore tables if you know what you are doing. +There are dependencies (like variables, units and keywords) and +some of the API is depending on some defaults (like datatypes).

+
+

As of now, the following tables can be pre-polulated:

+
    +
  • datasource_types

  • +
  • datatypes

  • +
  • entrygroup_types

  • +
  • keywords

  • +
  • licenses

  • +
  • person_roles

  • +
  • thesaurus

  • +
  • units

  • +
  • variables

  • +
+
+
Parameters:
+
    +
  • session (sqlalchemy.Session) – Session instance connected to the database.

  • +
  • ignore_tables (list) – List of tables to be omitted. Be aware that the actual +table name in the database has to be supplied, not the +name of the model in Python.

  • +
  • bump_sequences (int, None) – If integer (default), the primary key sequences will be +set to this value. It is recommended to use a high value like +the default 10,000 to allow for future including of new +pre-populated values. Otherwise, these might have integrity +conflicts with your database objects.

  • +
+
+
+
+ +
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/api/api_populate_defaults.ipynb b/api/api_populate_defaults.ipynb new file mode 100644 index 00000000..57038277 --- /dev/null +++ b/api/api_populate_defaults.ipynb @@ -0,0 +1,42 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Populate defaults" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.populate_defaults" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/api/api_show_attributes.html b/api/api_show_attributes.html new file mode 100644 index 00000000..d2ff9f9d --- /dev/null +++ b/api/api_show_attributes.html @@ -0,0 +1,679 @@ + + + + + + + + + + + + Show Attributes — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Show Attributes#

+
+

Note

+

The show_ endpoints are meant to be used by admins to build functionality into their metacatalog deployment. These endpoints are not really helpful for the end-user.

+
+
+

Help#

+
+
+metacatalog.api.show_attributes(table_name, add_type=False)#
+

Returns a list of available attributes on the given table. +The table_name has to match the actual table in the database.

+
+
Parameters:
+
    +
  • table_name (str) – Name of the table the attributes are requested for.

  • +
  • add_type (bool) – If True, a list of tuples will be returned. The tuple will +contain (column_table, colmun_data_type)

  • +
+
+
Returns:
+

attributes – List of attributes available on the requested table.

+
+
Return type:
+

list

+
+
+
+ +
+
+

Example#

+
+

Note

+

api.show_attribute is one of the very few api endpoints that don’t need a session to connect to the database, as it is using the models to collect the information requested. Thus, you don’t need actual database connection to use this function.

+
+
+
[1]:
+
+
+
from metacatalog import api
+
+
+
+
+
[4]:
+
+
+
api.show_attributes(table_name='persons', add_type=True)
+
+
+
+
+
[4]:
+
+
+
+
+[('id', Integer()),
+ ('first_name', String(length=128)),
+ ('last_name', String(length=128)),
+ ('affiliation', String(length=1024))]
+
+
+
+
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/api/api_show_attributes.ipynb b/api/api_show_attributes.ipynb new file mode 100644 index 00000000..240b61e2 --- /dev/null +++ b/api/api_show_attributes.ipynb @@ -0,0 +1,110 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Show Attributes" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. note::\n", + "\n", + " The ``show_`` endpoints are meant to be used by admins to build functionality into their metacatalog deployment. These endpoints are not really helpful for the end-user." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Help" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.show_attributes" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. note::\n", + "\n", + " ``api.show_attribute`` is one of the very few api endpoints that don't need a session to connect to the database, as it is using the models to collect the information requested. Thus, you don't need actual database connection to use this function." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from metacatalog import api" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('id', Integer()),\n", + " ('first_name', String(length=128)),\n", + " ('last_name', String(length=128)),\n", + " ('affiliation', String(length=1024))]" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "api.show_attributes(table_name='persons', add_type=True)" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/api/api_show_records.html b/api/api_show_records.html new file mode 100644 index 00000000..ad3b8a41 --- /dev/null +++ b/api/api_show_records.html @@ -0,0 +1,863 @@ + + + + + + + + + + + + Show Records — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Show Records#

+
+

Note

+

The show_ endpoints are meant to be used by admins to build functionality into their metacatalog deployment. These endpoints are not really helpful for the end-user.

+
+
+

Help#

+
+
+metacatalog.api.show_records(session, table_name, limit=None, where=None, as_dict=True)#
+

Returns a list or dictionary of raw table contents in +the database.

+
+
Parameters:
+
    +
  • session (sqlalchemy.Session) – SQLAlchemy session connected to the database.

  • +
  • table_name (str) – Name of the table the records are requested for.

  • +
  • limit (int) – Positive integer to limit the requested data. +If None, no limit will be set

  • +
  • where (str) – Possible raw SQL WHERE clause. If None, the +output will not be filtered.

  • +
  • as_dict (bool) – If True, each records will be returned as a dict, +else as a tuple with the columns ordered as +defined in the Model

  • +
+
+
Returns:
+

records – List of dicts or tuples representing the records.

+
+
Return type:
+

list

+
+
+
+ +
+
+

Example#

+
+

Note

+

This endpoint is not meant to be used to load metadata from the database in a production scenario. You can use it for backups or during development. It will return the raw table contents. To actually use the metadata, use the metacatalog.models classes or the api.find_* endpoints.

+
+
+
[1]:
+
+
+
from metacatalog import api
+
+session = api.connect_database()
+
+
+
+
+
[3]:
+
+
+
api.show_records(session, table_name='entries', limit=10)
+
+
+
+
+
[3]:
+
+
+
+
+[{'id': 1,
+  'title': 'Sap Flow - Hohes Holz - Tree 022',
+  'abstract': 'Sap flow tree 022- 20mm- east',
+  'external_id': '212',
+  'location': '01010000006806F1811D0B4A4002452C62D8712640',
+  'geom': None,
+  'creation': None,
+  'end': None,
+  'version': 1,
+  'latest_version_id': None,
+  'comment': 'not yet converted',
+  'license_id': 2,
+  'variable_id': 14,
+  'datasource_id': 1,
+  'embargo': True,
+  'embargo_end': datetime.datetime(2022, 5, 15, 12, 43, 57, 712110),
+  'publication': datetime.datetime(2020, 5, 15, 12, 43, 57, 712195),
+  'lastUpdate': datetime.datetime(2020, 5, 15, 13, 17, 47, 540713)},
+ {'id': 2,
+  'title': 'Sap Flow - Hohes Holz - Tree 022',
+  'abstract': 'Sap flow tree 022- 20mm- north-west',
+  'external_id': '214',
+  'location': '01010000006806F1811D0B4A4002452C62D8712640',
+  'geom': None,
+  'creation': None,
+  'end': None,
+  'version': 1,
+  'latest_version_id': None,
+  'comment': 'not yet converted',
+  'license_id': 2,
+  'variable_id': 14,
+  'datasource_id': 2,
+  'embargo': True,
+  'embargo_end': datetime.datetime(2022, 5, 15, 12, 43, 57, 749350),
+  'publication': datetime.datetime(2020, 5, 15, 12, 43, 57, 749407),
+  'lastUpdate': datetime.datetime(2020, 5, 15, 13, 17, 47, 565270)},
+ {'id': 3,
+  'title': 'Sap Flow - Hohes Holz - Tree 029',
+  'abstract': 'Sap flow tree 029- 20mm- north',
+  'external_id': '246',
+  'location': '0101000000761C3F541A0B4A4099F04BFDBC712640',
+  'geom': None,
+  'creation': None,
+  'end': None,
+  'version': 1,
+  'latest_version_id': None,
+  'comment': 'not yet converted',
+  'license_id': 2,
+  'variable_id': 14,
+  'datasource_id': 3,
+  'embargo': True,
+  'embargo_end': datetime.datetime(2022, 5, 15, 12, 43, 57, 770584),
+  'publication': datetime.datetime(2020, 5, 15, 12, 43, 57, 770633),
+  'lastUpdate': datetime.datetime(2020, 5, 15, 13, 17, 47, 576064)},
+ {'id': 4,
+  'title': 'Sap Flow - Hohes Holz - Tree 048',
+  'abstract': 'Sap flow tree 048- 20mm- north 2015',
+  'external_id': '269',
+  'location': '01010000008BA8893E1F0B4A400B24287E8C712640',
+  'geom': None,
+  'creation': None,
+  'end': None,
+  'version': 1,
+  'latest_version_id': None,
+  'comment': 'nan',
+  'license_id': 2,
+  'variable_id': 14,
+  'datasource_id': 4,
+  'embargo': True,
+  'embargo_end': datetime.datetime(2022, 5, 15, 12, 43, 57, 789248),
+  'publication': datetime.datetime(2020, 5, 15, 12, 43, 57, 789294),
+  'lastUpdate': datetime.datetime(2020, 5, 15, 13, 17, 47, 587474)},
+ {'id': 5,
+  'title': 'Sap Flow - Hohes Holz - Tree 050',
+  'abstract': 'Sap flow tree 050- 20mm- east',
+  'external_id': '275',
+  'location': '0101000000C11DA8531E0B4A404A26A77686712640',
+  'geom': None,
+  'creation': None,
+  'end': None,
+  'version': 1,
+  'latest_version_id': None,
+  'comment': 'nan',
+  'license_id': 2,
+  'variable_id': 14,
+  'datasource_id': 5,
+  'embargo': True,
+  'embargo_end': datetime.datetime(2022, 5, 15, 12, 43, 57, 807256),
+  'publication': datetime.datetime(2020, 5, 15, 12, 43, 57, 807302),
+  'lastUpdate': datetime.datetime(2020, 5, 15, 13, 17, 47, 598154)},
+ {'id': 6,
+  'title': 'Sap Flow - Hohes Holz - Tree 056',
+  'abstract': 'Sap flow tree 056- 20mm- north',
+  'external_id': '282',
+  'location': '01010000003FA7203F1B0B4A4036E50AEF72712640',
+  'geom': None,
+  'creation': None,
+  'end': None,
+  'version': 1,
+  'latest_version_id': None,
+  'comment': 'nan',
+  'license_id': 2,
+  'variable_id': 14,
+  'datasource_id': 6,
+  'embargo': True,
+  'embargo_end': datetime.datetime(2022, 5, 15, 12, 43, 57, 825347),
+  'publication': datetime.datetime(2020, 5, 15, 12, 43, 57, 825393),
+  'lastUpdate': datetime.datetime(2020, 5, 15, 13, 17, 47, 608043)},
+ {'id': 7,
+  'title': 'Sap Flow - Hohes Holz - Tree 057',
+  'abstract': 'Sap flow tree 057- 20mm- north',
+  'external_id': '292',
+  'location': '01010000009335EA211A0B4A40D6743DD175712640',
+  'geom': None,
+  'creation': None,
+  'end': None,
+  'version': 1,
+  'latest_version_id': None,
+  'comment': 'nan',
+  'license_id': 2,
+  'variable_id': 14,
+  'datasource_id': 7,
+  'embargo': True,
+  'embargo_end': datetime.datetime(2022, 5, 15, 12, 43, 57, 844292),
+  'publication': datetime.datetime(2020, 5, 15, 12, 43, 57, 844339),
+  'lastUpdate': datetime.datetime(2020, 5, 15, 13, 17, 47, 618236)},
+ {'id': 8,
+  'title': 'Sap Flow - Hohes Holz - Tree 058',
+  'abstract': 'Sap flow tree 058- 20mm- east-north-east',
+  'external_id': '306',
+  'location': '0101000000A46C91B41B0B4A40807EDFBF79712640',
+  'geom': None,
+  'creation': None,
+  'end': None,
+  'version': 1,
+  'latest_version_id': None,
+  'comment': 'nan',
+  'license_id': 2,
+  'variable_id': 14,
+  'datasource_id': 8,
+  'embargo': True,
+  'embargo_end': datetime.datetime(2022, 5, 15, 12, 43, 57, 863693),
+  'publication': datetime.datetime(2020, 5, 15, 12, 43, 57, 863740),
+  'lastUpdate': datetime.datetime(2020, 5, 15, 13, 17, 47, 627976)},
+ {'id': 9,
+  'title': 'Sap Flow - Hohes Holz - Tree 106',
+  'abstract': 'Sap flow tree 106 - 20mm - east - commertial',
+  'external_id': '326',
+  'location': '0101000000A87004A9140B4A40B79C4B7155712640',
+  'geom': None,
+  'creation': None,
+  'end': None,
+  'version': 1,
+  'latest_version_id': None,
+  'comment': 'commertial sensor in 20mm depth',
+  'license_id': 2,
+  'variable_id': 14,
+  'datasource_id': 9,
+  'embargo': True,
+  'embargo_end': datetime.datetime(2022, 5, 15, 12, 43, 57, 884135),
+  'publication': datetime.datetime(2020, 5, 15, 12, 43, 57, 884185),
+  'lastUpdate': datetime.datetime(2020, 5, 15, 13, 17, 47, 637880)},
+ {'id': 10,
+  'title': 'Sap Flow - Hohes Holz - Tree 108',
+  'abstract': 'Sap flow tree 108 - 20mm - east-north-east - commertial',
+  'external_id': '333',
+  'location': '0101000000D828EB37130B4A403C855CA967712640',
+  'geom': None,
+  'creation': None,
+  'end': None,
+  'version': 1,
+  'latest_version_id': None,
+  'comment': 'commertial sensor in 20mm depth',
+  'license_id': 2,
+  'variable_id': 14,
+  'datasource_id': 10,
+  'embargo': True,
+  'embargo_end': datetime.datetime(2022, 5, 15, 12, 43, 57, 904529),
+  'publication': datetime.datetime(2020, 5, 15, 12, 43, 57, 904578),
+  'lastUpdate': datetime.datetime(2020, 5, 15, 13, 17, 47, 647861)}]
+
+
+
+
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/api/api_show_records.ipynb b/api/api_show_records.ipynb new file mode 100644 index 00000000..e2c4b999 --- /dev/null +++ b/api/api_show_records.ipynb @@ -0,0 +1,290 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Show Records" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. note::\n", + "\n", + " The ``show_`` endpoints are meant to be used by admins to build functionality into their metacatalog deployment. These endpoints are not really helpful for the end-user." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Help" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. autofunction:: metacatalog.api.show_records" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. note::\n", + "\n", + " This endpoint is not meant to be used to load metadata from the database in a production scenario. You can use it for backups or during development. It will return the raw table contents. To actually use the metadata, use the ``metacatalog.models`` classes or the ``api.find_*`` endpoints." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from metacatalog import api\n", + "\n", + "session = api.connect_database()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'id': 1,\n", + " 'title': 'Sap Flow - Hohes Holz - Tree 022',\n", + " 'abstract': 'Sap flow tree 022- 20mm- east',\n", + " 'external_id': '212',\n", + " 'location': '01010000006806F1811D0B4A4002452C62D8712640',\n", + " 'geom': None,\n", + " 'creation': None,\n", + " 'end': None,\n", + " 'version': 1,\n", + " 'latest_version_id': None,\n", + " 'comment': 'not yet converted',\n", + " 'license_id': 2,\n", + " 'variable_id': 14,\n", + " 'datasource_id': 1,\n", + " 'embargo': True,\n", + " 'embargo_end': datetime.datetime(2022, 5, 15, 12, 43, 57, 712110),\n", + " 'publication': datetime.datetime(2020, 5, 15, 12, 43, 57, 712195),\n", + " 'lastUpdate': datetime.datetime(2020, 5, 15, 13, 17, 47, 540713)},\n", + " {'id': 2,\n", + " 'title': 'Sap Flow - Hohes Holz - Tree 022',\n", + " 'abstract': 'Sap flow tree 022- 20mm- north-west',\n", + " 'external_id': '214',\n", + " 'location': '01010000006806F1811D0B4A4002452C62D8712640',\n", + " 'geom': None,\n", + " 'creation': None,\n", + " 'end': None,\n", + " 'version': 1,\n", + " 'latest_version_id': None,\n", + " 'comment': 'not yet converted',\n", + " 'license_id': 2,\n", + " 'variable_id': 14,\n", + " 'datasource_id': 2,\n", + " 'embargo': True,\n", + " 'embargo_end': datetime.datetime(2022, 5, 15, 12, 43, 57, 749350),\n", + " 'publication': datetime.datetime(2020, 5, 15, 12, 43, 57, 749407),\n", + " 'lastUpdate': datetime.datetime(2020, 5, 15, 13, 17, 47, 565270)},\n", + " {'id': 3,\n", + " 'title': 'Sap Flow - Hohes Holz - Tree 029',\n", + " 'abstract': 'Sap flow tree 029- 20mm- north',\n", + " 'external_id': '246',\n", + " 'location': '0101000000761C3F541A0B4A4099F04BFDBC712640',\n", + " 'geom': None,\n", + " 'creation': None,\n", + " 'end': None,\n", + " 'version': 1,\n", + " 'latest_version_id': None,\n", + " 'comment': 'not yet converted',\n", + " 'license_id': 2,\n", + " 'variable_id': 14,\n", + " 'datasource_id': 3,\n", + " 'embargo': True,\n", + " 'embargo_end': datetime.datetime(2022, 5, 15, 12, 43, 57, 770584),\n", + " 'publication': datetime.datetime(2020, 5, 15, 12, 43, 57, 770633),\n", + " 'lastUpdate': datetime.datetime(2020, 5, 15, 13, 17, 47, 576064)},\n", + " {'id': 4,\n", + " 'title': 'Sap Flow - Hohes Holz - Tree 048',\n", + " 'abstract': 'Sap flow tree 048- 20mm- north 2015',\n", + " 'external_id': '269',\n", + " 'location': '01010000008BA8893E1F0B4A400B24287E8C712640',\n", + " 'geom': None,\n", + " 'creation': None,\n", + " 'end': None,\n", + " 'version': 1,\n", + " 'latest_version_id': None,\n", + " 'comment': 'nan',\n", + " 'license_id': 2,\n", + " 'variable_id': 14,\n", + " 'datasource_id': 4,\n", + " 'embargo': True,\n", + " 'embargo_end': datetime.datetime(2022, 5, 15, 12, 43, 57, 789248),\n", + " 'publication': datetime.datetime(2020, 5, 15, 12, 43, 57, 789294),\n", + " 'lastUpdate': datetime.datetime(2020, 5, 15, 13, 17, 47, 587474)},\n", + " {'id': 5,\n", + " 'title': 'Sap Flow - Hohes Holz - Tree 050',\n", + " 'abstract': 'Sap flow tree 050- 20mm- east',\n", + " 'external_id': '275',\n", + " 'location': '0101000000C11DA8531E0B4A404A26A77686712640',\n", + " 'geom': None,\n", + " 'creation': None,\n", + " 'end': None,\n", + " 'version': 1,\n", + " 'latest_version_id': None,\n", + " 'comment': 'nan',\n", + " 'license_id': 2,\n", + " 'variable_id': 14,\n", + " 'datasource_id': 5,\n", + " 'embargo': True,\n", + " 'embargo_end': datetime.datetime(2022, 5, 15, 12, 43, 57, 807256),\n", + " 'publication': datetime.datetime(2020, 5, 15, 12, 43, 57, 807302),\n", + " 'lastUpdate': datetime.datetime(2020, 5, 15, 13, 17, 47, 598154)},\n", + " {'id': 6,\n", + " 'title': 'Sap Flow - Hohes Holz - Tree 056',\n", + " 'abstract': 'Sap flow tree 056- 20mm- north',\n", + " 'external_id': '282',\n", + " 'location': '01010000003FA7203F1B0B4A4036E50AEF72712640',\n", + " 'geom': None,\n", + " 'creation': None,\n", + " 'end': None,\n", + " 'version': 1,\n", + " 'latest_version_id': None,\n", + " 'comment': 'nan',\n", + " 'license_id': 2,\n", + " 'variable_id': 14,\n", + " 'datasource_id': 6,\n", + " 'embargo': True,\n", + " 'embargo_end': datetime.datetime(2022, 5, 15, 12, 43, 57, 825347),\n", + " 'publication': datetime.datetime(2020, 5, 15, 12, 43, 57, 825393),\n", + " 'lastUpdate': datetime.datetime(2020, 5, 15, 13, 17, 47, 608043)},\n", + " {'id': 7,\n", + " 'title': 'Sap Flow - Hohes Holz - Tree 057',\n", + " 'abstract': 'Sap flow tree 057- 20mm- north',\n", + " 'external_id': '292',\n", + " 'location': '01010000009335EA211A0B4A40D6743DD175712640',\n", + " 'geom': None,\n", + " 'creation': None,\n", + " 'end': None,\n", + " 'version': 1,\n", + " 'latest_version_id': None,\n", + " 'comment': 'nan',\n", + " 'license_id': 2,\n", + " 'variable_id': 14,\n", + " 'datasource_id': 7,\n", + " 'embargo': True,\n", + " 'embargo_end': datetime.datetime(2022, 5, 15, 12, 43, 57, 844292),\n", + " 'publication': datetime.datetime(2020, 5, 15, 12, 43, 57, 844339),\n", + " 'lastUpdate': datetime.datetime(2020, 5, 15, 13, 17, 47, 618236)},\n", + " {'id': 8,\n", + " 'title': 'Sap Flow - Hohes Holz - Tree 058',\n", + " 'abstract': 'Sap flow tree 058- 20mm- east-north-east',\n", + " 'external_id': '306',\n", + " 'location': '0101000000A46C91B41B0B4A40807EDFBF79712640',\n", + " 'geom': None,\n", + " 'creation': None,\n", + " 'end': None,\n", + " 'version': 1,\n", + " 'latest_version_id': None,\n", + " 'comment': 'nan',\n", + " 'license_id': 2,\n", + " 'variable_id': 14,\n", + " 'datasource_id': 8,\n", + " 'embargo': True,\n", + " 'embargo_end': datetime.datetime(2022, 5, 15, 12, 43, 57, 863693),\n", + " 'publication': datetime.datetime(2020, 5, 15, 12, 43, 57, 863740),\n", + " 'lastUpdate': datetime.datetime(2020, 5, 15, 13, 17, 47, 627976)},\n", + " {'id': 9,\n", + " 'title': 'Sap Flow - Hohes Holz - Tree 106',\n", + " 'abstract': 'Sap flow tree 106 - 20mm - east - commertial',\n", + " 'external_id': '326',\n", + " 'location': '0101000000A87004A9140B4A40B79C4B7155712640',\n", + " 'geom': None,\n", + " 'creation': None,\n", + " 'end': None,\n", + " 'version': 1,\n", + " 'latest_version_id': None,\n", + " 'comment': 'commertial sensor in 20mm depth',\n", + " 'license_id': 2,\n", + " 'variable_id': 14,\n", + " 'datasource_id': 9,\n", + " 'embargo': True,\n", + " 'embargo_end': datetime.datetime(2022, 5, 15, 12, 43, 57, 884135),\n", + " 'publication': datetime.datetime(2020, 5, 15, 12, 43, 57, 884185),\n", + " 'lastUpdate': datetime.datetime(2020, 5, 15, 13, 17, 47, 637880)},\n", + " {'id': 10,\n", + " 'title': 'Sap Flow - Hohes Holz - Tree 108',\n", + " 'abstract': 'Sap flow tree 108 - 20mm - east-north-east - commertial',\n", + " 'external_id': '333',\n", + " 'location': '0101000000D828EB37130B4A403C855CA967712640',\n", + " 'geom': None,\n", + " 'creation': None,\n", + " 'end': None,\n", + " 'version': 1,\n", + " 'latest_version_id': None,\n", + " 'comment': 'commertial sensor in 20mm depth',\n", + " 'license_id': 2,\n", + " 'variable_id': 14,\n", + " 'datasource_id': 10,\n", + " 'embargo': True,\n", + " 'embargo_end': datetime.datetime(2022, 5, 15, 12, 43, 57, 904529),\n", + " 'publication': datetime.datetime(2020, 5, 15, 12, 43, 57, 904578),\n", + " 'lastUpdate': datetime.datetime(2020, 5, 15, 13, 17, 47, 647861)}]" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "api.show_records(session, table_name='entries', limit=10)" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/cli/cli.html b/cli/cli.html new file mode 100644 index 00000000..714c7367 --- /dev/null +++ b/cli/cli.html @@ -0,0 +1,665 @@ + + + + + + + + + + + + CLI — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

CLI#

+
+
+
+

Command Line Interface#

+

The setup.py used on installing metacatalog installed a command line script +which is also registered in the current anaconda environment (if used any). Therefore, just running

+
metacatalog
+
+
+

should work fine. +If you however experience problems, which seem to happen on Windows quite frequently, +there is also a CLI-like entrypoint into metacatalog. You can use it like:

+
python -m metacatalog
+
+
+

Under the hood, exactly the same script gets executed.

+
+
+

Builtin Help#

+

Like with most cli, you can pass the -h flag to any command and sub-command to show +the builtin help for the current command.

+
metacatalog -h
+
+
+

renders:

+
usage: metacatalog [-h] {create,populate,init,connection,find,show,add} ...
+
+MetaCatalog management CLI
+
+optional arguments:
+    -h, --help            show this help message and exit
+
+Commands:
+    CLI commands
+
+    {create,populate,init,connection,find,show,add}
+    create              Create a new Metacatalog instance.
+    populate            Populate the database with default auxiliary data.
+    init                Runs the create and and the populate command.
+    connection          Manage stored connections
+    find                Find records in the database on exact matches.
+    show                Show database structure or records.
+    add                 Add new records to the database. Has to be combined
+                        with one of the data origin flags.
+
+
+
+
+

Command Overview#

+ +
+
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/cli/cli_add.html b/cli/cli_add.html new file mode 100644 index 00000000..707c1d90 --- /dev/null +++ b/cli/cli_add.html @@ -0,0 +1,861 @@ + + + + + + + + + + + + Add command — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Add command#

+
+

Help#

+

The help text for the add subcommand can be shown by passing the -h flag.

+
+
[1]:
+
+
+
%%bash
+metacatalog add -h
+
+
+
+
+
+
+
+
+usage: metacatalog add [-h] [--version] [--connection CONNECTION] [--verbose]
+                       [--quiet] [--dev] [--logfile LOGFILE] [--csv CSV]
+                       [--txt TXT] [--json JSON]
+                       entity
+
+positional arguments:
+  entity                Name of the record entity to be added.
+
+optional arguments:
+  -h, --help            show this help message and exit
+  --version, -v         Returns the module version
+  --connection CONNECTION, -C CONNECTION
+                        Connection string to the database instance.Follows the
+                        syntax: driver://user:password@host:port/database
+  --verbose, -V         Activate extended output.
+  --quiet, -q           Suppress any kind of output.
+  --dev                 Development mode. Unexpected errors will not be
+                        handled and the full traceback is printed to the
+                        screen.
+  --logfile LOGFILE     If a file is given, output will be written to that
+                        file instead of printed to StdOut.
+  --csv CSV             Data Origin Flag. Pass a CSV filename or content
+                        containing the data. Column header have to match the
+                        ADD API keywords.
+  --txt TXT             Data Origin Flag. Pass a text filename or content
+                        containing whitespace separated key=value pairs where
+                        key has to match the ADD API keywords. If used
+                        directly remember to quote accordingly.
+  --json JSON           Data Origin Flag. Pass a JSON filename or content
+                        containing the data. Must contain a list of objects
+                        matchin the ADD API keywords.
+
+
+
+
+

Prerequisites#

+

The add command assumes that either `create <cli_create.ipynb>`__ and `populate <cli_populate.ipynb>`__ or `init <cli_init.ipynb>`__ were executed successfully.

+
+
+

Usage#

+
+

entity#

+

The add command has one positional argument entity that has to be provided. This is the name of the record entitiy that should be added. There is a dictionary in metacatalog that maps enitity names to database models:

+
+
[2]:
+
+
+
from metacatalog.api._mapping import ENTITY_MAPPING
+from pprint import pprint
+pprint(ENTITY_MAPPING)
+
+
+
+
+
+
+
+
+{'author': <class 'metacatalog.models.person.Person'>,
+ 'contributor': <class 'metacatalog.models.person.Person'>,
+ 'datasource': <class 'metacatalog.models.datasource.DataSource'>,
+ 'datasource_type': <class 'metacatalog.models.datasource.DataSourceType'>,
+ 'datasourcetype': <class 'metacatalog.models.datasource.DataSourceType'>,
+ 'entry': <class 'metacatalog.models.entry.Entry'>,
+ 'keyword': <class 'metacatalog.models.keyword.Keyword'>,
+ 'license': <class 'metacatalog.models.license.License'>,
+ 'person': <class 'metacatalog.models.person.Person'>,
+ 'person_role': <class 'metacatalog.models.person.PersonRole'>,
+ 'personrole': <class 'metacatalog.models.person.PersonRole'>,
+ 'unit': <class 'metacatalog.models.variable.Unit'>,
+ 'variable': <class 'metacatalog.models.variable.Variable'>}
+
+
+

Many entities map to the same model. This is either due to different spelling, or because the API creates database records in different contexts. E.g. the API forces the user to pass at least one person as the first author of an Entry on creation. The contributors are optional and can be added if applicable. All persons will, however, be saved into the same table.

+
+

Warning

+

The CLI is operating at a much lower level than the Python API. Many semantical workflows which add data to the database include way more individual steps using the CLI.

+
+
+
+

connection#

+

In case no default connection was created and saved, you have to supply a connection string to the database using the --connection flag. See `connection <cli_connection.ipynb>`__ command.

+
+
+

passing arguments#

+

Obviously, you need to pass the actual metadata, that should be stored in metacatalog. There are three data origin flags available:

+
    +
  • --csv - comma separated

  • +
  • --txt - key=value pairs

  • +
  • json - JSON

  • +
+

All three flags accept either a filename (including path) to a file in the specified format, or the content itself. Instead of creating a file and passing the filename:

+
name,symbol
+foo,F
+Bar,B
+
+
+

you can can also use the flag like: --csv 'name,symbol\nnfoo,F\nbar,B'. This might be the easier approach if only one or two records are added.

+
+

Note

+

You can inspect entities using the show command.

+
+
+
+
+

Operations#

+
+

Note

+

This section assumes that you are familiar with the metacatalog data model. As the CLI just uses the Python API under the hood, you will have to refer to the API documentation for a full reference.

+
+

Most metadata creation task cannot be done with one call to add. Furthermore, some entities relate to records, that have to be added in the first place, to not violate relation constraints. A prior example is that a person has to exist in the database, before it can be placed as an author.

+

A typical workflow is to add missing lookup data, which includes variables,units,licenses,keywords and details. Then, you create all persons involved. Finally, the metadata Entry can be added. For most lookup data, a 1:n relation is modelled and you can pass anything accepted by the find api or the ID. keywords and persons are, however, modelled in a m:n relation, which has to be specified in a second step.

+
+

Warning

+

The details entity is not reflected in the CLI or API yet.

+

The usage of passing other identifiers to find than the ID, is experimental and not fully functions. It might also change in a future release

+
+
+
+

Example#

+

The following example should illustrate a workflow for adding new meta-data. At first we add a unit of awesomeness and a variable of awesome - because most of our data is awesome.

+
+
[8]:
+
+
+
%%bash
+metacatalog add unit --csv 'name,symbol,si\nawesomeness,a,m'
+
+
+
+
+
+
+
+
+Using session: Engine(postgresql://postgres:***@localhost:5432/metacatalog)
+Added 1 unit records.
+Done.
+
+
+
+
[13]:
+
+
+
%%bash
+#metacatalog show attributes variables
+metacatalog add variable --csv 'name,symbol,unit\nawesome,A,awesomeness'
+
+
+
+
+
+
+
+
+Using session: Engine(postgresql://postgres:***@localhost:5432/metacatalog)
+Added 1 variable records.
+Done.
+
+
+

Here, we passed the new newly created unit name to the add variable endpoint.

+
+
[19]:
+
+
+
%%bash
+metacatalog add person --json '[{"first_name": "Alfred, E.", "last_name": "Neumann", "affiliation": "Institute of Awesomeness"}]'
+
+
+
+
+
+
+
+
+Using session: Engine(postgresql://postgres:***@localhost:5432/metacatalog)
+Added 1 person records.
+Done.
+
+
+

Finally, we can create the Entries of Alfred, E.’s data from a json file:

+
+
[14]:
+
+
+
import json
+
+meta = dict(
+    title="Alfred data",
+    abstract="A dummy test entry, which Alfred created",
+    location=(37.422051, -122.084615),
+    license=2,
+    embargo=True,
+    variable="awesome",
+    author="Neumann"
+)
+with open('alfred.json', 'w') as js:
+    json.dump([meta], js)
+
+
+
+
+
[15]:
+
+
+
%%bash
+metacatalog add entry --json alfred.json
+rm alfred.json
+
+
+
+
+
+
+
+
+Using session: Engine(postgresql://postgres:***@localhost:5432/metacatalog)
+Added 1 entry records.
+Done.
+
+
+

And finally, using the `find <cli_find.ipynb>`__ and `show <cli_show.ipynb>`__ command we can inspect the newly created entry:

+
+
[21]:
+
+
+
%%bash
+metacatalog find entry --by title "Alfred data"
+metacatalog show records entries --where "id=20" -T
+
+
+
+
+
+
+
+
+Using session: Engine(postgresql://postgres:***@localhost:5432/metacatalog)
+<ID=20 Alfred data [awesome] >
+Using session: Engine(postgresql://postgres:***@localhost:5432/metacatalog)
+  id  title           abstract         external_id    location         geom    creation    end      version  latest_version_id    comment      license_id    variable_id  datasource_id    embargo    embargo_end                 publication                 lastUpdate
+----  --------------  ---------------  -------------  ---------------  ------  ----------  -----  ---------  -------------------  ---------  ------------  -------------  ---------------  ---------  --------------------------  --------------------------  --------------------------
+  20  Alfred data...  A dummy test...                 01010000003F...                                     1                                             2             15                   True       2022-05-22 05:45:24.827462  2020-05-22 05:45:24.827531  2020-05-22 05:45:24.827539
+
+
+
+

Note

+

In a future release, a set of flags will be added to the find command. These will make the export of found records into a file or as output to StdOut possbile. The show command is intended for raw table inspections only and just a workaround here.

+
+
+
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/cli/cli_add.ipynb b/cli/cli_add.ipynb new file mode 100644 index 00000000..860b64e7 --- /dev/null +++ b/cli/cli_add.ipynb @@ -0,0 +1,421 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Add command" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Help\n", + "The help text for the `add` subcommand can be shown by passing the `-h` flag." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "usage: metacatalog add [-h] [--version] [--connection CONNECTION] [--verbose]\n", + " [--quiet] [--dev] [--logfile LOGFILE] [--csv CSV]\n", + " [--txt TXT] [--json JSON]\n", + " entity\n", + "\n", + "positional arguments:\n", + " entity Name of the record entity to be added.\n", + "\n", + "optional arguments:\n", + " -h, --help show this help message and exit\n", + " --version, -v Returns the module version\n", + " --connection CONNECTION, -C CONNECTION\n", + " Connection string to the database instance.Follows the\n", + " syntax: driver://user:password@host:port/database\n", + " --verbose, -V Activate extended output.\n", + " --quiet, -q Suppress any kind of output.\n", + " --dev Development mode. Unexpected errors will not be\n", + " handled and the full traceback is printed to the\n", + " screen.\n", + " --logfile LOGFILE If a file is given, output will be written to that\n", + " file instead of printed to StdOut.\n", + " --csv CSV Data Origin Flag. Pass a CSV filename or content\n", + " containing the data. Column header have to match the\n", + " ADD API keywords.\n", + " --txt TXT Data Origin Flag. Pass a text filename or content\n", + " containing whitespace separated key=value pairs where\n", + " key has to match the ADD API keywords. If used\n", + " directly remember to quote accordingly.\n", + " --json JSON Data Origin Flag. Pass a JSON filename or content\n", + " containing the data. Must contain a list of objects\n", + " matchin the ADD API keywords.\n" + ] + } + ], + "source": [ + "%%bash\n", + "metacatalog add -h" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Prerequisites\n", + "\n", + "The `add` command assumes that either [`create`](cli_create.ipynb) and [`populate`](cli_populate.ipynb) or [`init`](cli_init.ipynb) were executed successfully." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Usage\n", + "\n", + "### entity\n", + "\n", + "The `add` command has one positional argument `entity` that has to be provided. This is the name of the record entitiy that should be added. There is a dictionary in `metacatalog` that maps enitity names to database models:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'author': ,\n", + " 'contributor': ,\n", + " 'datasource': ,\n", + " 'datasource_type': ,\n", + " 'datasourcetype': ,\n", + " 'entry': ,\n", + " 'keyword': ,\n", + " 'license': ,\n", + " 'person': ,\n", + " 'person_role': ,\n", + " 'personrole': ,\n", + " 'unit': ,\n", + " 'variable': }\n" + ] + } + ], + "source": [ + "from metacatalog.api._mapping import ENTITY_MAPPING\n", + "from pprint import pprint\n", + "pprint(ENTITY_MAPPING)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Many entities map to the same model. This is either due to different spelling, or because the API creates database records in different contexts. E.g. the API forces the user to pass at least one *person* as the first author of an *Entry* on creation. The *contributors* are optional and can be added if applicable. All *person*s will, however, be saved into the same table." + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. warning::\n", + "\n", + " The CLI is operating at a much lower level than the Python API. Many semantical workflows which add data to the database include way more individual steps using the CLI." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### connection\n", + "\n", + "In case no default connection was created and saved, you have to supply a connection string to the database using the `--connection` flag. See [`connection`](cli_connection.ipynb) command." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### passing arguments\n", + "\n", + "Obviously, you need to pass the actual metadata, that should be stored in metacatalog. There are three data origin flags available: \n", + "\n", + "* `--csv` - comma separated\n", + "* `--txt` - key=value pairs\n", + "* --`json` - JSON\n", + "\n", + "All three flags accept either a filename (including path) to a file in the specified format, or the content itself.\n", + "Instead of creating a file and passing the filename:\n", + "\n", + "```csv\n", + "name,symbol\n", + "foo,F\n", + "Bar,B\n", + "```\n", + "\n", + "you can can also use the flag like: `--csv 'name,symbol\\nnfoo,F\\nbar,B'`. This might be the easier approach if only one or two records are added." + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. note::\n", + " \n", + " You can inspect entities using the `show` command." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Operations" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. note::\n", + "\n", + " This section assumes that you are familiar with the metacatalog data model. As the CLI just uses the Python API under the hood, you will have to refer to the API documentation for a full reference." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Most metadata creation task cannot be done with one call to add. Furthermore, some entities relate to records, that have to be added in the first place, to not violate relation constraints. A prior example is that a person has to exist in the database, before it can be placed as an author.\n", + "\n", + "A typical workflow is to add missing lookup data, which includes `variables,units,licenses,keywords` and `details`. Then, you create all `person`s involved. Finally, the metadata `Entry` can be added. For most lookup data, a `1:n` relation is modelled and you can pass anything accepted by the `find` api or the ID. \n", + "`keyword`s and `person`s are, however, modelled in a `m:n` relation, which has to be specified in a second step." + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. warning:: \n", + "\n", + " The ``details`` entity is not reflected in the CLI or API yet. \n", + " \n", + " The usage of passing other identifiers to `find` than the ID, is experimental and not fully functions. It might also change in a future release\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example\n", + "\n", + "The following example should illustrate a workflow for adding new meta-data.\n", + "At first we add a unit of `awesomeness` and a variable of `awesome` - because most of our data is awesome." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Using session: Engine(postgresql://postgres:***@localhost:5432/metacatalog)\n", + "Added 1 unit records.\n", + "Done.\n" + ] + } + ], + "source": [ + "%%bash\n", + "metacatalog add unit --csv 'name,symbol,si\\nawesomeness,a,m'" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Using session: Engine(postgresql://postgres:***@localhost:5432/metacatalog)\n", + "Added 1 variable records.\n", + "Done.\n" + ] + } + ], + "source": [ + "%%bash\n", + "#metacatalog show attributes variables\n", + "metacatalog add variable --csv 'name,symbol,unit\\nawesome,A,awesomeness'" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here, we passed the new newly created unit name to the `add variable` endpoint." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Using session: Engine(postgresql://postgres:***@localhost:5432/metacatalog)\n", + "Added 1 person records.\n", + "Done.\n" + ] + } + ], + "source": [ + "%%bash\n", + "metacatalog add person --json '[{\"first_name\": \"Alfred, E.\", \"last_name\": \"Neumann\", \"affiliation\": \"Institute of Awesomeness\"}]'" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Finally, we can create the Entries of Alfred, E.'s data from a json file:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "\n", + "meta = dict(\n", + " title=\"Alfred data\", \n", + " abstract=\"A dummy test entry, which Alfred created\",\n", + " location=(37.422051, -122.084615),\n", + " license=2,\n", + " embargo=True,\n", + " variable=\"awesome\",\n", + " author=\"Neumann\"\n", + ")\n", + "with open('alfred.json', 'w') as js:\n", + " json.dump([meta], js)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Using session: Engine(postgresql://postgres:***@localhost:5432/metacatalog)\n", + "Added 1 entry records.\n", + "Done.\n" + ] + } + ], + "source": [ + "%%bash\n", + "metacatalog add entry --json alfred.json\n", + "rm alfred.json" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And finally, using the [`find`](cli_find.ipynb) and [`show`](cli_show.ipynb) command we can inspect the newly created entry:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Using session: Engine(postgresql://postgres:***@localhost:5432/metacatalog)\n", + "\n", + "Using session: Engine(postgresql://postgres:***@localhost:5432/metacatalog)\n", + " id title abstract external_id location geom creation end version latest_version_id comment license_id variable_id datasource_id embargo embargo_end publication lastUpdate\n", + "---- -------------- --------------- ------------- --------------- ------ ---------- ----- --------- ------------------- --------- ------------ ------------- --------------- --------- -------------------------- -------------------------- --------------------------\n", + " 20 Alfred data... A dummy test... 01010000003F... 1 2 15 True 2022-05-22 05:45:24.827462 2020-05-22 05:45:24.827531 2020-05-22 05:45:24.827539\n" + ] + } + ], + "source": [ + "%%bash\n", + "metacatalog find entry --by title \"Alfred data\"\n", + "metacatalog show records entries --where \"id=20\" -T" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. note::\n", + " \n", + " In a future release, a set of flags will be added to the ``find`` command. These will make the export of found records into a file or as output to StdOut possbile. The ``show`` command is intended for raw table inspections only and just a workaround here. " + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "finalized": { + "timestamp": 1590129848476, + "trusted": false + }, + "kernelspec": { + "display_name": "base", + "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.9.13 (main, Aug 25 2022, 23:26:10) \n[GCC 11.2.0]" + }, + "vscode": { + "interpreter": { + "hash": "f54d8176e82297fa872ac8c77277e50c0e193f921954c1c4a0b1ae2e8be99b71" + } + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/cli/cli_create.html b/cli/cli_create.html new file mode 100644 index 00000000..aa35ddc0 --- /dev/null +++ b/cli/cli_create.html @@ -0,0 +1,665 @@ + + + + + + + + + + + + Create Command — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Create Command#

+
+

Help#

+

The help text for the create subcommand can be shown by passing the -h flag.

+
+
[1]:
+
+
+
%%bash
+metacatalog create -h
+
+
+
+
+
+
+
+
+usage: metacatalog create [-h] [--version] [--connection CONNECTION]
+                          [--verbose] [--quiet] [--dev] [--logfile LOGFILE]
+
+optional arguments:
+  -h, --help            show this help message and exit
+  --version, -v         Returns the module version
+  --connection CONNECTION, -C CONNECTION
+                        Connection string to the database instance.Follows the
+                        syntax: driver://user:password@host:port/database
+  --verbose, -V         Activate extended output.
+  --quiet, -q           Suppress any kind of output.
+  --dev                 Development mode. Unexpected errors will not be
+                        handled and the full traceback is printed to the
+                        screen.
+  --logfile LOGFILE     If a file is given, output will be written to that
+                        file instead of printed to StdOut.
+
+
+
+
+

Creating tables#

+

The create command will create all necessary tables in the connected database. It will not fail if the tables are already present and create missing table. However, table changes are not reflected.

+
+

Note

+

The create command has the purpose of creating metacatalog into a fresh installation. +If you want to update the database, refer to the migration section (place link here).

+
+

If no --connection or -C flag is given, the default connection URI will be loaded from metacatalog’s config file.

+
+
+

Prerequisites#

+

Metacatalog does neither install the database, nor the PostGIS extension. We assume that the user specified in the connection string has granted full rights on the given database, but no super-user rights and no right to create addiontional databases. Installing PostGIS does need further installation steps on the host system, which are dependent on the host OS. Refer to PostGIS website for further explanations.

+

With everything installed, the database and PostGIS extension can be created like:

+
CREATE DATABASE metacatalog;
+
+
+

Connect to that database and run:

+
CREATE EXTENSTION postgis;
+
+
+

You can verify that installation succeeded if the following query yields the installed version:

+
SELECT PostGIS_full_version();
+
+
+
"POSTGIS="2.4.4 r16526" PGSQL="100" GEOS="3.7.1-CAPI-1.11.1 27a5e771" PROJ="Rel. 4.9.3, 15 August 2016" GDAL="GDAL 2.2.3, released 2017/11/20" LIBXML="2.9.4" LIBJSON="0.12.1" LIBPROTOBUF="1.2.1" RASTER"
+
+
+
+ +
+

See Also#

+ +
+
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/cli/cli_create.ipynb b/cli/cli_create.ipynb new file mode 100644 index 00000000..aff1f687 --- /dev/null +++ b/cli/cli_create.ipynb @@ -0,0 +1,175 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Create Command" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Help\n", + "The help text for the `create` subcommand can be shown by passing the `-h` flag." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "usage: metacatalog create [-h] [--version] [--connection CONNECTION]\n", + " [--verbose] [--quiet] [--dev] [--logfile LOGFILE]\n", + "\n", + "optional arguments:\n", + " -h, --help show this help message and exit\n", + " --version, -v Returns the module version\n", + " --connection CONNECTION, -C CONNECTION\n", + " Connection string to the database instance.Follows the\n", + " syntax: driver://user:password@host:port/database\n", + " --verbose, -V Activate extended output.\n", + " --quiet, -q Suppress any kind of output.\n", + " --dev Development mode. Unexpected errors will not be\n", + " handled and the full traceback is printed to the\n", + " screen.\n", + " --logfile LOGFILE If a file is given, output will be written to that\n", + " file instead of printed to StdOut.\n" + ] + } + ], + "source": [ + "%%bash\n", + "metacatalog create -h" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating tables" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The create command will create all necessary tables in the connected database. It will **not** fail if the tables are already present and create missing table. However, table changes are **not** reflected." + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. note::\n", + " \n", + " The ``create`` command has the purpose of creating metacatalog into a **fresh installation**. \n", + " If you want to update the database, refer to the migration section (place link here)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If no ``--connection`` or ``-C`` flag is given, the default connection URI will be loaded from metacatalog's config file." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Prerequisites" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Metacatalog does neither install the database, nor the PostGIS extension. We assume that the user specified in the connection string has granted full rights on the given database, but no super-user rights and no right to create addiontional databases. \n", + "Installing PostGIS does need further installation steps on the host system, which are dependent on the host OS. Refer to [PostGIS website](https://postgis.net/) for further explanations." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "With everything installed, the database and PostGIS extension can be created like:\n", + "\n", + "```SQL\n", + "CREATE DATABASE metacatalog;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Connect to that database and run:\n", + "\n", + "```sql\n", + "CREATE EXTENSTION postgis;\n", + "```\n", + "\n", + "You can verify that installation succeeded if the following query yields the installed version:\n", + "\n", + "```sql\n", + "SELECT PostGIS_full_version();\n", + "```\n", + "\n", + "```\n", + "\"POSTGIS=\"2.4.4 r16526\" PGSQL=\"100\" GEOS=\"3.7.1-CAPI-1.11.1 27a5e771\" PROJ=\"Rel. 4.9.3, 15 August 2016\" GDAL=\"GDAL 2.2.3, released 2017/11/20\" LIBXML=\"2.9.4\" LIBJSON=\"0.12.1\" LIBPROTOBUF=\"1.2.1\" RASTER\"\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Related commands\n", + "\n", + "In many scenarios you run rather the `metacatalog init` command, which runs `create` and `populate` at the same time. " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + "## See Also\n", + "\n", + "* [init command](cli_init.ipynb)\n", + "* [populate command](cli_populate.ipynb)" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/cli/cli_find.html b/cli/cli_find.html new file mode 100644 index 00000000..75ba564f --- /dev/null +++ b/cli/cli_find.html @@ -0,0 +1,793 @@ + + + + + + + + + + + + Find Command — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Find Command#

+
+

Help#

+

The help text for the find subcommand can be shown by passing the -h flag.

+
+
[1]:
+
+
+
%%bash
+metacatalog find -h
+
+
+
+
+
+
+
+
+usage: metacatalog find [-h] [--version] [--connection CONNECTION] [--verbose]
+                        [--quiet] [--dev] [--logfile LOGFILE] [--by BY BY]
+                        [--json] [--stdout] [--csv]
+                        entity
+
+positional arguments:
+  entity                Name of the requested database entity.
+
+optional arguments:
+  -h, --help            show this help message and exit
+  --version, -v         Returns the module version
+  --connection CONNECTION, -C CONNECTION
+                        Connection string to the database instance.Follows the
+                        syntax: driver://user:password@host:port/database
+  --verbose, -V         Activate extended output.
+  --quiet, -q           Suppress any kind of output.
+  --dev                 Development mode. Unexpected errors will not be
+                        handled and the full traceback is printed to the
+                        screen.
+  --logfile LOGFILE     If a file is given, output will be written to that
+                        file instead of printed to StdOut.
+  --by BY BY            key value pair to be used for finding record(s) in the
+                        database. Flag can be used multiple times.
+  --json                Output the found entities as JSON objects
+  --stdout              Default option. Print the string representation of
+                        found entities to StdOut.
+  --csv                 Output the found entities as CSV.
+
+
+
+
+

Prerequists#

+

The find command assumes that either `create <cli_create.ipynb>`__ and `populate <cli_populate.ipynb>`__ or `init <cli_init.ipynb>`__ were executed successfully.

+
+
+

Usage#

+
+

entity#

+
+

Note

+

The CLI endpoint of find is just wrapping the Python API endpoint. The API is designed for building model instances, which is often not really helpful from the command line. In future releases, more database model clases will represent themselves correctly when printed to StdOut. Furthermore a set of export flags are planned, to export models into CSV or JSON files. +Until then, some entities might not turn out very helpful at the current state.

+
+

The find command has one positional argument entity that has to be provided. This is the name of the record entitiy that should be found. There is a dictionary in metacatalog that maps enitity names to database models:

+
+
[2]:
+
+
+
from metacatalog.api._mapping import TABLE_MAPPING
+from pprint import pprint
+pprint(TABLE_MAPPING)
+
+
+
+
+
+
+
+
+{'datasource_types': <class 'metacatalog.models.datasource.DataSourceType'>,
+ 'datasources': <class 'metacatalog.models.datasource.DataSource'>,
+ 'entries': <class 'metacatalog.models.entry.Entry'>,
+ 'entry_groups': <class 'metacatalog.models.entrygroup.EntryGroup'>,
+ 'keywords': <class 'metacatalog.models.keyword.Keyword'>,
+ 'licenses': <class 'metacatalog.models.license.License'>,
+ 'person_roles': <class 'metacatalog.models.person.PersonRole'>,
+ 'persons': <class 'metacatalog.models.person.Person'>,
+ 'thesaurus': <class 'metacatalog.models.keyword.Thesaurus'>,
+ 'units': <class 'metacatalog.models.variable.Unit'>,
+ 'variables': <class 'metacatalog.models.variable.Variable'>}
+
+
+

Many entities map to the same model. This is either due to different spelling, or because the API creates database records in different contexts. E.g. the API forces the user to pass at least one person as the first author of an Entry on creation. The contributors are optional and can be added if applicable. All persons will, however, be saved into the same table.

+
+
+

connection#

+

In case no default connection was created and saved, you have to supply a connection string to the database using the --connection flag. See `connection <cli_connection.ipynb>`__ command.

+
+
+

passing arguments#

+

Arguments to filter for the correct records can be spcified by the --by flag. It’s usage is optional. If no filter is set, all records will be returned, which might be a lot. You can pass --by multiple times to create multiple filters.

+
+

Note

+

The find endpoint is not made for open searches and does not offer fine-granular filtering. Each filter passed is stacked on top of each other, effectively resulting in a logical AND connection.

+
+

The --by flag requires exactly two arguments. The first is the column to filter and the second the value which has to be matched. It cannot perform not-filters and does not accept a None or null.

+
+
+
+

Example#

+
+
[3]:
+
+
+
%%bash
+metacatalog find licenses --by short_title ODbL
+
+
+
+
+
+
+
+
+Open Data Commons Open Database License <ID=4>
+
+
+
+
[4]:
+
+
+
%%bash
+metacatalog find licenses --by id 4
+
+
+
+
+
+
+
+
+Open Data Commons Open Database License <ID=4>
+
+
+
+
[5]:
+
+
+
%%bash
+metacatalog find licenses --by by_attribution True
+
+
+
+
+
+
+
+
+Open Data Commons Open Database License <ID=4>
+Open Data Commons Attribution License v1.0 <ID=5>
+Creative Commons Attribution 4.0 International <ID=6>
+Creative Commons Attribution-ShareAlike 4.0 International <ID=7>
+Creative Commons Attribution-NonCommerical 4.0 International <ID=8>
+Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International <ID=9>
+
+
+
+
[6]:
+
+
+
%%bash
+metacatalog find entry
+
+
+
+
+
+
+
+
+<ID=3 Sap Flow - Hohes Hol [sap flow] >
+<ID=4 Sap Flow - Hohes Hol [sap flow] >
+<ID=5 Sap Flow - Hohes Hol [sap flow] >
+<ID=6 Sap Flow - Hohes Hol [sap flow] >
+<ID=7 Sap Flow - Hohes Hol [sap flow] >
+<ID=8 Sap Flow - Hohes Hol [sap flow] >
+<ID=9 Sap Flow - Hohes Hol [sap flow] >
+<ID=16 Sap Flow - Hohes Hol [sap flow] >
+<ID=17 Sap Flow - Hohes Hol [sap flow] >
+<ID=18 Alfred's data [awesome] >
+<ID=19 Alfred's data [awesome] >
+<ID=1 Sap Flow - Hohes Hol [sap flow] >
+<ID=11 Sap Flow - Hohes Hol [sap flow] >
+<ID=10 Sap Flow - Hohes Hol [sap flow] >
+<ID=12 Sap Flow - Hohes Hol [sap flow] >
+<ID=13 Sap Flow - Hohes Hol [sap flow] >
+<ID=14 Sap Flow - Hohes Hol [sap flow] >
+<ID=15 Sap Flow - Hohes Hol [sap flow] >
+<ID=2 Sap Flow - Hohes Hol [sap flow] >
+<ID=20 Alfred data [awesome] >
+
+
+
+
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/cli/cli_find.ipynb b/cli/cli_find.ipynb new file mode 100644 index 00000000..cedee0e4 --- /dev/null +++ b/cli/cli_find.ipynb @@ -0,0 +1,302 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Find Command" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Help\n", + "The help text for the `find` subcommand can be shown by passing the `-h` flag." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "usage: metacatalog find [-h] [--version] [--connection CONNECTION] [--verbose]\n", + " [--quiet] [--dev] [--logfile LOGFILE] [--by BY BY]\n", + " [--json] [--stdout] [--csv]\n", + " entity\n", + "\n", + "positional arguments:\n", + " entity Name of the requested database entity.\n", + "\n", + "optional arguments:\n", + " -h, --help show this help message and exit\n", + " --version, -v Returns the module version\n", + " --connection CONNECTION, -C CONNECTION\n", + " Connection string to the database instance.Follows the\n", + " syntax: driver://user:password@host:port/database\n", + " --verbose, -V Activate extended output.\n", + " --quiet, -q Suppress any kind of output.\n", + " --dev Development mode. Unexpected errors will not be\n", + " handled and the full traceback is printed to the\n", + " screen.\n", + " --logfile LOGFILE If a file is given, output will be written to that\n", + " file instead of printed to StdOut.\n", + " --by BY BY key value pair to be used for finding record(s) in the\n", + " database. Flag can be used multiple times.\n", + " --json Output the found entities as JSON objects\n", + " --stdout Default option. Print the string representation of\n", + " found entities to StdOut.\n", + " --csv Output the found entities as CSV.\n" + ] + } + ], + "source": [ + "%%bash\n", + "metacatalog find -h" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Prerequists\n", + "\n", + "The `find` command assumes that either [`create`](cli_create.ipynb) and [`populate`](cli_populate.ipynb) or [`init`](cli_init.ipynb) were executed successfully." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Usage\n", + "\n", + "### entity" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. note::\n", + "\n", + " The CLI endpoint of ``find`` is just wrapping the Python API endpoint. The API is designed for building model instances, which is often not really helpful from the command line. In future releases, more database model clases will represent themselves correctly when printed to StdOut. Furthermore a set of *export flags* are planned, to export models into CSV or JSON files.\n", + " Until then, some entities might not turn out very helpful at the current state." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `find` command has one positional argument `entity` that has to be provided. This is the name of the record entitiy that should be `found`. There is a dictionary in `metacatalog` that maps enitity names to database models:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'datasource_types': ,\n", + " 'datasources': ,\n", + " 'entries': ,\n", + " 'entry_groups': ,\n", + " 'keywords': ,\n", + " 'licenses': ,\n", + " 'person_roles': ,\n", + " 'persons': ,\n", + " 'thesaurus': ,\n", + " 'units': ,\n", + " 'variables': }\n" + ] + } + ], + "source": [ + "from metacatalog.api._mapping import TABLE_MAPPING\n", + "from pprint import pprint\n", + "pprint(TABLE_MAPPING)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Many entities map to the same model. This is either due to different spelling, or because the API creates database records in different contexts. E.g. the API forces the user to pass at least one *person* as the first author of an *Entry* on creation. The *contributors* are optional and can be added if applicable. All *person*s will, however, be saved into the same table." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### connection\n", + "\n", + "In case no default connection was created and saved, you have to supply a connection string to the database using the `--connection` flag. See [`connection`](cli_connection.ipynb) command." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### passing arguments\n", + "\n", + "Arguments to filter for the correct records can be spcified by the `--by` flag. It's usage is optional. If no filter is set, **all** records will be returned, which might be a lot.\n", + "You can pass `--by` multiple times to create multiple filters. " + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. note::\n", + "\n", + " The ``find`` endpoint is not made for open searches and does not offer fine-granular filtering. Each filter passed is stacked **on top** of each other, effectively resulting in a logical **AND** connection. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `--by` flag requires exactly two arguments. The first is the column to filter and the second the value which has to be matched. It cannot perform *not*-filters and does not accept a `None` or `null`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Open Data Commons Open Database License \n" + ] + } + ], + "source": [ + "%%bash\n", + "metacatalog find licenses --by short_title ODbL" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Open Data Commons Open Database License \n" + ] + } + ], + "source": [ + "%%bash\n", + "metacatalog find licenses --by id 4" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Open Data Commons Open Database License \n", + "Open Data Commons Attribution License v1.0 \n", + "Creative Commons Attribution 4.0 International \n", + "Creative Commons Attribution-ShareAlike 4.0 International \n", + "Creative Commons Attribution-NonCommerical 4.0 International \n", + "Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International \n" + ] + } + ], + "source": [ + "%%bash \n", + "metacatalog find licenses --by by_attribution True" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ] + } + ], + "source": [ + "%%bash\n", + "metacatalog find entry" + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "finalized": { + "timestamp": 1590129905396, + "trusted": true + }, + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/cli/cli_init.html b/cli/cli_init.html new file mode 100644 index 00000000..f09f4bfa --- /dev/null +++ b/cli/cli_init.html @@ -0,0 +1,637 @@ + + + + + + + + + + + + Init command — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Init command#

+
+

Help#

+

The help text for the init subcommand can be shown by passing the -h flag.

+
+
[1]:
+
+
+
%%bash
+metacatalog init -h
+
+
+
+
+
+
+
+
+usage: metacatalog init [-h] [--version] [--connection CONNECTION] [--verbose]
+                        [--quiet] [--dev] [--logfile LOGFILE]
+                        [--ignore IGNORE [IGNORE ...]]
+
+optional arguments:
+  -h, --help            show this help message and exit
+  --version, -v         Returns the module version
+  --connection CONNECTION, -C CONNECTION
+                        Connection string to the database instance.Follows the
+                        syntax: driver://user:password@host:port/database
+  --verbose, -V         Activate extended output.
+  --quiet, -q           Suppress any kind of output.
+  --dev                 Development mode. Unexpected errors will not be
+                        handled and the full traceback is printed to the
+                        screen.
+  --logfile LOGFILE     If a file is given, output will be written to that
+                        file instead of printed to StdOut.
+  --ignore IGNORE [IGNORE ...], -I IGNORE [IGNORE ...]
+                        List tables to be ignored for default population.
+
+
+
+
+

Usage#

+

The init command just runs two other commands. First, create and then populate. Refer to their documentation for more info

+
+
+

See Also#

+ +
+
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/cli/cli_init.ipynb b/cli/cli_init.ipynb new file mode 100644 index 00000000..784f6b64 --- /dev/null +++ b/cli/cli_init.ipynb @@ -0,0 +1,95 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Init command" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Help\n", + "The help text for the `init` subcommand can be shown by passing the `-h` flag." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "usage: metacatalog init [-h] [--version] [--connection CONNECTION] [--verbose]\n", + " [--quiet] [--dev] [--logfile LOGFILE]\n", + " [--ignore IGNORE [IGNORE ...]]\n", + "\n", + "optional arguments:\n", + " -h, --help show this help message and exit\n", + " --version, -v Returns the module version\n", + " --connection CONNECTION, -C CONNECTION\n", + " Connection string to the database instance.Follows the\n", + " syntax: driver://user:password@host:port/database\n", + " --verbose, -V Activate extended output.\n", + " --quiet, -q Suppress any kind of output.\n", + " --dev Development mode. Unexpected errors will not be\n", + " handled and the full traceback is printed to the\n", + " screen.\n", + " --logfile LOGFILE If a file is given, output will be written to that\n", + " file instead of printed to StdOut.\n", + " --ignore IGNORE [IGNORE ...], -I IGNORE [IGNORE ...]\n", + " List tables to be ignored for default population.\n" + ] + } + ], + "source": [ + "%%bash\n", + "metacatalog init -h" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Usage\n", + "\n", + "The `init` command just runs two other commands. First, `create` and then `populate`. Refer to their documentation for more info" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## See Also\n", + "\n", + "* [create command](cli_create.ipynb)\n", + "* [populate command](cli_populate.ipynb)" + ] + } + ], + "metadata": { + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/cli/cli_populate.html b/cli/cli_populate.html new file mode 100644 index 00000000..16149cbd --- /dev/null +++ b/cli/cli_populate.html @@ -0,0 +1,674 @@ + + + + + + + + + + + + Populate command — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Populate command#

+
+

Help#

+

The help text for the populate subcommand can be shown by passing the -h flag.

+
+
[1]:
+
+
+
%%bash
+metacatalog populate -h
+
+
+
+
+
+
+
+
+usage: metacatalog populate [-h] [--version] [--connection CONNECTION]
+                            [--verbose] [--quiet] [--dev] [--logfile LOGFILE]
+                            [--ignore IGNORE [IGNORE ...]]
+
+optional arguments:
+  -h, --help            show this help message and exit
+  --version, -v         Returns the module version
+  --connection CONNECTION, -C CONNECTION
+                        Connection string to the database instance.Follows the
+                        syntax: driver://user:password@host:port/database
+  --verbose, -V         Activate extended output.
+  --quiet, -q           Suppress any kind of output.
+  --dev                 Development mode. Unexpected errors will not be
+                        handled and the full traceback is printed to the
+                        screen.
+  --logfile LOGFILE     If a file is given, output will be written to that
+                        file instead of printed to StdOut.
+  --ignore IGNORE [IGNORE ...], -I IGNORE [IGNORE ...]
+                        List tables to be ignored for default population.
+
+
+
+
+

Usage#

+

The populate command will load some default values for a set of lookup tables into the (default) database instanstance defined. You can omit tables by passing their table names using the --ignore flag.

+

The tables that have default values and will be imported are mapped to their model class in a dict called IMPORTABLE_TABLES, which is printed below:

+
+
[2]:
+
+
+
from pprint import pprint
+from metacatalog.api.db import IMPORTABLE_TABLES
+pprint(IMPORTABLE_TABLES)
+
+
+
+
+
+
+
+
+{'datasource_types': <class 'metacatalog.models.datasource.DataSourceType'>,
+ 'datatypes': <class 'metacatalog.models.datasource.DataType'>,
+ 'entrygroup_types': <class 'metacatalog.models.entrygroup.EntryGroupType'>,
+ 'keywords': <class 'metacatalog.models.keyword.Keyword'>,
+ 'licenses': <class 'metacatalog.models.license.License'>,
+ 'person_roles': <class 'metacatalog.models.person.PersonRole'>,
+ 'thesaurus': <class 'metacatalog.models.keyword.Thesaurus'>,
+ 'units': <class 'metacatalog.models.variable.Unit'>,
+ 'variables': <class 'metacatalog.models.variable.Variable'>}
+
+
+
+
+

Prerequisites#

+

It is assumed that create was executed before.

+
+ +
+

See also#

+ +
+
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/cli/cli_populate.ipynb b/cli/cli_populate.ipynb new file mode 100644 index 00000000..4618c276 --- /dev/null +++ b/cli/cli_populate.ipynb @@ -0,0 +1,147 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Populate command" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Help\n", + "The help text for the `populate` subcommand can be shown by passing the `-h` flag." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "usage: metacatalog populate [-h] [--version] [--connection CONNECTION]\n", + " [--verbose] [--quiet] [--dev] [--logfile LOGFILE]\n", + " [--ignore IGNORE [IGNORE ...]]\n", + "\n", + "optional arguments:\n", + " -h, --help show this help message and exit\n", + " --version, -v Returns the module version\n", + " --connection CONNECTION, -C CONNECTION\n", + " Connection string to the database instance.Follows the\n", + " syntax: driver://user:password@host:port/database\n", + " --verbose, -V Activate extended output.\n", + " --quiet, -q Suppress any kind of output.\n", + " --dev Development mode. Unexpected errors will not be\n", + " handled and the full traceback is printed to the\n", + " screen.\n", + " --logfile LOGFILE If a file is given, output will be written to that\n", + " file instead of printed to StdOut.\n", + " --ignore IGNORE [IGNORE ...], -I IGNORE [IGNORE ...]\n", + " List tables to be ignored for default population.\n" + ] + } + ], + "source": [ + "%%bash\n", + "metacatalog populate -h" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Usage" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `populate` command will load some default values for a set of lookup tables into the (default) database instanstance defined. You can omit tables by passing their table names using the `--ignore` flag.\n", + "\n", + "The tables that have default values and will be imported are mapped to their model class in a dict called `IMPORTABLE_TABLES`, which is printed below:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'datasource_types': ,\n", + " 'datatypes': ,\n", + " 'entrygroup_types': ,\n", + " 'keywords': ,\n", + " 'licenses': ,\n", + " 'person_roles': ,\n", + " 'thesaurus': ,\n", + " 'units': ,\n", + " 'variables': }\n" + ] + } + ], + "source": [ + "from pprint import pprint\n", + "from metacatalog.api.db import IMPORTABLE_TABLES\n", + "pprint(IMPORTABLE_TABLES)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Prerequisites\n", + "\n", + "It is assumed that `create` was executed before." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Related commands\n", + "\n", + "In many scenarios you run rather the `metacatalog init` command, which runs `create` and `populate` at the same time. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## See also\n", + "\n", + "* [create command](cli_create.ipynb)\n", + "* [init command](cli_init.ipynb)" + ] + } + ], + "metadata": { + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/cli/cli_show.html b/cli/cli_show.html new file mode 100644 index 00000000..1e42ff06 --- /dev/null +++ b/cli/cli_show.html @@ -0,0 +1,641 @@ + + + + + + + + + + + + Show command — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Show command#

+
+

Help#

+

The help text for the show subcommand can be shown by passing the -h flag.

+
+
[1]:
+
+
+
%%bash
+metacatalog show -h
+
+
+
+
+
+
+
+
+usage: metacatalog show [-h] [--version] [--connection CONNECTION] [--verbose]
+                        [--quiet] [--dev] [--logfile LOGFILE] [--names-only]
+                        [--limit LIMIT] [--where WHERE] [--truncate]
+                        {attributes,records} table
+
+positional arguments:
+  {attributes,records}  Element to be shown. attributes Show table attributes.
+                        records Show raw table records
+  table                 Table name.
+
+optional arguments:
+  -h, --help            show this help message and exit
+  --version, -v         Returns the module version
+  --connection CONNECTION, -C CONNECTION
+                        Connection string to the database instance.Follows the
+                        syntax: driver://user:password@host:port/database
+  --verbose, -V         Activate extended output.
+  --quiet, -q           Suppress any kind of output.
+  --dev                 Development mode. Unexpected errors will not be
+                        handled and the full traceback is printed to the
+                        screen.
+  --logfile LOGFILE     If a file is given, output will be written to that
+                        file instead of printed to StdOut.
+  --names-only          Show only the attribute names. Only valid with
+                        'attribute' action.
+  --limit LIMIT, -L LIMIT
+                        Only valid with 'records' action. Will limit the
+                        number of records returned
+  --where WHERE         Only valid with 'records' action. Raw SQL WHERE clause
+                        to filter the results. Use carefully.
+  --truncate, -T        Only valid with 'records' action. Truncates string
+                        output to 12 signs.
+
+
+
+

Warning

+

The show command might be completely replaced by a export command.

+
+
+
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/cli/cli_show.ipynb b/cli/cli_show.ipynb new file mode 100644 index 00000000..953077c6 --- /dev/null +++ b/cli/cli_show.ipynb @@ -0,0 +1,105 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Show command" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Help\n", + "The help text for the `show` subcommand can be shown by passing the `-h` flag." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "usage: metacatalog show [-h] [--version] [--connection CONNECTION] [--verbose]\n", + " [--quiet] [--dev] [--logfile LOGFILE] [--names-only]\n", + " [--limit LIMIT] [--where WHERE] [--truncate]\n", + " {attributes,records} table\n", + "\n", + "positional arguments:\n", + " {attributes,records} Element to be shown. attributes Show table attributes.\n", + " records Show raw table records\n", + " table Table name.\n", + "\n", + "optional arguments:\n", + " -h, --help show this help message and exit\n", + " --version, -v Returns the module version\n", + " --connection CONNECTION, -C CONNECTION\n", + " Connection string to the database instance.Follows the\n", + " syntax: driver://user:password@host:port/database\n", + " --verbose, -V Activate extended output.\n", + " --quiet, -q Suppress any kind of output.\n", + " --dev Development mode. Unexpected errors will not be\n", + " handled and the full traceback is printed to the\n", + " screen.\n", + " --logfile LOGFILE If a file is given, output will be written to that\n", + " file instead of printed to StdOut.\n", + " --names-only Show only the attribute names. Only valid with\n", + " 'attribute' action.\n", + " --limit LIMIT, -L LIMIT\n", + " Only valid with 'records' action. Will limit the\n", + " number of records returned\n", + " --where WHERE Only valid with 'records' action. Raw SQL WHERE clause\n", + " to filter the results. Use carefully.\n", + " --truncate, -T Only valid with 'records' action. Truncates string\n", + " output to 12 signs.\n" + ] + } + ], + "source": [ + "%%bash\n", + "metacatalog show -h" + ] + }, + { + "cell_type": "raw", + "metadata": { + "raw_mimetype": "text/restructuredtext" + }, + "source": [ + ".. warning::\n", + "\n", + " The ``show`` command might be completely replaced by a ``export`` command." + ] + } + ], + "metadata": { + "celltoolbar": "Raw Cell Format", + "finalized": { + "timestamp": 1590129908857, + "trusted": true + }, + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/cli/cli_uuid.html b/cli/cli_uuid.html new file mode 100644 index 00000000..2e926f26 --- /dev/null +++ b/cli/cli_uuid.html @@ -0,0 +1,766 @@ + + + + + + + + + + + + Uuid command — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Uuid command#

+
+

Help#

+

The help text for the uuid subcommand can be shown by passing the -h flag.

+
+
[1]:
+
+
+
%%bash
+metacatalog uuid -h
+
+
+
+
+
+
+
+
+usage: metacatalog uuid [-h] [--version] [--connection CONNECTION] [--verbose]
+                        [--quiet] [--dev] [--logfile LOGFILE] [--json]
+                        uuid
+
+positional arguments:
+  uuid                  Version 4 UUID of the requested resource
+
+optional arguments:
+  -h, --help            show this help message and exit
+  --version, -v         Returns the module version
+  --connection CONNECTION, -C CONNECTION
+                        Connection string to the database instance.Follows the
+                        syntax: driver://user:password@host:port/database
+  --verbose, -V         Activate extended output.
+  --quiet, -q           Suppress any kind of output.
+  --dev                 Development mode. Unexpected errors will not be
+                        handled and the full traceback is printed to the
+                        screen.
+  --logfile LOGFILE     If a file is given, output will be written to that
+                        file instead of printed to StdOut.
+  --json                If set, the object will be returned as JSON
+
+
+
+
+

Prerequisites#

+

The uuid command assumes that metadata records have been added using `add <cli_add.ipynb>`__.

+
+
+

Usage#

+

The uuid command can be used to search the database for an object of given UUID. metacatalog always refers to UUID version 4. The command will search records across models for the reqeusted resource. Currently, the following model instances can be found:

+
    +
  • Entry

  • +
  • EntryGroup

  • +
  • Keyword

  • +
+
+
+

Example#

+

The following example shows how a keyword can be found by UUID and related keywords can be searched. If the database included the GCMD Keywords on `init <cli_init.ipynb>`__, there will be an controlled keyword of UUID 885735f3-121e-4ca0-ac8b-f37dbc972f03, which tags the hydrosphere.

+
+
[2]:
+
+
+
%%bash
+metacatalog uuid 885735f3-121e-4ca0-ac8b-f37dbc972f03
+
+
+
+
+
+
+
+
+Using session: Engine(postgresql://postgres:***@localhost:5432/metacatalog)
+EARTH SCIENCE > TERRESTRIAL HYDROSPHERE
+
+
+

By passing the --json flag, the output can be transformed to json, giving us more information

+
+
[3]:
+
+
+
%%bash
+metacatalog uuid --json 885735f3-121e-4ca0-ac8b-f37dbc972f03
+
+
+
+
+
+
+
+
+Using session: Engine(postgresql://postgres:***@localhost:5432/metacatalog)
+{
+    "id": 25,
+    "uuid": "885735f3-121e-4ca0-ac8b-f37dbc972f03",
+    "value": "TERRESTRIAL HYDROSPHERE",
+    "path": "EARTH SCIENCE > TERRESTRIAL HYDROSPHERE",
+    "children": [
+        "099ab1ae-f4d2-48cc-be2f-86bd58ffc4ca",
+        "734f8f27-6976-4b67-8794-c7fc79d6161e",
+        "50b8fe04-9149-4b7f-a8b2-b33b1e3aa192",
+        "5debb283-51e4-435e-b2a2-e8e2a977220d",
+        "8c02f5d1-ce86-4bf5-84d5-b3496cdba6ad"
+    ],
+    "thesaurus_id": 1
+}
+
+
+

Now we can easily check the children keywords:

+
+
[4]:
+
+
+
%%bash
+metacatalog uuid --json 5debb283-51e4-435e-b2a2-e8e2a977220d
+
+
+
+
+
+
+
+
+Using session: Engine(postgresql://postgres:***@localhost:5432/metacatalog)
+{
+    "id": 214,
+    "uuid": "5debb283-51e4-435e-b2a2-e8e2a977220d",
+    "value": "SURFACE WATER",
+    "path": "EARTH SCIENCE > TERRESTRIAL HYDROSPHERE > SURFACE WATER",
+    "children": [
+        "1baa552d-c563-43fb-b618-54651f8b07e6",
+        "959f1861-a776-41b1-ba6b-d23c71d4d1eb",
+        "9d86cd70-062a-4c39-b3f3-226abebc07f7",
+        "c84b61fe-720a-4240-b6c8-8dcc9ae24a36"
+    ],
+    "thesaurus_id": 1
+}
+
+
+
+
[5]:
+
+
+
%%bash
+metacatalog uuid --json 9d86cd70-062a-4c39-b3f3-226abebc07f7
+
+
+
+
+
+
+
+
+Using session: Engine(postgresql://postgres:***@localhost:5432/metacatalog)
+{
+    "id": 6220,
+    "uuid": "9d86cd70-062a-4c39-b3f3-226abebc07f7",
+    "value": "SURFACE WATER PROCESSES/MEASUREMENTS",
+    "path": "EARTH SCIENCE > TERRESTRIAL HYDROSPHERE > SURFACE WATER > SURFACE WATER PROCESSES/MEASUREMENTS",
+    "children": [
+        "3609b843-d840-460c-b1a3-d4fcc69a32f6",
+        "36a2999b-2255-4d4e-a249-40df3b7b3aaf",
+        "269c7277-fa8f-4c1c-bd8b-ab772c1df4e5",
+        "7fdc339e-017f-4e4b-89a3-12e441a40bad",
+        "960037c5-57b1-4cdf-84be-4542beee7d5a",
+        "d4e8b5c5-9203-4982-82bc-2611b517ffdb",
+        "c6c0c5dd-c0ca-4670-bbaa-c22d39e73570",
+        "5cb5d5b9-0c0b-497f-a4ea-a8cece52d13d",
+        "6f52de55-f5f2-45c0-b83f-59dbfb1fe221",
+        "42aa1fa1-56a9-4e96-8063-077bd7ba88d8",
+        "84784fef-5b76-45a0-91e0-28788e09fea6",
+        "04922ba6-8f00-4f54-b80c-ce2414c91e2e",
+        "f6a54329-486b-4d5f-b105-c639cec42351"
+    ],
+    "thesaurus_id": 1
+}
+
+
+
+
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/cli/cli_uuid.ipynb b/cli/cli_uuid.ipynb new file mode 100644 index 00000000..fc91ab0c --- /dev/null +++ b/cli/cli_uuid.ipynb @@ -0,0 +1,246 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Uuid command" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Help\n", + "The help text for the `uuid` subcommand can be shown by passing the `-h` flag." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "usage: metacatalog uuid [-h] [--version] [--connection CONNECTION] [--verbose]\n", + " [--quiet] [--dev] [--logfile LOGFILE] [--json]\n", + " uuid\n", + "\n", + "positional arguments:\n", + " uuid Version 4 UUID of the requested resource\n", + "\n", + "optional arguments:\n", + " -h, --help show this help message and exit\n", + " --version, -v Returns the module version\n", + " --connection CONNECTION, -C CONNECTION\n", + " Connection string to the database instance.Follows the\n", + " syntax: driver://user:password@host:port/database\n", + " --verbose, -V Activate extended output.\n", + " --quiet, -q Suppress any kind of output.\n", + " --dev Development mode. Unexpected errors will not be\n", + " handled and the full traceback is printed to the\n", + " screen.\n", + " --logfile LOGFILE If a file is given, output will be written to that\n", + " file instead of printed to StdOut.\n", + " --json If set, the object will be returned as JSON\n" + ] + } + ], + "source": [ + "%%bash\n", + "metacatalog uuid -h" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Prerequisites\n", + "\n", + "The `uuid` command assumes that metadata records have been added using [`add`](cli_add.ipynb). " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Usage\n", + "\n", + "The `uuid` command can be used to search the database for an object of given UUID. `metacatalog` always refers to UUID version 4. The command will search records across models for the reqeusted resource. Currently, the following model instances can be found:\n", + "\n", + "* `Entry`\n", + "* `EntryGroup`\n", + "* `Keyword`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example\n", + "\n", + "The following example shows how a keyword can be found by UUID and related keywords can be searched.\n", + "If the database included the GCMD `Keywords` on [`init`](cli_init.ipynb), there will be an controlled keyword of UUID `885735f3-121e-4ca0-ac8b-f37dbc972f03`, which tags the hydrosphere." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Using session: Engine(postgresql://postgres:***@localhost:5432/metacatalog)\n", + "EARTH SCIENCE > TERRESTRIAL HYDROSPHERE\n" + ] + } + ], + "source": [ + "%%bash\n", + "metacatalog uuid 885735f3-121e-4ca0-ac8b-f37dbc972f03" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "By passing the `--json` flag, the output can be transformed to json, giving us more information" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Using session: Engine(postgresql://postgres:***@localhost:5432/metacatalog)\n", + "{\n", + " \"id\": 25,\n", + " \"uuid\": \"885735f3-121e-4ca0-ac8b-f37dbc972f03\",\n", + " \"value\": \"TERRESTRIAL HYDROSPHERE\",\n", + " \"path\": \"EARTH SCIENCE > TERRESTRIAL HYDROSPHERE\",\n", + " \"children\": [\n", + " \"099ab1ae-f4d2-48cc-be2f-86bd58ffc4ca\",\n", + " \"734f8f27-6976-4b67-8794-c7fc79d6161e\",\n", + " \"50b8fe04-9149-4b7f-a8b2-b33b1e3aa192\",\n", + " \"5debb283-51e4-435e-b2a2-e8e2a977220d\",\n", + " \"8c02f5d1-ce86-4bf5-84d5-b3496cdba6ad\"\n", + " ],\n", + " \"thesaurus_id\": 1\n", + "}\n" + ] + } + ], + "source": [ + "%%bash\n", + "metacatalog uuid --json 885735f3-121e-4ca0-ac8b-f37dbc972f03" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can easily check the children keywords:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Using session: Engine(postgresql://postgres:***@localhost:5432/metacatalog)\n", + "{\n", + " \"id\": 214,\n", + " \"uuid\": \"5debb283-51e4-435e-b2a2-e8e2a977220d\",\n", + " \"value\": \"SURFACE WATER\",\n", + " \"path\": \"EARTH SCIENCE > TERRESTRIAL HYDROSPHERE > SURFACE WATER\",\n", + " \"children\": [\n", + " \"1baa552d-c563-43fb-b618-54651f8b07e6\",\n", + " \"959f1861-a776-41b1-ba6b-d23c71d4d1eb\",\n", + " \"9d86cd70-062a-4c39-b3f3-226abebc07f7\",\n", + " \"c84b61fe-720a-4240-b6c8-8dcc9ae24a36\"\n", + " ],\n", + " \"thesaurus_id\": 1\n", + "}\n" + ] + } + ], + "source": [ + "%%bash \n", + "metacatalog uuid --json 5debb283-51e4-435e-b2a2-e8e2a977220d" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Using session: Engine(postgresql://postgres:***@localhost:5432/metacatalog)\n", + "{\n", + " \"id\": 6220,\n", + " \"uuid\": \"9d86cd70-062a-4c39-b3f3-226abebc07f7\",\n", + " \"value\": \"SURFACE WATER PROCESSES/MEASUREMENTS\",\n", + " \"path\": \"EARTH SCIENCE > TERRESTRIAL HYDROSPHERE > SURFACE WATER > SURFACE WATER PROCESSES/MEASUREMENTS\",\n", + " \"children\": [\n", + " \"3609b843-d840-460c-b1a3-d4fcc69a32f6\",\n", + " \"36a2999b-2255-4d4e-a249-40df3b7b3aaf\",\n", + " \"269c7277-fa8f-4c1c-bd8b-ab772c1df4e5\",\n", + " \"7fdc339e-017f-4e4b-89a3-12e441a40bad\",\n", + " \"960037c5-57b1-4cdf-84be-4542beee7d5a\",\n", + " \"d4e8b5c5-9203-4982-82bc-2611b517ffdb\",\n", + " \"c6c0c5dd-c0ca-4670-bbaa-c22d39e73570\",\n", + " \"5cb5d5b9-0c0b-497f-a4ea-a8cece52d13d\",\n", + " \"6f52de55-f5f2-45c0-b83f-59dbfb1fe221\",\n", + " \"42aa1fa1-56a9-4e96-8063-077bd7ba88d8\",\n", + " \"84784fef-5b76-45a0-91e0-28788e09fea6\",\n", + " \"04922ba6-8f00-4f54-b80c-ce2414c91e2e\",\n", + " \"f6a54329-486b-4d5f-b105-c639cec42351\"\n", + " ],\n", + " \"thesaurus_id\": 1\n", + "}\n" + ] + } + ], + "source": [ + "%%bash \n", + "metacatalog uuid --json 9d86cd70-062a-4c39-b3f3-226abebc07f7" + ] + } + ], + "metadata": { + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/dev/dev.html b/dev/dev.html new file mode 100644 index 00000000..7b9e2af0 --- /dev/null +++ b/dev/dev.html @@ -0,0 +1,576 @@ + + + + + + + + + + + + Developers — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Developers#

+
+
+
+

Guide#

+

You will find different guides and how-tos here that help +database administrators and developers to customize metacatalog to +fit their specific requirements.

+

You will also find a section (soon) that will guide you through +contributing to metacatalog.

+

We are working on opening some of metacatalog’s functionality to make +it easier to overwrite some of metacatalogs default behaviors. So be +sure to check out this section from time to time.

+
+
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/dev/iso19115.html b/dev/iso19115.html new file mode 100644 index 00000000..afec74f7 --- /dev/null +++ b/dev/iso19115.html @@ -0,0 +1,2047 @@ + + + + + + + + + + + + ISO 19115 — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

ISO 19115#

+
+

Overview#

+

Metacatalog makes it possible to store metadata in ISO 19115 standard. +Some of the required information for ISO 19115 will be the same for the +whole metacatalog instance, or, is specific to your installation and use +case. Below is a table of ISO 19115 code lists, which are implemented +and how they translate into metacatalog.

+
+
+

ISO 19115 Fields#

+

ISO 19115, 19115-1, and 19115-2 define only a few mandatory (M) fields, but a long +list of optional (O) and coditional (C) fields. In the table below you find a mapping +of ISO names to metacatalog names. If the column Code List is filled, metacatalog +makes use of published ISO 19115 CodeList values. In the section below you can find +for every list, how the single values map into metacatalog.

+
+

MD_Metadata#

+ +++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

ISO field

metacatalog

CodeList or Table

Cardinality

description

fileIdentifier

Entry.uuid

[1]

language

'en-US'

ISO 639-2 values are allowed

[1]

metacatalog is not multi-language and therefore a common value should be used

characterSet

DataSource.encoding

MD_CharacterSetCode

[1]

DataSource.encoding

parentIdentifier

EntryGroup.id if Entry.is_partial

[0] or [1]

Only for partial Entries a Parent Entry is mandatory.

hierachyLevel

'dataset'

MD_ScopeCode

[1]

All other values are not applicable for metacatalog.

hierachyLevelName

[0..*]

Conditional: not applicable if hierachyLevel == 'dataset'

contact

Entry.author | Entry.contributors

CI_ResponsibleParty, CI_RoleCode

[1..*]

In metacatalog, the first author is contact person.

dateStamp

Entry.lastUpdate

ISO 19103

[1]

either creation or last edit

metadataStandardName

'ISO19115-2'

[1]

As of now ISO is the only standard used. More can be added here

metadataStandardVersion

ISO19115-2:2019'

[1]

Not entirely sure about this

locale

'en-US.utf8'

ISO 19139

[0..*]

Not sure about the format

metadataLinkageURL

[0..*]

The specific installation of metacatalog has to define this.

spatialRepresentationInfo

MD_SpatialRepresentation

[0..*]

Recommended for INSPIRE topic ‘elevation’

referenceSystemInfo

MD_ReferenceSystem

[1]

In INSPIRE [1..*] cardinality. EPSG:4326 for metadata

metadataExtensionInfo

MD_MetadataExtensionInformation

[0..*]

Details.value if Details.thesaurus_id not None

identificationInfo

MD_Identification

[1..*]

Many possible. Only the first occarance is used for INSPIRE

contentInfo

DataSourceType.name & Variable.name

MD_ContentInformation

[1..*]

this does not map exactly, but is optional in ISO anyway

distributionInfo

MD_Distribution

[1]

INSPIRE requires distribution information. Has to be implemented by metacatalog admin.

dataQualityInfo

DQ_DataQuality

[1..*]

not yet implemended

portrayalCatalogueInfo

MD_PortrayalCatalogueReference

[0..*]

This has to be defined outside metacatalog.

applicationSchemaInfo

MD_ApplicationSchema

[0]

Not sure if metacatalog can implement this at all.

+
+
+

MD_Indentification#

+ +++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

ISO field

metacatalog

CodeList or Table

Cardinality

description

citation

Entry.citation

CI_Citation

[1]

abstract

Entry.abstract

[1]

The abstract may contain a details table (Entry.details_table(fmt=’markdown’))

purpose

[0]

The purpose goes into the abstract in metacatalog

status

MD_ProgressCode

[0..1]

not yet implemented.

pointOfContact

Entry.author

CI_ResponsibleParty

[1..*]

In metacatalog, this is a double entry to contact. Filled by Entry.author

resourceMaintenance

MD_MaintenaceInformation

[0..*]

as of now, no planned implementation

graphicOverview

MD_BrowseGraphic

[0..*]

as of now, no planned implementation.

descriptiveKeywords

Keyword

MD_Keywords

[1..*]

I have no idea, what the forced Keyword is…

resourceSpecificUsage

MD_Usage

[0..*]

as of now, no planned implementation

resourceConstraints

License

MD_Constrains

[1..*]

only a few values are allowed within metacatalog

aggregationInfo

EntryGroup

MD_AggregationInformation

[0..*]

only a few values are alled within metacatalog

+
+
+

MD_DataIdentification#

+ +++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

ISO field

metacatalog

CodeList or Table

Cardinality

description

spatialRepresentationType

'raster' or 'vector'

MD_SpatialRepresentation_TypeCode

[1..*]

'raster' for raster data-types, 'vector' else.

spatialResolution

DataSource.spatial_scale

MD_Resolution

[0..*]

if applicable (DataSource.spatial_scale can be None)

language

'en-US'

ISO 639-2

[1]

metacatalog is as of now not multi-language

characterSet

'utf8'

MD_CharacterSetCode

[1]

it is recommended to only use UTF-8 encodings

topicCategory

Keyword.value

MD_TopicCategoryCode

[1..*]

This may be mappable from Keywords

environmentDescription

Entry.abstract

[0..1]

If important, should go into the abstract

extent

SpatialScale.extent TemporalScale.extent

Ex_Extent

[1..*]

not sure if temporal scale is supported by ISO

supplementalInformation

DataSource.args

[0..1]

This may not be helpful on export

+
+
+

MD_BrowseGraphic#

+
+

Note

+

The MD_Identification.graphicOverview is as of now not implemented in metacatalog. +Currently, no implementation is planned.

+
+
+
+

MD_Keywords#

+ +++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

ISO field

metacatalog

CodeList or Table

Cardinality

description

keyword

Keyword.full_path

[1..*]

Keyword hierachies will always be separated by ' > '

type

'theme'

MD_KeywordTypeCode

[0..1]

in metacatalog keywords are: 'topic > term > ...'

thesaurusName

Keyword.thesaurusName

CI_Citation

[0..1]

Keyword.thesaurusName is a read only

+
+

Note

+

To reference a thesaurus, the thesaurus name is needed. +Then, the url of the given Keyword is given as +thesaurusUrl. The system, which operates metacatalog +has to be able to create a valid CI_Citation. +The thesaurusName.citedResponsibleParty is represented +by an organisationName given by thesaurus_organisation +and contactInfo, which is an OnlineResource of linkage +given as thesaurus_url.

+
+
+
+

MD_RepresentativeFraction#

+

Has only one field: denominator, which is the ISO 19103 scale. Applies only to +raster sources in metacatalog. See MD_Resolution.

+
+
+

MD_Resolution#

+

Has only one of two fields. The resolution applies only to raster sources in metacatalog +and is either a MD_RepresentativeFraction (scale) or a ground distance stored in the +field distance. If DataSource has a +spatial scale, the SpatialScale.resolution +can be used to give the ground distance.

+
+
+

MD_Usage#

+
+

Note

+

Metacatalog does not store the usage information in extra fields, but they can be +extracted from existing ISO fields, that are extended in metacatalog

+
+ +++++++ + + + + + + + + + + + + + + + + + + + + + + +

ISO field

metacatalog

CodeList or Table

Cardinality

description

specificUsage

Entry.abstract

[1]

This information is added to the abstract in metacatalog

userContactInfo

Entry.author

[1]

metacatalog defines the first author as a universal contact person

+
+
+

MD_AggregateInformation#

+ +++++++ + + + + + + + + + + + + + + + + + + + + + + +

ISO field

metacatalog

CodeList or Table

Cardinality

description

aggregateDataSetIdentifier

Entry.associated_groups.entries.uuid

MD_Identifier

[1]

can either implement the UUID or full MD_Identifier

associationType

EntryGroupType.name

DS_AssociationTypeCode

[1]

not all types are mapped into metacatalog

+
+
+

MD_Constraints#

+ +++++++ + + + + + + + + + + + + + + + + +

ISO field

metacatalog

CodeList or Table

Cardinality

description

useLimitation

License.full_text

[1]

This is redunant to meet INSPIRE

+
+

Note

+

There is an onging debate about this field between INSPIRE and GDI-DE. At the moment the +useLimitations are meant to describe use-cases where the data is not applicable. But it +is a mandatory field and it is not possible to leave it blank. DGI-DE is duplication the +useConstraints from MD_LegalConstraints into this field to satisfy ISO 19115 and INSPIRE.

+
+
+
+

MD_LegalConstraints#

+ +++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

ISO field

metacatalog

CodeList or Table

Cardinality

description

accessConstraints

MD_RestrictionsCode

[C..0]

not mapped in metacatalog

useConstraints

'otherRestrictions'

MD_RestrictionsCode

[1]

note the warning below!

otherConstraints

License.full_text

[1]

note the warning below!

+
+

Note

+

ISO 19115 makes the fields accessConstraints, useConstraints and otherConstraints +dependent on each other. otherConstraints is mandatory and either accessConstraints or +useConstraints need at least a reference 'otherRestrictions' as a value to reference +the field. All of them together are needed to set the legal framework of working with data. +GDI-DE is trying to unify this semantic und suggests to duplicate open data licenses into +all of these fields. They also have a suggestion how to store open data licenses (which +only applies to the german geodata infrastructure and is therefore not a part of metacatalog.)

+
+
+

Warning

+

If you implement metacatalog in your application, you have to make sure, that the license +information is mapped into ISO 19115 accordingly. Other restrictions and use limitation do +not apply as metacatalog is made for open data. If you wish to store private or restricted +information, you will need a security, authorization and authentification middleware as +metacatalog does not handle these issues.

+
+
+
+

MD_SecurityConstraints#

+ +++++++ + + + + + + + + + + + + + + + + +

ISO field

metacatalog

CodeList or Table

Cardinality

description

classification

'unclassified'

MD_ClassificationCode

[1]

note the warning below

+
+

Warning

+

Please also see MD_LegalConstraints. The metadata in metacatalog is always +'unclassified'. If you wish to implement classified information, you need a +security middleware. However, ISO 19115 and INSPIRE define this field as +mandatory and you have to include it.

+
+
+
+

DQ_DataQuality#

+ +++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

ISO field

metacatalog

CodeList or Table

Cardinality

description

scope

'dataset', 'series'

DQ_Scope

[1]

see note below

report

DQ_Element

[1..*]

not yet implemented

lineage

LI_Lineage

[1..*]

not yet implemented

+
+

Note

+

The scope is always 'dataset' for Entry and 'series' for EntryGroup. +The DQ_Scope can then be filled automatically, as both entries do not need user-information +to fill the other DQ_Scope fields and are therefore not implemented into metacatalog.

+
+
+

Note

+

All related data-quality tables are not mapped into metacatalog as the implementation +is still in discussion.

+
+
+
+

MD_MaintenaceInformation#

+
+

Note

+

Currently, there are no plans to implement MD_MaintenaceInformation

+
+
+
+

MD_SpatialRepresentation#

+
+

Note

+

This only applies to 'raster' and 'vector' data types, which can be derived from +a data source type. +Any further implementations are not planned.

+
+
+
+

MD_ReferenceSystem#

+
+

Note

+

Metacatalog stores all geographic information in EPS:4326, WGS84 and you can therefore +handle the reference system. If the data uses different reference systems, the DataSource +will be able to handle this information with the next revision.

+
+
+

Warning

+

Due to technical reasons, the DataSource can not yet handle CRS information. Please store +only EPSG:4326 referenced data in metacatalog. This will be resolved with one of +the future releases.

+
+
+
+

MD_ContentInformation#

+
+

Note

+

The MD_ContentInformation and all related entities are not yet implemented in metacatalog. +As metacatalog only uses a very limited amount of the defined values, DataSource and +Variable will be mappable to MD_ContentInformation in the future.

+
+
+
+

MD_PortrayalCatalogueReference#

+
+

Note

+

There are currently no plans to implement portayal information into metacatalog. +But these records would have a m:1 relationship to Entry and can be +implemented outside metacatalog in a data-delivery middleware.

+
+
+
+

MD_Distribution#

+
+

Note

+

By the use of I/O extensions, almost any format and way +of distributing data can be implemented into metacatalog. +It is recommended to append distribution information on export +filling the fields accordingly. Some of the fields can be +determined by following metacatalog’s data types.

+

The distributor will always be the authority running the +metacatalog installation (not the data owner!)

+
+ +++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

ISO field

metacatalog

CodeList or Table

Cardinality

description

testData

[0]

this is not mapped in metacatalog

distributionFormat

'.txt' or .csv'

MD_Format

[1]

See note above.

distributor

MD_Distributor

[1]

metacatalog admin

transferOptions

MD_DigitalTransferOptions

[0..*]

Depending on the distribution system

+
+

Note

+

If the DataSourceTypes should be used for distribution, an CI_OnlineResource +with the DataSource.path as linkage can be automatically derived.

+
+
+

Note

+

All other entities related to distribution are not part of metacatalog and have to be +added, specifying the ways how the data can be requested and who is responsible. +The I/O Extentions might store some useful information.

+
+
+
+

MD_MetadataExtensionInformation#

+ +++++++ + + + + + + + + + + + + + + + + +

ISO field

metacatalog

CodeList or Table

Cardinality

description

extendedRoleInformation

nm_entries_details

MD_ExtendedElementInformation

[1..*]

+
+
+

MD_ExtendedElementInformation#

+
+

Note

+

MD_ExtendedElementInformation can be realized by Details that relate a +Thesaurus with public URI.

+
+ +++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

ISO field

metacatalog

CodeList or Table

Cardinality

description

name

Details.key

[1]

shortName

Details.stem

[0..1]

can be omitted if 'codeListElement'

domainCode

[1]

3-digit integer code. No idea for what.

definition

Detail.description

[1]

obligation

MD_ObligationCode

[0]

condition

[0..1]

Can be omitted because obligation cannot be 'conditional'.

dataType

characterString

MD_DataTypeCode

[1]

models.Detail.value is always string

maximumOccurence

1

[1]

A key may not be duplicated on the same Entry

domainValue

'any'

[1]

key=value are arbitrary.

parentEntity

MD_Metadata

[1..*]

in metacatalog Detail is bound to Entry

rule

'descriptive Value'

[1]

Detail is always specifying Entry. You can set other text.

rationale

Detail.description

[0..1]

the description may contain a rationale

source

Entry.author

[1]

metacatalog specifies the author to be the source

+
+
+

MD_ApplicationSchema#

+
+

Note

+

There are no plans to implement MD_ApplicationSchema.

+
+
+
+

Ex_Extent#

+ +++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

ISO field

metacatalog

CodeList or Table

Cardinality

description

description

[0..1]

Only one field of Ex_Extent can to be filled

geographicElement

EX_GeographicExtent

[0..1]

Only one field of Ex_Extent can to be filled

temporalElement

EX_TemporalExtent

[0..1]

Only one field of Ex_Extent can to be filled

verticalElement

EX_VerticalElement

[0]

verticalElements are not mapped in metacatalog

+
+
+

EX_GeographicExtent#

+

The geographic extent is always given as bounding box in +EPSG:4326, if applicable. Other values and objects defined in +IOS 19115 are not supported by metacatalog.

+
+
+

EX_TemporalExtent#

+

ISO 19115 only requires a EX_TemporalExtent.extent value, which has to be +a ISO 19108 time range. The TemporalScale +has a start and a end property and any instance of it can return an +ISO 19108 time range.

+
+
+

EX_VerticalExtent#

+
+

Note

+

EX_VerticalExtent cannot be mapped in metacatalog

+
+
+
+

CI_Citation#

+ +++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

ISO field

metacatalog

CodeList or Table

Cardinality

description

title

Entry.title

[1]

alternateTitle

[0..*]

not available in metacatalog

date

Entry.publication

[1]

The CI_Date is always publication in metacatalog

edition

Entry.version

[0..1]

This might change in the future

editionDate

Entry.publication

ISO 19103

[0..1]

mandatory if edition is set. It is the publication of the new Entry.version

identifier

[0]

this does not apply to metacatalog

citedResponsibleParty

CI_ResponsibleParty

[0..1]

Not implemented, but could be filled by the metacatlog admin as CI_ResponsibleParty.

presentationForm

CI_PresentationFormCode

[0]

Not implemented in metacatalog.

series

EntryGroup.uuid

CI_Series

[0..1]

only applicable for EntryGroup

otherCitationDetails

[0]

not available in metacatalog

collectiveTitle

EntryGroup.title

[0..1]

only applicable for EntryGroup

ISBN

[0]

not available

ISSN

[0]

not available

+
+
+

CI_ResponsibleParty#

+
+

Note

+

In metacatalog only two cases of using CI_ResponsibleParty are covered. Either it +is the first author of the dataset and can then be filled by Entry.author, or it +is the authority running metacatalog and CI_ResponsibleParty can automatically be +filled on export.

+
+
+
+
+

Code-Lists#

+

Metacatalog mappings are based on the CodeList dictionaries published by NOAA. +The following list gives you an idea, where and how the codes lists and the values +are implemented.

+

https://www.ngdc.noaa.gov/wiki/index.php/ISO_19115_and_19115-2_CodeList_Dictionaries#CI_DateTypeCode

+
+

CI_DateTypeCode#

+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

ISO

metacatalog

description

creation

Entry.creation

start-date of the data

publication

Entry.publication

creation date of the Entry record

revision

/

we use ISO 19115-2 lastUpdate

adopted

n.a.

not applicable

deprecated

/

not yet implemented

distribution

n.a.

not applicable as metacatalog is a distribution system. Will be the same as publication here.

expiry

n.a.

not applicable

inForce

n.a.

not applicable

lastRevision

/

not yet implemented

lastUpdate

Entry.lastUpdate

updates on every edit

nextUpdate

n.a.

not applicable

release

n.a.

metacatalog is intended for open data

superseded

/

not yet implemented

unavailable

n.a.

not applicable

validityBegins

n.a.

not applicable

validityExpires

n.a.

not applicable

+
+
+

CI_PresentationFormCode#

+

The definitions given in this list do not apply to environmental datasets. +Depending on the metacatalog instance and the metadata stored, the +CI_PresentationFormCode will apply to all data. If applicable it will be one of

+
    +
  • mapDigital

  • +
  • modelDigital

  • +
  • tableDigital

  • +
  • physicalSample

  • +
+
+

Note

+

You will have to implement this _after_ metacatalog has exported the +Entry information, if needed.

+
+
+
+

CI_RoleCode#

+
+

Note

+

The full CI_RoleCode Codelist +is implemented exactly into metacatalog.PersonRole.

+
+ + +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Roles#

id

name

description

1

author

the individual or organization whose name should appear first in the citation for the resource (for names that come after the first use co-author). while it is possible to have an author and principle investigator be the same individual or organization, author is not the same as nor synonymous with principle investigator. applicable mainly to documents, reports, memos, etc.

2

custodian

the individual or organization that has accountability and responsibility for the data and ensures appropriate care and maintenance of the resource.

3

distributor

the individual or organization that has accountability and responsibility for the data and ensures appropriate care and maintenance of the resource.

4

originator

the name of the individual or organization who is responsible for the data at the point when the data was first created. applicable for data sets that are an aggregation of two or more data sets or if the data set is the first instance of the signal having been converted into data.

5

owner

the individual or organization that has ownership of the resource.

6

pointOfContact

the individual or organization who is responsible for the initial triage of and answering questions related to the resource.

7

principalInvestigator

the individual or individuals who are the lead researchers for a grant (i.e. head of the laboratory, research group leader, etc.). if there are co-principal investigators then this field will repeat for each principle investigator. while it is possible to have a principal investigator and author be the same individual or organization, principal investigator is not the same nor synonymous with author.

8

processor

the name of the individual or organization who has processed the data in a manner such that the resource has been modified.

9

publisher

the individual or organization who prepares and issues the resource.

10

resourceProvider

the individual or organization that supplies or allocates the resource for another entity.

11

sponsor

the individual or organization who is providing sponsorship for the resource.

12

user

the individuals or organizations who are the intended consumers of the resource.

13

coAuthor

the individual(s) or organization(s) who name(s) should appear after the first name in a citation for the resource (use author to denote the first name in the citation). while it is possible to have a co-author and principal investigator/collaborator be the same individual or organization, co-author is no the same as nor synonymous with principle investigator or collaborator

14

collaborator

party who assists with the generation of the resource other than the principal investigator

15

contributor

the individuals or organizations whose contributions deserve recognition in the citation. contributor is mutually exclusive from author, co-author, principal investigator, and collaborator. use ISO MD_Identification credit field to identify individual or organizations that should be given acknowledgement only.

16

editor

the individual who has made a corrective or editorial change to the resource as part of a systematic revision process.

17

funder

the individual or organization which has provided all or part of the finances associated with the resource.

18

mediator

a class of entity that mediates access to the resource and for whom the resource is intended or useful

19

rightsHolder

he individual or organization who has ownership of the legal right to the resource.

20

stakeholder

an individual or organization who has an interest in the resource and/or is affected by or affects the actions of the resource

+
+
+

DQ_EvaluationMethodTypeCode#

+

The DQ_EvaluationMethodTypeCode +list is not yet implemented.

+
+
+

DS_AssociationTypeCode#

+

The EntryGroup maps some of the +DS_AssociationTypeCode.

+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

ISO

metacatalog

description

crossReference

/

not implemented yet

largerWorkCitation

EntryGroupType.name=='Composite'

‘Citation’ might be misleading here.

partOfSeamlessDatabase

n.a.

not sure if this applies to metacatalog

source

n.a.

not applicable, as metacatalog does not store dependencies if the data is an image

stereoMate

n.a.

not applicable

collectiveTitle

EntryGroupType.name==’Project’

applies if the Entries are grouped by Project name

dependency

Entry if Entry.is_partial==True

Entry.uuid of all Entry.is_partial==False for a partial Entry within the same composite

isComposedOf

Entry.uuid

Entry.uuid of all child Entries for a EntryGroup

revisionOf

/

not yet implemented

series

n.a.

not applicable.

+
+
+

DS_InitiativeTypeCode#

+

The InitiativeTypeCode List +does not apply to metacatalog. In cases you use a data platform around metacatalog, +which can either return aggregated datasets or processing results or datasets +that share a context, you have to implement this list to describe the type of +dataset aggregation.

+
+
+

MD_CellGeometryCode#

+

The MD_CellGeometryCode List +is extended in metacatalog by the Entry.location <metacatalog.models.Entry +and Entry.geom <metacatalog.models.Entry properties.

+
+

Note

+

Note that MD_CellGeometryCode List +is describing grid cells, therefore this section only applies to +raster datasources and is not yet implemented.

+
+ +++++ + + + + + + + + + + + + +

ISO

metacatalog

description

point

Entry.location <metacatalog.models.Entry

location is always a point in metacatalog

+
+
+

MD_CharacterSetCode#

+

The characterset of the metacatalog database is always the same as metacatalog +is not supporting multi-database installations. We recommend to use 'utf-8'.

+
+
+

MD_ClassificationCode#

+

The MD_ClassificationCode List +describes classified information. As metacatalog is designed for and dedicated to +managing open data this list does not apply.

+

However a Entry can be put into embargo for a +limited amount of time. This defaults to two years after 'publication' date. +An Entry under embargo is still 'unclassified' following +MD_ClassificationCode List +but just not visible in the system.

+
+
+

MD_CoverageContentTypeCode#

+

The MD_CoverageContentTypeCode List +is not yet implemented.

+
+
+

MD_DatatypeCode#

+

The MD_DatatypeCode List +is not implemented yet, but will be available as a lookup value for data types.

+
+
+

MD_DimensionNameTypeCode#

+

The MD_DimensionNameTypeCode List +does not apply to metacatalog, as the data can be more generalized than geometric dimensions.

+
+
+

MD_GeometricObjectTypeCode#

+

The value is always 'point' for Entry.location

+
+
+

MD_ImagingConditionCode#

+

The MD_ImagingConditionCode List +is not yet implemented, but will be available optinally, to be linked to +Detail information.

+
+
+

MD_KeywordTypeCode#

+

The MD_KeywordTypeCode +is not yet implemented. Some of the keyword types can be used to specify the controlled +keywords implemented as Keyword and some might +further specify Details. +It will be decided with Version 0.2 of metacatalog how much of this information +will be reflected within metacatalog.

+
+
+
+ + +
+ + + + + + + +
+ + + + + + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/dev/tests.html b/dev/tests.html new file mode 100644 index 00000000..88204a78 --- /dev/null +++ b/dev/tests.html @@ -0,0 +1,638 @@ + + + + + + + + + + + + E2E Tests — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

E2E Tests#

+
+

Testing in metacatalog#

+

metacatalog includes an icreasing suite of tests to assure functional correctness. +As typical unittests would need some serious amount of database mocking and be +of one magnitude larger than the database management code, it was decided to +include end-to-end tests, as they are common in javascript frameworks.

+

The tests are implemented using pytest and can be run like that. They run +common tasks like importing, editing and searching data and compare the +results of queries against expected results.

+

On this path, some minor functions might not be covered by a unittest, but +in a larger context, the tests assure that the whole application does what +it should do.

+
+

Note

+

metacatalog is a one-man project. If you spot a function that is not +covered and needs coverage, contact me via Github but be patient please.

+
+
+
+

Run#

+
+

Running tests locally#

+

The tests are designed to run on Github actions, by actually installing a +PostgreSQL database and actually uploading data into the DB. You can run +the tests locally, as well. You need to install:

+
pip install pytest pytest-cov pytest-depends
+
+
+

as these testing packages are not in the metacatalog requirements. +By simply running:

+
pytest
+
+
+

metacatalog will install a new database instance called 'test_[a-z]8 +with user postgres and password``postgres`` at port 5432. +You can overwrite these settings by the environment variables:

+
    +
  • POSTGRES_USER

  • +
  • POSTGRES_PASSWORD

  • +
  • POSTGRES_PORT

  • +
+
+

Note

+

After each test run, a DBNAME file will be added to the +metacatalog/test folder, to track the current database +between the tests. After the tests have finished, you should +remove this file. This might be done by an optional cleanup +tests in the future.

+
+
+
+

Clean up#

+

If you run a lot of local tests, or if your copy of metacatalog runs +tests after upgrading (which makes a lot of sense), you will find +yourself left with maybe hundereds of databases, as the test suite does +not drop the database.

+

Run the following chunk to delete all databases that follow the +naming convention of metacatalog tests:

+
sudo -u postgres psql -d postgres < <( sudo -u postgres psql -Atc "select 'drop database \"' || datname || '\";' from pg_database where datname like 'test_%';")
+
+
+
+

Warning

+

If you have other databases that start with test_, they will be deleted as well.

+
+
+
+
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/ext/custom.html b/ext/custom.html new file mode 100644 index 00000000..e2d70e0d --- /dev/null +++ b/ext/custom.html @@ -0,0 +1,661 @@ + + + + + + + + + + + + Custom Extensions — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Custom Extensions#

+

The basic mechanism to add new extensions to metacatalog is to +create an Interface class and call the adding method of metacatalog.

+

A new interface class has to inherit from MetacatalogExtensionInterface +and can be added like:

+
class DummyExtension(MetacatalogExtensionInterface):
+    def init_extension(self):
+        pass
+
+from metacatalog.ext import extension
+
+extension('dummy', DummyExtension)
+
+
+

You can do almost anything within the init function of the +Extension. E.g. you can add a new method to the Entry model:

+
from metacatalog.models import Entry
+import json
+
+def save(self):
+    with open('test.json', 'w') as js:
+        json.dump(self.to_dict(), js)
+
+class SaveToFileExtension(MetacatalogExtensionInterface):
+    @classmethod
+    def init_extension(cls):
+        setattr(Entry, 'saveToJson', save)
+
+
+

More complicated examples are also possible. The following example will +re-implement the __init__ function of Entry +to add a print statement on each initialization. The difference here is, that +the old __init__ functions is copied and executed inside the new +__init__ function.

+
from metacatalog.models import Entry
+from metacatalog import ext
+
+class PrintExtension(ext.MetacatalogExtensionInterface):
+    @classmethod
+    def init_extension(cls):
+        init = Entry.__init__
+        def new_init(self, *args, **kwargs):
+            init(self, *args, **kwargs)
+            print('New Entry build')
+        Entry.__init__ = init
+
+ext.extension('print', PrintExtension)
+
+
+
+
+class metacatalog.ext.base.MetacatalogExtensionInterface#
+

Abstract Base Class for Metacatalog extensions.

+

To create a new Metacatalog extension, you need define a +new interface class. This class needs at least implement +the init_extension(), which will be executed +as soon as the extension is added to metacatalog. For +this, you can use the metacatalog.ext.extension() +function.

+
+
+classmethod init_cli(subparsers: _SubParsersAction[ArgumentParser], defaults: ArgumentParser) None#
+

Add a new ArgumentParser to the metacatalog CLI. +The main CLI argument parser will call the init_cli class method of all active +extensions and pass the main subparser to the init function. The second argument +is the ArgumentParser, which holds the default +arguments, that should affect all CLI functions.

+

Example

+
from metacatalog.ext import MetacatalogExtensionInterface
+
+class MyExt(MetacatalogExtensionInterface):
+    @classmethod
+    def init_extension(cls):
+        pass
+
+    @classmethod
+    def cli(cls, args):
+        if not args.quiet:
+            print(args.foo.upper())
+
+    @classmethod
+    def init_cli(cls, subparsers, defaults):
+        myparser = subparsers.add_parser('foobar', parents=[defaults], help="Just a foobar parser")
+        myparser.add_argument('foo', type=str, help="A nonsense argument")
+        myparser.set_defaults(func=MyExt.cli)
+
+
+
+ +
+ +
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/ext/export.html b/ext/export.html new file mode 100644 index 00000000..77bad79c --- /dev/null +++ b/ext/export.html @@ -0,0 +1,814 @@ + + + + + + + + + + + + Export Extenstion — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Export Extenstion#

+
+
+class metacatalog.ext.export.ExportExtension#
+

Export functions.

+

The default ExportExtension can be used to produce raw export +functionality. The Entry has +a export function, that +will translate the requested format to a activated extension of +this name or fall back to this extension and call a function +of same name. +Raw export means, that the given Entry will be translated into a +Python dictionary and then implemented into the given file-format +as natively as possible. This does not follow any specified +standards or rules. Standard metadata formats are implemented +in separate extensions.

+

Currently, the following exports are supported:

+
    +
  • JSON

  • +
  • XML (called fast_XML)

  • +
  • pickle

  • +
  • netCDF

  • +
+
+

Note

+

The XML export is currently done with another package +(dicttoxml). This works great, but it is not possible to +adjust the exported XML to i.e. ISO19115 standard requirements. +This will be implemented with an export via lxml. +Thus, for the base version of that export, the function xml +is reserved.

+
+

If no path is specified, the native Python object that will be used +to create the file is returned. This can be very useful to pack more +than one Entry together. With path=None, the export function will +return the following objects:

+
    +
  • JSON -> str: the JSON object as string

  • +
  • fast_XML -> str: the XML representation as (decoded UTF-8) string

  • +
  • pickle -> dict: the dict underlying all export functions

  • +
  • netCDF -> xarray: the xarray used to build the netCDF file

  • +
+

Note that the export of 'pickle' and 'netCDF' can be particularly +useful without setting the path.

+
+
+classmethod fast_xml(entry: Entry, path=None, no_data=False, **kwargs)#
+

Export an Entry to XML. +If a path is given, a new file will be created. This is the fast +XML version, which will convert the metadata to custom XML tags.

+
+
Parameters:
+
    +
  • entry (metacatalog.models.Entry) – The entry instance to be exported

  • +
  • path (str) – If given, a file location for export.

  • +
  • no_data (bool) – If set to True, the actual data will not be loaded and included. +This can be helpful if the data is not serializable or very large.

  • +
+
+
Returns:
+

out – The the XML str if path is None, else None

+
+
Return type:
+

str

+
+
+

Notes

+

The content of the file will be created using a +ImmutableResultSet. +This will lazy-load sibling Entries and parent groups as needed for +an useful Metadata export. +The list of exported properties is hardcoded into this extension, but can +be overwritten. You can also import the list:

+
>>> from metacatalog.ext.export.extension import ENTRY_KEYS
+
+
+

A updated list can then be passed as kwargs:

+
>>> use_keys = [k for k in ENTRY_KEYS if not k.startswith('embargo')]
+>>> Export = metacatalog.ext.extension('export')
+>>> Export.fast_xml(entry, '/temp/metadata.xml', use_keys=use_keys)
+
+
+
+ +
+
+classmethod flat_keys(data: dict, prefix=False, delimiter: str = '.', **kwargs) dict#
+

Turn nested dictionaries into flat dictionaries by expanding +nested keys using the given delimiter

+
+ +
+
+classmethod get_data(entry: Entry, serialize=True, **kwargs) dict#
+

Return the data as UUID indexed dict

+
+ +
+
+classmethod json(entry: Entry, path: Optional[str] = None, flat=False, indent: int = 4, no_data=False, **kwargs)#
+

Export an Entry to JSON. +If a path is given, a new file will be created.

+
+
Parameters:
+
    +
  • entry (metacatalog.models.Entry) – The entry instance to be exported

  • +
  • path (str) – If given, a file location for export.

  • +
  • flat (bool) – If True, the resulting JSON will be un-nested and build formerly +nested keys like parent.child, where the delimiter defaults to +‘.’ but can be changed. +Defaults to False.

  • +
  • indent (int) – The default indentation for the JSON file

  • +
  • no_data (bool) – If set to True, the actual data will not be loaded and included. +This can be helpful if the data is not serializable or very large.

  • +
+
+
Returns:
+

out – The JSON string if path is None, else None

+
+
Return type:
+

str

+
+
+

Notes

+

The content of the file will be created using a +ImmutableResultSet. +This will lazy-load sibling Entries and parent groups as needed for +an useful Metadata export. +The list of exported properties is hardcoded into this extension, but can +be overwritten. You can also import the list:

+
>>> from metacatalog.ext.export.extension import ENTRY_KEYS
+
+
+

A updated list can then be passed as kwargs:

+
>>> use_keys = [k for k in ENTRY_KEYS if not k.startswith('embargo')]
+>>> Export = metacatalog.ext.extension('export')
+>>> Export.json(entry, '/temp/metadata.json', use_keys=use_keys)
+
+
+
+ +
+
+classmethod netcdf(entry: Entry, path=None, **kwargs)#
+

Export an Entry to netCDF or xarray. +If a path is given, a new netCDF file will be created, if path is None, +the xarray used for building the netCDF is returned.

+

Note that the common attribute no_data, which is available for the +other export functions, is not available for netCDF export. +Furthermore, the flat flag is always true, as the Python netCDF +implementation does not support nested attributes.

+
+
Parameters:
+
    +
  • entry (metacatalog.models.Entry) – The entry instance to be exported

  • +
  • path (str) – If given, a file location for export.

  • +
+
+
Returns:
+

out – The the XML str if path is None, else None

+
+
Return type:
+

str

+
+
+

Notes

+

The content of the file will be created using a +ImmutableResultSet. +This will lazy-load sibling Entries and parent groups as needed for +an useful Metadata export. +The list of exported properties is hardcoded into this extension, but can +be overwritten. You can also import the list:

+
>>> from metacatalog.ext.export.extension import ENTRY_KEYS
+
+
+

A updated list can then be passed as kwargs:

+
>>> use_keys = [k for k in ENTRY_KEYS if not k.startswith('embargo')]
+>>> Export = metacatalog.ext.extension('export')
+>>> Export.netCDF(entry, '/temp/metadata.xml', use_keys=use_keys)
+
+
+
+ +
+
+classmethod pickle(entry: Entry, path=None, flat=False, serialize=False, no_data=False, **kwargs)#
+

Export an Entry to Python dict. +If a path is given, a new file will be created.

+
+
Parameters:
+
    +
  • entry (metacatalog.models.Entry) – The entry instance to be exported

  • +
  • path (str) – If given, a file location for export.

  • +
  • flat (bool) – If True, the resulting JSON will be un-nested and build formerly +nested keys like parent.child, where the delimiter defaults to +‘.’ but can be changed. +Defaults to False.

  • +
  • serialize (bool) – If True, all output data will be converted to serializable types, +if possible. This may not work for all data formats. +If no path is given, it is recommended to set serializable to False. +Defaults to False

  • +
  • no_data (bool) – If set to True, the actual data will not be loaded and included. +This can be helpful if the data is not serializable or very large.

  • +
+
+
Returns:
+

out – The native Python dict if path is None, else None

+
+
Return type:
+

dict

+
+
+

Notes

+

The content of the file will be created using a +ImmutableResultSet. +This will lazy-load sibling Entries and parent groups as needed for +an useful Metadata export. +The list of exported properties is hardcoded into this extension, but can +be overwritten. You can also import the list:

+
>>> from metacatalog.ext.export.extension import ENTRY_KEYS
+
+
+

A updated list can then be passed as kwargs:

+
>>> use_keys = [k for k in ENTRY_KEYS if not k.startswith('embargo')]
+>>> Export = metacatalog.ext.extension('export')
+>>> Export.pickle(entry, '/temp/metadata.pickle', use_keys=use_keys)
+
+
+
+ +
+
+classmethod to_dict(entry: Entry, use_keys: List[str] = ('uuid', 'external_id', 'title', 'authors', 'abstract', 'citation', 'location_shape', 'variable', 'license', 'datasource', 'details', 'embargo', 'embargo_end', 'version', 'latest_version', 'plain_keyword_dict', 'publication', 'lastUpdate', 'comment', 'associated_groups'), serialize=True, no_data=False, clean=True, **kwargs) dict#
+

Return as dict to finally export.

+
+ +
+ +
+ + +
+ + + + + + + +
+ + + + + + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/ext/ext.html b/ext/ext.html new file mode 100644 index 00000000..e203409e --- /dev/null +++ b/ext/ext.html @@ -0,0 +1,574 @@ + + + + + + + + + + + + Extensions — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Extensions#

+
+
+
+

Overview#

+

Since version 0.2 metacatalog uses an extension system to +load functionality, that is not part of the core metacatalog +features. The package itself uses it to add data I/O operations +and export functionality.

+
+
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/ext/io.html b/ext/io.html new file mode 100644 index 00000000..a5f8cf86 --- /dev/null +++ b/ext/io.html @@ -0,0 +1,649 @@ + + + + + + + + + + + + Read / Write Extension — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Read / Write Extension#

+
+
+class metacatalog.ext.io.IOExtension(entry: Entry)#
+

Input / Output extension. +This is the default extension for all kind of CRUD operations on the +actual data described by metacatalog. It can be used on two different +levels. As a class, it offers classmethods to add and return new +functions for performing the actual +TODO: finish documentation

+
+
+append(data, **kwargs)#
+

Execute an append operation on the datasource. +To load the registered function and run the after_append converter, +you can simply call the abstractmethod template from the new +Interface like:

+
class IOInterface(IOExtensionInterface):
+    def append(self, data, **kwargs):
+        return super(IOInterface, self).append(data, **kwargs)
+
+
+
+ +
+
+delete(**kwargs)#
+

Execute a delete operation on the datasource. +To load the registered function and run the after_delete converter, +you can simply call the abstractmethod template from the new +Interface like:

+
class IOInterface(IOExtensionInterface):
+    def delete(self, **kwargs):
+        return super(IOInterface, self).delte(**kwargs)
+
+
+
+ +
+
+import_(data, **kwargs)#
+

Execute an import operation on the datasource. +To load the registered function and run the after_import converter, +you can simply call the abstractmethod template from the new +Interface like:

+
class IOInterface(IOExtensionInterface):
+    def import_(self, data, **kwargs):
+        return super(IOInterface, self).import_(data, **kwargs)
+
+
+
+ +
+
+classmethod init_extension()#
+

Add the IOExtension as an attribute to +the Entry model

+
+ +
+
+read(**kwargs)#
+

Execute a read operation on the datasource. +To load the registered function and run the after_read converter, +you can simply call the abstractmethod template from the new +Interface like:

+
class IOInterface(IOExtensionInterface):
+    def read(self, **kwargs):
+        return super(IOInterface, self).read(**kwargs)
+
+
+
+ +
+ +
+ + +
+ + + + + + + +
+ + + + + + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/ext/standards_export.html b/ext/standards_export.html new file mode 100644 index 00000000..8044540f --- /dev/null +++ b/ext/standards_export.html @@ -0,0 +1,805 @@ + + + + + + + + + + + + Standards Export Extension — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Standards Export Extension#

+
+
+class metacatalog.ext.standards_export.StandardsExportExtension#
+

Extension to export Entries in standard format. +ISO 19115 and DataCite metadata standard formats +are supported. +The exported standard is determined by the jinja +template passed to the extension functions as +parameter template_path.

+

Adds the method standards_export to Entry +which returns the metadata standard as a Python +XML ElementTree representation for the ImmutableResultSet +of the Entry. +The method create_standards_xml is added to the +API (metacatalog.api.catalog), which is used to +export Entries / ImmutableResultSets +in the database session and write the XML files +to the folder location specified in path. +Additionally, the command standards-export is +added to the metacatalog CLI. The command is used to +export one or more Entries via the command line.

+
+

Changed in version 0.8.3.

+
+

Support for DataCite export added.

+
+
+classmethod cli_create_standards_xml(args)#
+

Adds functionality to the metacatalog CLI to export metadata of +Entries in standard format as .xml files. +Export one or more Entries, which are identified by positional +argument entries. Entries can be identified by ID or UUID and +are exported in standard format. The metadata standard is specified +with argument –format. +The produced .xml file is saved to the location specified with +argument –path. +If no path is given, the .xml file is saved to the current +working directory. +Use the flag –all to export all entries in the given metacatalog +connection. +Use the flag –strict to only export well-formed and content +validated XML files (defaults to False).

+
+

New in version 0.8.2.

+
+

Example

+

With the following command, ISO 19115 XML files for the entries with +id=10 and id=20 in the default database session are created under the +specified path:

+
$ python -m metacatalog standards-export 10 20 --format iso19115 --path /path/to/store/xmls --connection default
+
+
+

Notes

+

The content of the xml files will be created using a +ImmutableResultSet. +This will lazy-load sibling Entries and parent groups as needed for +a useful Metadata export.

+
+ +
+
+create_standards_xml() None#
+
+create_standards_xml() ElementTree
+

This function can be imported from metacatalog.api.catalog

+

Create standard metadata XML file for an entry, which is found by +its id or uuid. +The XML file is saved to the folder given in path. If path +does not end with ‘.xml’, the name of the XML file is generated +with the uuid of the used ImmutableResultSet, depending on the +exported standard: +* f"iso19115_{irs_uuid}.xml". +* f"datacite_{irs_uuid}.xml” +If no path is given, the ElementTree XML representation +is returned.

+
+

New in version 0.8.1.

+
+
+
Parameters:
+
    +
  • session (sqlalchemy.Session) – SQLAlchemy session connected to the database.

  • +
  • id_or_uuid (Union[int, str]) – id or uuid of the Entry to be exported.

  • +
  • config_dict (dict) –

    Configuration dictionary, containing information about the data provider. +The following keys and their values are expected when rendering the +jinja template:

    +
    dict(
    +    contact = dict(
    +        organisationName = '',
    +        deliveryPoint = '',
    +        city = '',
    +        administrativeArea = '',
    +        postalCode = '',
    +        country = '',
    +        electronicMailAddress = ['', ''],
    +        linkage = '',
    +        linkage_name = '',
    +        linkage_description = ''
    +    ),
    +    publisher = dict(
    +        organisation_name = ''
    +    ))
    +
    +
    +

  • +
  • path (str) – Location where the .xml file is saved to. +If path ends with the name of the XML file (i.e. ends with ‘.xml’), the file is +named as given. +If path is a folder location, the name of the XML file is auto-generated with +the uuid of the ImmutableResultSet of the entry and the exported standard. +If no path is given, the class:ElementTree <xml.etree.ElementTree.ElementTree> +XML object is returned.

  • +
  • template_path (str) – Full path (including the template name) to the jinja2 template for +metadata export. This determines the metadata standard for export. +Defaults to ISO 19115 template.

  • +
  • strict

    +

    New in version 0.8.3.

    +
    +

    If strict is True, only syntactically (well-formed) and content validated +XML files are generated. +Note that in this version, DataCite XML files are never valid in terms of +content, as metacatalog does currently not provice DOIs for its datasets. +In the case of ISO 19115, content is currently not validated and a +NotImplementedError is raised. +Defaults to False.

    +

  • +
+
+
Returns:
+

xml_etree – If no path is given, the ElementTree object +representing the XML ElementTree in Python is returned. +If a path is given, the .xml is created and None is returned.

+
+
Return type:
+

Union[ElementTree, None]

+
+
+

Notes

+

The content of the file is created using a +ImmutableResultSet. +This will lazy-load sibling Entries and parent groups as needed for +a useful Metadata export.

+
+ +
+
+classmethod init_cli(subparsers, defaults)#
+

Add the parser standards-export to the metacatalog CLI and register +arguments.

+
+ +
+
+classmethod standards_export(entry_or_resultset: Union[Entry, ImmutableResultSet], config_dict: dict = {}, template_path: str = '/home/runner/work/metacatalog/metacatalog/metacatalog/ext/standards_export/schemas/iso19115/iso19115-2.j2', strict: bool = False) ElementTree#
+

Export a Entry or +ImmutableResultSet to XML. +The metadata standard is determined by the jinja2 template passed to this function +as paramter template_path. +Always returns an ElementTree object.

+

This function is added as a method to Entry +when the extension is activated.

+
+

New in version 0.7.7.

+
+
+
Parameters:
+
    +
  • entry_or_resultset (Union[Entry, ImmutableResultSet]) – The entry instance to be exported

  • +
  • config_dict (dict) –

    Configuration dictionary, containing information about the data provider. +The following keys and their values are expected when rendering the +jinja template:

    +
    dict(
    +    contact = dict(
    +        organisationName = '',
    +        deliveryPoint = '',
    +        city = '',
    +        administrativeArea = '',
    +        postalCode = '',
    +        country = '',
    +        electronicMailAddress = ['', ''],
    +        linkage = '',
    +        linkage_name = '',
    +        linkage_description = ''
    +    ),
    +    publisher = dict(
    +        organisation_name = ''
    +    )
    +    )
    +
    +
    +

    It is also possible to create a .json file standards_export_contact.json +containing the contact information and add the path to this file to the +metacatalog CONFIGFILE under the top level key extra:

    +
    "extra":{
    +    "standards_export_contact": "/path/to/standards_export_contact.json"
    +    }
    +
    +
    +

  • +
  • template_path (str) – Full path (including the template name) to the jinja2 template for +metadata export. This determines the metadata standard for export. +Defaults to ISO 19115 template.

  • +
  • strict (bool) –

    +

    New in version 0.8.3.

    +
    +

    If strict is True, only syntactically (well-formed) and content validated +XML files are generated. +Note that in version v0.8.3, DataCite XML files are never valid in terms of +content, as metacatalog does currently not provice DOIs for its datasets. +In the case of ISO 19115, content is currently not validated and a +NotImplementedError is raised. +Defaults to False.

    +

  • +
+
+
Returns:
+

xml_etree – The ElementTree object +representing the XML ElementTree in Python.

+
+
Return type:
+

xml.etree.ElementTree.ElementTree

+
+
+

Notes

+

The content of the file is created using a ImmutableResultSet. +This will lazy-load sibling Entries and parent groups as needed for +a useful metadata export.

+
+ +
+ +
+ + +
+ + + + + + + +
+ + + + + + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/genindex.html b/genindex.html new file mode 100644 index 00000000..c46d7b85 --- /dev/null +++ b/genindex.html @@ -0,0 +1,1348 @@ + + + + + + + + + + + Index — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+ + + + + +
+ + +

Index

+ +
+ A + | B + | C + | D + | E + | F + | G + | I + | J + | K + | L + | M + | N + | O + | P + | R + | S + | T + | U + | V + +
+

A

+ + + +
+ +

B

+ + + +
+ +

C

+ + + +
+ +

D

+ + + +
+ +

E

+ + + +
+ +

F

+ + + +
+ +

G

+ + + +
+ +

I

+ + + +
+ +

J

+ + +
+ +

K

+ + + +
+ +

L

+ + + +
+ +

M

+ + + +
    +
  • make_composite() (metacatalog.models.entry.Entry method) +
  • +
  • + metacatalog.api + +
  • +
  • + metacatalog.ext + +
  • +
  • + metacatalog.ext.base + +
  • +
  • + metacatalog.ext.export + +
  • +
  • + metacatalog.ext.io + +
  • +
  • + metacatalog.ext.standards_export + +
  • +
  • + metacatalog.models + +
  • +
  • + metacatalog.models.datasource + +
  • +
  • + metacatalog.models.entry + +
  • +
  • + metacatalog.models.entrygroup + +
  • +
  • + metacatalog.models.generic_data + +
  • +
  • + metacatalog.models.geometry_data + +
  • +
  • + metacatalog.models.keyword + +
  • +
  • + metacatalog.models.license + +
  • +
  • + metacatalog.models.person + +
  • +
+ +

N

+ + + +
+ +

O

+ + + +
+ +

P

+ + + +
+ +

R

+ + + +
+ +

S

+ + + +
+ +

T

+ + + +
+ +

U

+ + + +
+ +

V

+ + + +
+ + + +
+ + + + + +
+ +
+
+
+ +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/home/getting_started.html b/home/getting_started.html new file mode 100644 index 00000000..366d1217 --- /dev/null +++ b/home/getting_started.html @@ -0,0 +1,559 @@ + + + + + + + + + + + + Getting Started — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Getting Started#

+
+

Note

+

Will will follow soon

+
+
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/home/home.html b/home/home.html new file mode 100644 index 00000000..9ed31b3b --- /dev/null +++ b/home/home.html @@ -0,0 +1,592 @@ + + + + + + + + + + + + Home — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Home#

+
+
+
+

How the docs work#

+

Metacatalog is a management tool for a PostgreSQL/PostGIS database that is +primarily used to store meta-data of environmental open data. While it can +also store the data itself, it is more meant as a Meta-database that should +rather interface the original data stores.

+

Metacatalog has three main submodules:

+ +

Please refer to each section to learn more about each submodules. +In general terms, the Models give you great freedom in adding, editing and +changing metadata and use sqlalchemy to search the +database. But the models are only the Python classes that model the metadata. +You will have to implement all steps to manage the data and check integrity yourself.

+

The command line interface offers some robust functionality to automate some +common tasks and quickly add some entries. But the CLI gives you less freedom and +some of the API and Model functionality is not implemented in the CLI.

+

The Python API gives you the best balance between abstraction and usability. Usually, +you will import the api and do all necessary work on the database from this entrypoint.

+
from metacatalog import api
+
+
+
+
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/home/install.html b/home/install.html new file mode 100644 index 00000000..93b62ae0 --- /dev/null +++ b/home/install.html @@ -0,0 +1,625 @@ + + + + + + + + + + + + Installation — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Installation#

+
+

Prerequisites#

+

First you need to install PostgreSQL and the PostGIS extension. +You can find the PostgreSQL installer for Windows on the official PostgreSQL website. +Make sure that the stack installer is installed during the PostgreSQL installation process to install the PostGIS extension as well.

+

On Linux the commands might look similar to:

+
sudo apt install postgresql postgis
+
+
+

PostGIS will in many cases be a rather outdated version. This is up to now not a big issue, as +metacatalog uses only a limited amount of spatial functions. Anything > v2.0 should be fine.

+

Next, you need to install the database and create the PostGIS extension. In this example, the chosen database name is ‘metacatalog’. +You can create the database and the extension in the GUI application pgAdmin, which is installed together with PostgreSQL or +you can open a SQL console to postgresql or use psql:

+
create database metacatalog with encoding='UTF8';
+create extension postgis;
+
+
+
+
+

Install metacatalog#

+

You can install metacatalog from PyPI

+
pip install metacatalog
+
+
+
+
+

Create Tables#

+
+

Note

+

Refer to the CLI command create, populate and +init for more detailed information.

+
+

After the database has been installed, you can use the metacatalog CLI +to create the necessary tables. +Follow the syntax of the init command and replace driver, user, password, host and database with your parameters

+
+

Note

+

All parameters can be accessed in pgAdmin or have been previously set by the user.

+
+
metacatalog init --connection driver://user:password@host:port/database
+
+
+

When using Windows this command can lead to errors and must be changed in this case (refer to metacatalog CLI):

+
python -m metacatalog init --connection driver://user:password@host:port/database
+
+
+

The (standard) connection command could look like this:

+
metacatalog init --connection postgresql://postgres:\ *yourpassword*\ @localhost:5432/metacatalog
+
+
+
+

Note

+

If you get a FileNotFoundError when first running the init command, try to (re)install the shapely package with conda install shapely.

+
+
+
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/home/scheme.html b/home/scheme.html new file mode 100644 index 00000000..a589792c --- /dev/null +++ b/home/scheme.html @@ -0,0 +1,771 @@ + + + + + + + + + + + + Metadata Overview — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Metadata Overview#

+
+

Available metdata entities#

+

The following table sumamrizes all Metadata information that can be stored in metacatalog.

+ + ++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Metadata descriptions#

header

description

user_provided

mandatory

external_id

If the metadata is referencing the dataset in another system (i.e. on export), the external id should identify the original metadata set

yes

no

uuid

Version 4 universal unique identifier, that is assigned by the system.

no

yes

title

The title of the dataset. It should delimit the dataset from similar, i.e. within the same campain. Use ie. IDs, or variable names

yes

yes

citation

Citation information, how the dataset should be cited. If empty, a default standard citation style will be applied on export.

optional

yes

abstract

Detailed description of the dataset. Give as much information as possible in a natural english text. The abstract is full-text searchable. Please do also include any information about data proovenance and quality if available

yes

yes

author

The one main author of the dataset.

yes

yes

coAuthors

List of additional co-authors to the dataset. They have the same structure as the first author but hold an additional order key to set the order of coAuthors. +Note: Technically, other relationships than coAuthor can be set. On export to DataCite or ISO19115, anyone, who is not an author will become a coAuthor.

yes

no

author.first_name

First name for real persons

yes

yes

author.last_name

Last name for real persons

yes

yes

author.affiliation

The persons affiliation if applicable.

yes

no

author.order

only applies to coAuthors. Can be used to set the order of contributors

yes

no

contributors, editors, publishers, rightHolders, owners, originators

All lists of persons, just like coAuthors, but of different relationship. All are optional, one or more can be set. Persons can be in more than one list, except for contributors, which is mutually exclusive. Please note that all lists will be transformed to coAuthors on metadata export.

optional

no

location

WGS84 coordinates of the dataset. Important: this is only used as a reference location, thus only POINT geometry is allowed. If not applicable use the geometric center of the original geometry

yes

yes

geom

WGS84 multi-geometry collection if the geometry of the dataset cannot be represented by a POINT (location) properly. Add a description to the abstract if used

yes

no

variable

The name of the used variable. Note, that the main design decision of the metadata scheme is that each dataset has only one variable. If more than one variable is contained in a dataset, you need to split them and create a ‘Composite’ dataset-group.

yes

yes

unit

The unit of the variable. Use SI or derived SI units, wherever possible.

yes

yes

license

Data license: Each dataset must contain usage information represented by a license. Available licenses are: +- ODbL (Open Data COmmons Open Database License) +- ODC-by (Open data COmmons Attribution License v1.0) +- CC BY 4.0 (Creative Commons Attribution 4.0 International) +CC BY-NC 4.0 (Creative Commons Attribution-NonCommercial 4.0 International) +Important: The usage of CC BY-NC is discouraged as it not an open license

yes

yes

comment

general comments, which are of more technical nature. Usually not used

yes

no

temporalScale

the Scale triplet for temporal scaled data. If you set a scale, you need to specify all three components

yes

no

temporalScale.resolution

The temporal resolution of one time-step in the dataset. Please use ISO 8601 Durations to decribe the resolution

yes

no

temporalScale.extent

The temporal extent of the dataset. Provide a list of [start, end] for XML or JSON format or observation_start, observation_end for columnar metadata formats

yes

no

temporalScale.support

The ratio of the specified resolution that is actually supported by an observation. Defaults to 1.0, which means the full-timestep. Use smaller numbers if observations do not support the full resolution. I.e. if support is 0.5 and resolution 1hour, means the observation is only representative for the 30min up to the observation timestamp.

optional

no

spatialScale

the Scale triplet for spatially scaled datasets. If you set a scale, you need to specify all three components

yes

no

spatialScale.resolution

Spatial resolution in meter. Please estimate or approximate, if the datasource does not use a meter based CRS.

yes

no

spatialScale.extent

The extent of the dataset. Whenever possible use bounding boxes here. If not applicable, a POLYGON can be used.

yes

no

spatialScale.support

The ratio of the resolution that is supported by an observation. The support is usually always 1.0 for remote sensing products. If ground truthing is available, the support can be set to the share of resolution covered by ground truthing.

optional

no

data_names

The database stores default (column) names for the data on export. data_names can overwrite these settings. If used, you must describe the names in the abstract or via details keywords

optional

no

encoding

If the dataset is exported to a file-based format, it will by default be UTF-8 endcoded. If another encoding is needed for technical reasons, the encoding can be overwritten.

optional

yes

embargo

The dataset can be kept private for the first two years after uploading, while i.e. a publication is in preparation. After this period, the dataset will be public under the specified license

optional

yes

keywords

The database implements the NASA GCMD Earth Science keywords (https://earthdata.nasa.gov/earth-observation-data/find-data/idn/gcmd-keywords). You can tag the dataset by as many as needed. As they are hierachical, only the uuid of the last element is needed.

optional

yes

details

A dataset can be described by an arbitrary amount of additonal information as key-value pairs. The keys should be short and descriptive. The values can be literals or nested structures like lists. Details can be provided as a key=value list of as nested structures ie. JSON: {“mykey”: {“value”: “foobar”, “description”: “Text what mykey and foobar is all about”}}. Additinally, keys should be described in the abstract.

yes

no

groups

In the metadata sheme, groups are used to model relations between datasets. There are three important groups, that can be specified, if applicable: +- Split-dataset: If the metadata changes for one dataset, it has to be split up into two datasets, with their own metadata. A Split-dataset group will merge the data together again on export. +- Composite-dataset: If this variables must not or cannot be used without another dataset, you can indicate a composite dataset, that will always export siblings along with the data. +- Labeled-dataset: If there is any structuring within the Project, that groups datasets together, but not in a strict sense like a composite, a Label can be used. This is usually a Site or a place name, that makes working with the data more convenient

yes

no

group.title

The title of the group. For Composites and Split-datasets, the same title for the group and each dataset is commonly used.

yes

no

group.description

If necessary, an additional description for the group

yes

no

project

Any number of datasets, that were collected within the same context can be grouped together by a project. Technically, this is also a group, but it does not cause any lazy-loading of other datasets and has only descriptive character. If not specified, the original network is used as project, if applicable

optional

no

+
+
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 00000000..14e796e1 --- /dev/null +++ b/index.html @@ -0,0 +1,624 @@ + + + + + + + + + + + + Welcome to Metacatalog’s documentation! — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + + +
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/models/data.html b/models/data.html new file mode 100644 index 00000000..d42bcb6c --- /dev/null +++ b/models/data.html @@ -0,0 +1,607 @@ + + + + + + + + + + + + Data Tables — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Data Tables#

+

While metacatalog is quite flexible in handling metadata of sources that are +actually stored remotely, or file based, it has some internal tables defined. +You can also add additionaly specialized data tables in your implementation +of metacatalog. For this, refer to the classes provided below.

+
+
+class metacatalog.models.timeseries.Timeseries(**kwargs)#
+
+ +
+
+class metacatalog.models.geometry_data.GenericGeometryData(**kwargs)#
+
+ +
+
+class metacatalog.models.geometry_data.GeometryTimeseries(**kwargs)#
+
+ +
+
+class metacatalog.models.generic_data.DataPoint(**kwargs)#
+
+ +
+
+class metacatalog.models.generic_data.DataPoint2D(**kwargs)#
+
+ +
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/models/datasource.html b/models/datasource.html new file mode 100644 index 00000000..0aeb3ce8 --- /dev/null +++ b/models/datasource.html @@ -0,0 +1,1140 @@ + + + + + + + + + + + + DataSource — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

DataSource#

+
+
+class metacatalog.models.datasource.DataSource(**kwargs)#
+

Model to represent a datasource of a specific +Entry. The datasource further specifies +an DataSourceType +by setting a path and args.

+
+
+id#
+

Unique id of the record. If not specified, the database will assign it.

+
+
Type:
+

int

+
+
+
+ +
+
+path#
+

Path to the actual data. Depending on type, this can be a filepath, SQL +tablename or URL.

+
+
Type:
+

str

+
+
+
+ +
+
+encoding#
+

The encoding of the file or database representation of the actual +data. Defaults to 'utf-8'. Do only change if necessary.

+
+
Type:
+

str

+
+
+
+ +
+
+args#
+

Optional. If the I/O classes need further arguments, these can be stored +as a JSON-serializable str. Will be parsed into a dict and passed to the +I/O functions as **kwargs.

+
+
Type:
+

str

+
+
+
+ +
+
+type_id#
+

Foreign key referencing the :DataSourceType.

+
+
Type:
+

int

+
+
+
+ +
+
+type#
+

The referenced DataSourceType. +Can be used instead of setting``type_id``.

+
+
Type:
+

metacatalog.models.DataSourceType

+
+
+
+ +
+
+data_names#
+
+

New in version 0.3.0.

+
+

List of column names that will be displayed when exporting the data. +The columns are named in the same order as they appear in the list.

+
+
Type:
+

list

+
+
+
+ +

Example

+

There is a DataSourceType of name='internal', which handles +I/O operations on tables in the same database. The datasource itself +will then store the tablename as path. It can be linked to +Entry in a 1:n relationship. +This way, the admin has the full control over data-tables, while still using +common I/O classes.

+
+
+create_scale(resolution, extent, support, scale_dimension, commit: bool = False) None#
+

Create a new scale for the dataset

+
+ +
+
+load_args() dict#
+

Load the stored arguments from the 'args' column. +It was filled by a JSON string and will be converted as dict before. +This dict is usually used for I/O operations and passed as keyword arguments. +Therefore this is only useful for a DB admin and should not be exposed to the end-user.

+
+

New in version 0.1.11.

+
+
+ +
+
+save_args_from_dict(args_dict: dict, commit: bool = False) None#
+

Save all given keyword arguments to the database. +These are passed to the importer/adder functions as **kwargs.

+
+
Parameters:
+

args_dict (dict) – Dictionary of JSON-serializable keyword arguments that +will be stored as a JSON string in the database.

+
+
+
+

Note

+

All kwargs need to be json encodeable. This function is only useful +for a DB admin and should not be exposed to the end-user

+
+
+

See also

+

load_args

+
+
+ +
+
+to_dict(deep: bool = False) dict#
+

To dict

+

Return the model as a python dictionary.

+
+
Parameters:
+

deep (bool) – If True, all related objects will be included as +dictionary. Defaults to False

+
+
Returns:
+

obj – The Model as dict

+
+
Return type:
+

dict

+
+
+
+ +
+ +
+
+class metacatalog.models.datasource.DataSourceType(**kwargs)#
+

Model to represent a type of datasource.

+
+
+id#
+

Unique id of the record. If not specified, the database will assign it.

+
+
Type:
+

int

+
+
+
+ +
+
+name#
+

A short (64) name for the Type. Should not contain any whitespaces

+
+
Type:
+

str

+
+
+
+ +
+
+title#
+

The full title of this Type.

+
+
Type:
+

str

+
+
+
+ +
+
+description#
+

Optional description about this type

+
+
Type:
+

str

+
+
+
+ +
+

Note

+

While it is possible to add more records to the table, +this is the only Class that needs actual Python functions to +handle the database input. Usually, each type of datasource +relies on a specific importer +and reader reader that can use +the information saved in a DataSource +to perform I/O operations.

+
+
+
+to_dict(deep: bool = False) dict#
+

Return the model as a python dictionary.

+
+
Parameters:
+

deep (bool) – If True, all related objects will be included as +dictionary as well and deep will be passed down. +Defaults to False

+
+
Returns:
+

obj – The Model as dict

+
+
Return type:
+

dict

+
+
+
+ +
+ +
+
+class metacatalog.models.datasource.DataType(**kwargs)#
+

DataType is describing the type of the actual data. +The metacatalog documentation includes several default abstract +types. Each combination of +DataType and +DataSourceType can be +assigned with custom reader and writer functions.

+
+
+id#
+

Unique id of the record. If not specified, the database will assign it.

+
+
Type:
+

int

+
+
+
+ +
+
+name#
+

A short (64) name for the DataType. Should not contain any whitespaces.

+
+
Type:
+

str

+
+
+
+ +
+
+title#
+

The full title of this DataType.

+
+
Type:
+

str

+
+
+
+ +
+
+description#
+

Optional description about this DataType.

+
+
Type:
+

str

+
+
+
+ +
+
+children_list() List[DataType]#
+

Returns an dependency tree for the current datatype. +If the list is empty, there are no child (inheriting) +datatypes for the current datatype. +Otherwise, the list contains all child datatypes that +are inheriting the current datatype.

+
+ +
+
+parent_list() List[DataType]#
+

Returns an inheritance tree for the current datatype. +If the list is empty, the current datatype is a +top-level datatype. +Otherwise, the list contains all parent datatypes +that the current one inherits from.

+
+ +
+
+to_dict(deep: bool = False) dict#
+

Return the model as a python dictionary.

+
+
Parameters:
+

deep (bool) – If True, all related objects will be included as +dictionary as well and deep will be passed down. +Defaults to False

+
+
Returns:
+

obj – The Model as dict

+
+
Return type:
+

dict

+
+
+
+ +
+ +
+
+class metacatalog.models.datasource.SpatialScale(**kwargs)#
+

The SpatialScale is used to commonly describe the spatial scale at which +the data described is valid. metacatalog uses the scale triplet +(spacing, extent, support), but renames 'spacing' to 'resolution'.

+
+
+id#
+

Unique id of the record. If not specified, the database will assign it.

+
+
Type:
+

int

+
+
+
+ +
+
+resolution#
+

Spatial resoultion in meter. The resolution usually describes a grid +cell size, which only applies to gridded datasets. Use the +resolution_str property for a string representation

+
+
Type:
+

int

+
+
+
+ +
+
+extent#
+

The spatial extent of the dataset is given as a 'POLYGON'. +.. versionchanged:: 0.6.1 +From this POLYGON, a bounding box and the centroid are internally +calculated. +To specify a point location here, use the same value for easting and +westing and the same value for northing and southing.

+
+
Type:
+

geoalchemy2.Geometry

+
+
+
+ +
+
+support#
+

The support gives the spatial validity for a single observation. +It specifies the spatial extent at which an observed value is valid. +It is given as a fraction of resolution. For gridded datasets, it is +common to set support to 1, as the observations are validated to +represent the whole grid cell. In case ground truthing data is +available, the actual footprint fraction of observations can be +given here. +Defaults to support=1.0.

+
+
Type:
+

float

+
+
+
+ +
+
+to_dict(deep: bool = False) dict#
+

Return the model as a python dictionary.

+
+
Parameters:
+

deep (bool) – If True, all related objects will be included as +dictionary. Defaults to False

+
+
Returns:
+

obj – The Model as dict

+
+
Return type:
+

dict

+
+
+
+ +
+ +
+
+class metacatalog.models.datasource.TemporalScale(*args, **kwargs)#
+

The TemporalScale is used to commonly describe the temporal scale at which +the data described is valid. metacatalog uses the scale triplet +(spacing, extent, support), but renames 'spacing' to 'resolution'.

+
+
+id#
+

Unique id of the record. If not specified, the database will assign it.

+
+
Type:
+

int

+
+
+
+ +
+
+resolution#
+

Temporal resolution. The resolution has to be given as an ISO 8601 +Duration, or a fraction of it. You can substitute standalone minutes can +be identified by non-ISO 'min'.

+
resolution = '15min'
+
+
+

defines a temporal resolution of 15 Minutes. The ISO 8601 is built like:

+
'P[n]Y[n]M[n]DT[n]H[n]M[n]S'
+
+
+
+
Type:
+

str

+
+
+
+ +
+
+observation_start#
+

Point in time, when the first observation was made. +Forms the temporal extent toghether with observation_end.

+
+
Type:
+

datetime.datetime

+
+
+
+ +
+
+observation_end#
+

Point in time, when the last available observation was made. +Forms the temporal extent toghether with observation_start.

+
+
Type:
+

datetime.datetime

+
+
+
+ +
+
+support#
+

The support gives the temporal validity for a single observation. +It specifies the time before an observation, that is still +represented by the observation. +It is given as a fraction of resolution. +I.e. if support=0.5 at resolution='10min', the observation +supports 5min (5min before the timestamp) and the resulting dataset +would not be exhaustive. +Defaults to support=1.0, which would make a temporal exhaustive +dataset, but may not apply to each dataset.

+
+
Type:
+

float

+
+
+
+ +
+
+to_dict(deep: bool = False) dict#
+

Return the model as a python dictionary.

+
+
Parameters:
+

deep (bool) – If True, all related objects will be included as +dictionary. Defaults to False

+
+
Returns:
+

obj – The Model as dict

+
+
Return type:
+

dict

+
+
+
+ +
+ +
+ + +
+ + + + + + + +
+ + + + + + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/models/entry.html b/models/entry.html new file mode 100644 index 00000000..0f0c3205 --- /dev/null +++ b/models/entry.html @@ -0,0 +1,1542 @@ + + + + + + + + + + + + Entry — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Entry#

+

The Entry is the core class of metacatalog. It represents the core logical unit of the meta data model. +In principle, an Entry needs a first Author, a title, position and a license to describe +one type of environmental variable. It can hold a reference and interface to the actual data. +If a supported data format is used, Entry can load the data.

+
+
+class metacatalog.models.entry.Entry(**kwargs)#
+

The Entry is the main entity in metacatalog. An object instance models a +set of metadata needed to store and manage a datasource. The Entry is not +the actual data. +The Entry is designed to store all necessary information to be exportable +in ISO19115 in the scope of metacatalog. That means, Properties which are +always the same across metacatalog, or can be derived from the actual +implementation, are not part of an Entry.

+
+
+id#
+

Unique id of the record. If not specified, the database will assign it.

+
+
Type:
+

int

+
+
+
+ +
+
+uuid#
+
+

New in version 0.1.9.

+
+

Version 4 UUID string to identify the Entry across installations. +This field is read-only and will be assigned on creation. It is primarily +used to export Entry into ISO19115 metadata.

+
+
Type:
+

str

+
+
+
+ +
+
+title#
+

A full title (512) to describe the datasource as well as possible. +The truncated title (first 25 signs) is usually used to print an +Entry object to the console.

+
+
Type:
+

str

+
+
+
+ +
+
+abstract#
+

Full abstract of the datasource. The abstract should include all +necessary information that is needed to fully understand the data.

+
+
Type:
+

str

+
+
+
+ +
+
+external_id#
+

Any kind of OID that was used to identify the data in the first place. +Usually an unque ID field of other data-storage solutions. The +exernal_id is only stored for reference reasons.

+
+
Type:
+

str

+
+
+
+ +
+
+location#
+
+

Changed in version 0.6.1.

+
+

A POINT location should be specified here if there is a physical measurement +point that is different from the centroid of the spatial extent (e.g., +discharge measurement with the extent of the catchment).Otherwise, +Datasource.spatial_scale.extent +should be used to specify the location of the measured data.

+

The location as a POINT Geometry in unprojected WGS84 (EPSG: 4326). +The location is primarily used to show all Entry objects on a map, or +perform geo-searches. +The location can be passed as WKT or a tuple of (x, y) coordinates. +Note that it will be returned and stored as WKB. The output value will +be reworked in a future release

+
+
Type:
+

str, tuple

+
+
+
+ +
+
+creation#
+

Following the ISO19115 the creation date is referring to the creation +date of the data resource described by the Entry, not the Entry +itself. If creation date is not set, it is assumed, that yet no data +resource is connected to the Entry.

+
+
Type:
+

datetime.datetime

+
+
+
+ +
+
+end#
+

The last date the data source described by this Entry has data for. +The end date is not ISO19115-compliant and will be reworked.

+
+
Type:
+

datetime.datimetime

+
+
+
+ +
+
+version#
+

The version of this Entry. Usually metacatalog will handle the version +itself and there is not need to set the version manually.

+
+
Type:
+

int

+
+
+
+ +
+
+latest_version_id#
+

Foreign key to Entry.id. This key is self-referencing the another +Entry. This has to be set if the current Entry is not the latest one. +If latest_version_id is None, the Entry is the most recent one and +database operations that find multiple entries will in a future release +filter to ‘version duplicates’.

+
+
Type:
+

int

+
+
+
+ +
+
+is_partial#
+
+

New in version 0.1.10.

+
+

If an Entry is partial, it is not self-contained and has to be part +of a EntryGroup of type +composite.

+
+

Note

+

To make it possbile to add partial Entrys via the models submodule, +The Entry class itself will not check integrity. This has to +be done on adding partial Entry records, or by checking the database

+
+
+
Type:
+

bool

+
+
+
+ +
+
+comment#
+

Arbitrary free-text comment to the Entry

+
+
Type:
+

str

+
+
+
+ +
+
+citation#
+
+

New in version 0.1.13.

+
+

Citation informatio for this Entry. Note, that metacatalog does not +assign DOIs and thus a citation is only useful if the associated +data has a DOI and the bibliographic information applies to the Entry +as well. +.. note:

+
Metacatalog does not manage bibliography. Thus it is highly
+recommended to use thrid party software for management and only
+export the reference to the resource in a common citation style.
+
+
+
+
Type:
+

str

+
+
+
+ +
+
+license#
+

Data License associated to the data and the metadata. You can pass +the License itself, or use the +license_id attribute.

+
+
Type:
+

metacatalog.models.License

+
+
+
+ +
+
+license_id#
+

Foreign key to the data license.

+
+
Type:
+

int

+
+
+
+ +
+
+author#
+

Person that acts as first author +for the given entry. Only one first author is possible, co-authors can +be requested from either the contributors list or the +authors property. author is a property and setting a +new author using this property is not supported.

+
+
Type:
+

metacatalog.models.Person

+
+
+
+ +
+
+authors#
+

List of Person. The first element +is the first author, see author. The others are +Person of 'coAuthor' ``. +The list of authors is sorted by the ``order attribute. +authors is a property and setting a new list of authors using this +property is not supported.

+
+
Type:
+

list

+
+
+
+ +
+

Note

+

One Entry object instance is always described by exactly one variable. +If a datasource is a composite of many datasources, there are two +strategies. Either a new table can be implemented and an abstract +Variable be added. This is done with +Eddy-Covariance data. Secondly, Each variable of the datasource can be +represented by its own Entry, which get then grouped by an +EntryGroup of EntryGroupType 'composite'.

+
+
+

See also

+

EntryGroup +`EntryGroupType

+
+
+
+add_details(details=None, commit: bool = False, **kwargs) None#
+

Adds arbitrary key-value pairs to this entry.

+
+
Parameters:
+
    +
  • details (list) –

    +

    New in version 0.1.8.

    +
    +

    List of dict of structure:

    +
    [{
    +    'key': '',
    +    'value': '',
    +    'description': ''
    +}]
    +
    +
    +

    where the description is optional and can be omitted. +If no descriptions are passed at all, you can also use **kwargs +to pass key=value pairs.

    +

  • +
  • commit (bool) – If True, the Entry session will be added to the +current session and the transaction is commited. +Can have side-effects. Defaults to False.

  • +
+
+
+
+ +
+
+append_data(data, **kwargs)#
+

Append data. This is only possible if a datasource is specified and +any kind of IOExtension or IOInterface is activated. By default, +the builtin IOExtension +is activated since version 0.1.12.

+

For the default interface, the datasource type and data type determine +where the data will be stored and how the data has to look like. +You can easily inherit from the +IOExtension to +customize read and write behaviour. If you import i.e. a timeseries to +the same database as metacatalog, you will need to prepared data to +to only hold an datetime index and the data to be stored.

+
+

New in version 0.1.12.

+
+
+ +
+
+property checksum: str#
+

MD5 checksum of this entry. The checksum will change if any of the linked +Metadata changes. This can be used in application built on metacatalog to +verify integrity.

+
+

New in version 0.3.8.

+
+
+ +
+
+create_datasource(path: str, type: Union[int, str], datatype: Union[int, str], commit: bool = False, **args) DataSource#
+

Create a DataCource for this +Entry. The data-source holds specific metadata about the actual type +of source, the data resides in.

+
+
Parameters:
+
    +
  • path (str) – The path to the data. This depends on the type of datasource used. +This can be a URI, database table, local path etc.

  • +
  • type (metacatalog.models.DataSourceType) – type of the datasource

  • +
  • datatype (metacatalog.models.DataType) – Data Type of the referenced source

  • +
  • commit (bool, optional) – If true, the created datasource will directly be commited to the +database. If false (default) the model will be created and linked +to this Entry, but you still need to add it to a database session +and commit the session.

  • +
+
+
Returns:
+

datasource – Returns the newly created Datasource, which is also available as +Entry.datasource.

+
+
Return type:
+

metacatalog.models.DataSource

+
+
+
+ +
+
+delete_data(delete_source=False, **kwargs)#
+

Delete data. This is only possible if a datasource is specified and +any kind of IOExtension or IOInterface is activated. By default, +the builtin IOExtension +is activated since version 0.1.12.

+

For the default interface, the datasource type and data type determine +where the data is stored and how the data will be delted. +You can easily inherit from the +IOExtension to +customize read and write behaviour.

+
+

New in version 0.1.12.

+
+
+
Parameters:
+

delete_source (bool) – If True, the DataSource will be deleted as well after the data +has been deleted.

+
+
+
+ +
+
+details_dict(full: bool = True) dict#
+

Returns the associated details as dictionary.

+
+
Parameters:
+

full (bool) – If True (default) the keywords will contain the +full info including key description, ids and +stemmed key. If false, it will be truncated to a +plain key:value dict

+
+
+
+ +
+
+details_table(fmt: str = 'html') str#
+

Return the associated details as table

+
+
Parameters:
+

fmt (string) –

Can be one of:

+
    +
  • html to return a HTML table

  • +
  • latex to return LaTeX table

  • +
  • markdown to return Markdown table

  • +
+

+
+
+
+ +
+
+export(path: Optional[str] = None, fmt: str = 'JSON', **kwargs)#
+

Export the Entry. Exports the data using a metacatalog extension. +Refer to the note below to learn more about export extensions.

+
+
Parameters:
+
    +
  • path (str) – If set, the export will be written into a file at the given +location.

  • +
  • fmt (str) – Export format. Each export extension should at least support +json and XML export.

  • +
  • **kwargs – Any other argument given will be passed down to the actual +export function.

  • +
+
+
+

Notes

+

Uses any extension prefixed with ‘export-’ activated, by passing +itself to the extension. If not format-specific extension is activated, +the default ExportExtension +will be used. A method of same name as fmt on the extension will be used. +If such a method is not present, the ‘export’ method is used and the fmt +attribute will be passed along. This can be used for format specific +extensions. +Refer to the notes about custom extensions +to learn more about writing your own export extension.

+

Consider this example:

+
from metacatalog.ext import MetacatalogExtensionInterface
+import json
+
+class RawJSONExtension(MetacatalogExtensionInterface):
+    @classmethod
+    def init_extension(cls):
+        pass
+
+    @classmethod
+    def json(cls, entry, path, **kwargs):
+        # get the dict
+        data = entry.to_dict(stringify=True)
+        if path is None:
+            return data
+        else:
+            with open(path, 'w') as f:
+                json.dump(data, f, indent=kwargs.get('indent', 4))
+
+
+

You can activate and use it like:

+

>> from metacatalog import config +>> config.load_extension(‘export’, RawJSONEXtension) +>> entry.export(path=’testfile.json’, fmt=’json’, indent=2)

+
+ +
+
+classmethod from_dict(session: Session, data: dict) Entry#
+

Create a new Entry in the database from the given dict.

+
+

New in version 0.4.8.

+
+
+

Changed in version 0.7.4: PersonRoles other than ‘author’ and ‘coAuthor’ can be +imported as well.

+
+
+
Parameters:
+

data (dict) – The dictionary to create the Entry from.

+
+
Returns:
+

entry – The newly created Entry.

+
+
Return type:
+

Entry

+
+
+

Notes

+

Currently, uploading data sources and data records is not supported.

+
+ +
+
+get_data(**kwargs)#
+

Read the data. This is only possible if a datasource is specified and +any kind of IOExtension or IOInterface is activated. By default, +the builtin IOExtension +is activated since version 0.1.12.

+
+

Changed in version 0.1.12.

+
+
+ +
+
+import_data(data, **kwargs)#
+

Import data. This is only possible if a datasource is specified and +any kind of IOExtension or IOInterface is activated. By default, +the builtin IOExtension +is activated since version 0.1.12.

+

For the default interface, the datasource type and data type determine +where the data will be stored and how the data has to look like. +You can easily inherit from the +IOExtension to +customize read and write behaviour. If you import i.e. a timeseries to +the same database as metacatalog, you will need to prepared data to +to only hold an datetime index and the data to be stored.

+
+

Changed in version 0.1.12.

+
+
+ +
+
+io_interface#
+

alias of IOExtension

+
+ +
+
+keyword_list() List[str]#
+

List of tagged keywords associated to this instance. +The keywords are related via the association table.

+
+ +
+
+keywords_dict()#
+
+ +
+
+make_composite(others: List['Entry'] = [], title: str = None, description: str = None, commit: bool = False) EntryGroup#
+

Create a composite EntryGroup from this Entry. A composite marks +stand-alone (is_partial = False) entries as inseparable. +A composite can also contain a partial Entry +(is_partial = True), whichs data only makes sense in the +context of the composite group.

+
+
Parameters:
+
    +
  • others (list of Entry) – The other Entries that +should be part of the composite.

  • +
  • title (str) – Optional title of the composite, if applicable

  • +
  • description (str) – Optional description of the composite if applicable

  • +
  • commit (bool) – If True, the newly created Group will be persisted in the +database. Defaults to False.

  • +
+
+
Returns:
+

composite – The newly created EntryGroup of EntryGroupType.name == ‘Composite’

+
+
Return type:
+

metacatalog.models.EntryGroup

+
+
+
+ +
+
+neighbors(distance: Union[int, float], unit: str = 'meter', buffer_epsg: int = 3857, as_sql: bool = False, **kwargs) List[Entry]#
+

Find neighboring Entries around the +location of this instance. You can return the result, or the sqlalchemy +Query object, which can be printed as plain SQL.

+
+
Parameters:
+
    +
  • distance (int, float) – The maximum distance at which another Entry is still considered to be a neighbor.

  • +
  • unit (str) – Has to be one of [‘meter’, ‘km’, ‘mile’, ‘nautic’] to specify the unit +of the given distance. Note that the distance will always be transformed +into meter.

  • +
  • buffer_epsg (int) –

    The EPSG identification number of any projected cartesian coordinate +reference system that uses meter as unit. This CRS will be used to +apply the search distance (in meter).

    +
    +

    Note

    +

    The default system is the transversal Mercartor projection, which is +a global system. Thus, it can always be applied, but may introduce +large uncertainties in small areas. Replace this attribute by a +local CRS wherever possible.

    +
    +

  • +
  • as_sql (bool) – If False (default) the SQL query for neighbors will be executed and +the result is returned. Else, the SQL query itself will be returned.

  • +
  • kwargs (keyword arguments) – Any passed keyword argument will be passed down to the +api.find_entry function to further +filter the results.

  • +
+
+
+
+

See also

+

around +find_entry

+
+
+ +
+
+plain_keywords_dict() List[Dict[str, str]]#
+

Get a list of dictionaries containing a dict representation +of each associated keyword to this Entry.

+
+ +
+
+plain_keywords_list() List[str]#
+

Returns list of controlled keywords associated with this +instance of meta data. +The List only includes the full path

+
+ +
+
+set_new_author(new_author: Person, commit: bool = False)#
+

Set a new first Author for this entry.

+
+
Parameters:
+
    +
  • new_author (metacatalog.models.Person) – The new first author. As of now the new author has to be passed as a +model instance. Passing the ID or query parameter is not yet supported.

  • +
  • commit (boolean) –

    If True, the whole Entry will commit +and persist itself to the database.

    +
    +

    Note

    +

    This will also affect other uncommited edits to the Entry.

    +
    +

  • +
+
+
+
+ +
+
+to_dict(deep: bool = False, stringify: bool = False) dict#
+

Return the model as a python dictionary.

+
+

Changed in version 0.7.4: The dictionary now contains all persons roles

+
+
+
Parameters:
+
    +
  • deep (bool) – If True, all related objects will be included as +dictionary. Defaults to False

  • +
  • stringify (bool) – If True, all values will be turned into a string, +to make the object serializable.

  • +
+
+
Returns:
+

obj – The Model as dict

+
+
Return type:
+

dict

+
+
+
+ +
+ +
+
+class metacatalog.models.entrygroup.EntryGroup(**kwargs)#
+

An EntryGroup is an association object between any number of +Entry records. The type +of association is further described by +EntryGroupType.

+
+
+id#
+

Unique id of the record. If not specified, the database will assign it.

+
+
Type:
+

int

+
+
+
+ +
+
+uuid#
+
+

New in version 0.1.9.

+
+

Version 4 UUID string to identify the Entry across installations. +This field is read-only and will be assigned on creation. It is primarily +used to export Entry into ISO19115 metadata.

+
+
Type:
+

str

+
+
+
+ +
+
+type#
+

The type is most important to give meaning to a group of +entries. Three types ship with metacatalog 'Project', 'Composite' and 'Split dataset'.

+
+
Type:
+

metacatalog.models.EntryGroupType

+
+
+
+ +
+
+type_id#
+

Foreign key to the EntryGroupType

+
+
Type:
+

int

+
+
+
+ +
+
+title#
+

A descriptive title for the Group. Maximum of 40 letters.

+
+
Type:
+

str

+
+
+
+ +
+
+description#
+

An optional full text description of the EntryGroup. This +description should contain all information necessary to +understand the grouping.

+
+
Type:
+

str

+
+
+
+ +
+
+publication#
+
+

New in version 0.1.9.

+
+

Autogenerated field giving the UTC timestamp of record creation.

+
+
Type:
+

datetime.datetime

+
+
+
+ +
+
+lastUpdate#
+

Autogenerated field giving the UTC timestamp of last edit.

+
+
Type:
+

datetime.datetime

+
+
+
+ +
+
+property checksum: str#
+
+

New in version 0.3.9.

+
+

MD5 checksum of this entry. The checksum will change if any of the linked +Entries’ checksum changes. This can be used in application built on metacatalog to +verify integrity.

+
+ +
+
+export(path: Optional[str] = None, fmt: str = 'JSON', **kwargs)#
+

Export the EntryGroup. Exports the data using a metacatalog extension. +Refer to the note below to learn more about export extensions.

+
+
Parameters:
+
    +
  • path (str) – If set, the export will be written into a file at the given +location.

  • +
  • fmt (str) – Export format. Each export extension should at least support +json and XML export.

  • +
  • **kwargs – Any other argument given will be passed down to the actual +export function.

  • +
+
+
+

Notes

+

Uses any extension prefixed with ‘export-’ activated, by passing +itself to the extension. If not format-specific extension is activated, +the default ExportExtension +will be used. A method of same name as fmt on the extension will be used. +If such a method is not present, the ‘export’ method is used and the fmt +attribute will be passed along. This can be used for format specific +extensions. +Refer to the notes about custom extensions +to learn more about writing your own export extension.

+

Consider this example:

+
from metacatalog.ext import MetacatalogExtensionInterface
+import json
+
+class RawJSONExtension(MetacatalogExtensionInterface):
+    @classmethod
+    def init_extension(cls):
+        pass
+
+    @classmethod
+    def json(cls, group, path, **kwargs):
+        # get the dict
+        data = group.to_dict(stringify=True)
+        if path is None:
+            return data
+        else:
+            with open(path, 'w') as f:
+                json.dump(data, f, indent=kwargs.get('indent', 4))
+
+
+

You can activate and use it like:

+

>> from metacatalog import ext +>> ext.extension(‘export’, RawJSONEXtension) +>> group.export(path=’testfile.json’, fmt=’json’, indent=2)

+
+ +
+
+to_dict(deep: bool = False, stringify: bool = False) dict#
+

To dict

+

Return the model as a python dictionary.

+
+

Changed in version 0.3.8: added stringify option

+
+
+
Parameters:
+
    +
  • deep (bool) – If True, all related objects will be included as +dictionary. Defaults to False

  • +
  • stringify (bool) – If True, all values will be turned into a string, +to make the object serializable.

  • +
+
+
Returns:
+

obj – The Model as dict

+
+
Return type:
+

dict

+
+
+
+ +
+ +
+
+class metacatalog.models.entrygroup.EntryGroupAssociation(**kwargs)#
+
+ +
+
+class metacatalog.models.entrygroup.EntryGroupType(**kwargs)#
+
+
+to_dict(deep: bool = False) dict#
+

To dict

+

Return the model as a python dictionary.

+
+
Parameters:
+

deep (bool) – If True, all related objects will be included as +dictionary. Defaults to False

+
+
Returns:
+

obj – The Model as dict

+
+
Return type:
+

dict

+
+
+
+ +
+ +
+ + +
+ + + + + + + +
+ + + + + + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/models/keyword.html b/models/keyword.html new file mode 100644 index 00000000..9c2061fe --- /dev/null +++ b/models/keyword.html @@ -0,0 +1,932 @@ + + + + + + + + + + + + Controlled Keywords — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Controlled Keywords#

+
+
+class metacatalog.models.keyword.Keyword(**kwargs)#
+

The major purpose of a Keyword is to describe a Metadataset by +a common word. Ideally, these keywords are hosted in a controlled +dictionary. That means a third party distributes and describes +the scope of these keywords and makes them publicly available. +Added to an Entry, the +keyword is primarily used to filter entities by keyword.

+

Keywords can be used to filter for similar Metadata in +cross-database repositories.

+
+
+id#
+

Unique id of the record. If not specified, the database will assign it.

+
+
Type:
+

int

+
+
+
+ +
+
+uuid#
+

Version 4 UUID string to identify the Thesaurus across installations. +Usually, this is created outside of metacatalog.

+
+
Type:
+

str

+
+
+
+ +
+
+parent_id#
+

Foreign key to another Keyword (keywords.id). Used to build the +hierachical order of keywords.

+
+
Type:
+

int

+
+
+
+ +
+
+value#
+

The keyword itself. Should be all uppercase letters

+
+
Type:
+

str

+
+
+
+ +
+
+full_path#
+

The full keyword including all ancestors keyword values. Usually, +the term ‘Keyword’ refers to the full path of the keyword.

+
+

Note

+

The Keyword does also have a method Keyword.path to +contruct the path by recursively querying the parents’ +values. You can use this function to store the result in +full_path for convenience.

+
+
+
Type:
+

str

+
+
+
+ +
+
+thesaurusName#
+
+

New in version 0.1.10.

+
+

If the keyword is part of a controlled dictionary, this +thesaurus should be described here. Usually, metacatalog will +only implement onr or two, which are maintained by the admin.

+
+
Type:
+

metacatalog.models.Thesaurus

+
+
+
+ +
+
+thesaurus_id#
+
+

New in version 0.1.10.

+
+

Foreign key to the associated thesaurus.

+
+
Type:
+

int

+
+
+
+ +
+
+tagged_entries#
+

List of Associations between the current keyword and +Entries tagged by this keyword. +This list is filled automatically.

+
+
Type:
+

list

+
+
+
+ +
+

Note

+

If you use additional keywords, that are already part of a +controlled dictionary, make sure not to upload them via the +metacatalog API, as this will assign new UUIDs to the keywords. +Rather use this model to upload them directly using sqlalchemy.

+

Keywords are build hierachical, separated by ' > '. +This makes it possible to filter by keywords themselves or +by groups of keywords. They have to follow the pattern:

+
category > topic > term ...
+
+
+

followed by any number of more granular groupings.

+
+ +
+
+path() str#
+

Returns the full keyword path for the given level. +The levels are separated by a ‘>’ sign. The levels are:

+

Topic > Term > Variable_Level_1 > Variable_Level_2 > Variable_Level_3 > Detailed_Variable

+
+
Returns:
+

path – The full keyword path

+
+
Return type:
+

str

+
+
+
+ +
+
+to_dict(deep: bool = False) dict#
+

Return the model as a python dictionary.

+
+

New in version 0.1.10.

+
+
+
Parameters:
+

deep (bool) – If True, all related objects will be included as +dictionary. Defaults to False

+
+
Returns:
+

obj – The Model as dict

+
+
Return type:
+

dict

+
+
+
+ +
+ +
+
+class metacatalog.models.keyword.KeywordAssociation(**kwargs)#
+

Keyword association

+

Association between keyword and entry.

+
+
+keyword_id#
+

Unique ID of the metacatalog.models.Keyword

+
+
Type:
+

int

+
+
+
+ +
+
+entry_id#
+

Unique ID of the metacatalog.models.Entry

+
+
Type:
+

int

+
+
+
+ +
+ +
+
+class metacatalog.models.keyword.Thesaurus(**kwargs)#
+

A thesaurus is a controlled dictionary, which is served at +a permanent URL. As of now, metacatalog implements NASA +Global Climate change Master Dictionary (GCMD) science keywords.

+
+

New in version 0.1.10.

+
+
+
+id#
+

Unique id of the record. If not specified, the database will assign it.

+
+
Type:
+

int

+
+
+
+ +
+
+uuid#
+

Version 4 UUID string to identify the Thesaurus across installations. +Usually, this is created outside of metacatalog.

+
+
Type:
+

str

+
+
+
+ +
+
+name#
+

Short name of the thesaurus entry. Should be 'custom' for +non-official thesaurii. Example: 'GCMD' for the NASA/GCMD Science +Keywords, which can be loaded by default.

+
+
Type:
+

str

+
+
+
+ +
+
+title#
+

Full name of the keyword list. The title should contain the full +qualified name. It is best practice to use a title the distiributor +officially uses for the list. A short name can be given as name +attribute.

+
+
Type:
+

str

+
+
+
+ +
+
+organisation#
+

Name of the publishing Organisation for the thesaurus. It has to be +the responsible party for the resource given at url.

+
+
Type:
+

str

+
+
+
+ +
+
+description#
+

Full description of the scope and context of the thesaurus. From this +description it should become obvious how keywords relate to the matadata +that is tagged by the keywords

+
+
Type:
+

str

+
+
+
+ +
+
+url#
+

URL of the full keyword list. May contain a placehoder for an UUID +if the API at the resource is capable of loading keywords by UUID.

+
+
Type:
+

str

+
+
+
+ +

Notes

+

If you add other keywords, a thesaurus isnstance has to be +specfied, which can be found at the url. If only one url is +given, a full XML list of all keywords have to be published here. +The url may contain a UUID placeholder for loading keywords +by UUID. Do not reference a WIKI page or description via URL.

+

If the server instance operating metacatalog implements custom +keyword lists, you can add a thesaurus instance here on +installation. Do not expose this table to the user.

+
+
+to_dict(deep: bool = False) dict#
+

Return the model as a python dictionary.

+
+
Parameters:
+

deep (bool) – If True, all related objects will be included as +dictionary. Defaults to False

+
+
Returns:
+

obj – The Model as dict

+
+
Return type:
+

dict

+
+
+
+ +
+ +
+ + +
+ + + + + + + +
+ + + + + + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/models/license.html b/models/license.html new file mode 100644 index 00000000..6dff3e13 --- /dev/null +++ b/models/license.html @@ -0,0 +1,756 @@ + + + + + + + + + + + + Data Licenses — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Data Licenses#

+
+
+class metacatalog.models.license.License(**kwargs)#
+

License represents usage information and limitations that is +distributed with each Entry. +If the Entry records are +further grouped by a Composite +it is highly recommended to use the same License on all childs. +metacatalog ships with a number of open data licenses and it is +recommended to only use existing data licenses.

+
+

Changed in version 0.1.9: Either full_text or link have to be not NULL

+
+
+
+id#
+

Unique id of the record. If not specified, the database will assign it.

+
+
Type:
+

int

+
+
+
+ +
+
+short_title#
+

The abbreviation of the license title (max 40 letters). Usually +Licenses are more known under their short title, like ‘GPL-3’

+
+
Type:
+

str

+
+
+
+ +
+
+title#
+

The full title of the License agreement.

+
+
Type:
+

str

+
+
+
+ +
+
+summary#
+

Optional, but highly recommended to fill. The summary should +give the user the most important information from the license +in an understandable not legal binding manner. +The license itself is stored as full_text or referenced +via link.

+
+
Type:
+

str

+
+
+
+ +
+
+full_text#
+

Full license text. This is the legally binding contract +that has to be acknowleged by the user. It specifies the +terms of usage for the reference data set. Instead of +copying the full text to metacatalog, a link to a permanent +version of the license can be given. One of either is mandatory

+
+

Note

+

It is highly recommended to use existing licenses to +assure that they are legally correct.

+
+
+
Type:
+

str

+
+
+
+ +
+ +

URI link to the full license text. If possible, make sure +that the URI links a page of type 'text/plain' to +return the license text in a user fiendly way.

+
+
Type:
+

str

+
+
+
+ +
+
+by_attribution#
+

If True (default) this dataset has to be cited on usage.

+
+
Type:
+

bool

+
+
+
+ +
+
+share_alike#
+

If True (default) this dataset has to be licensed by the +same license on distribution.

+
+
Type:
+

bool

+
+
+
+ +
+
+commercial_use#
+

If True (default) this dataset can be used for commerical +purposes

+
+
Type:
+

bool

+
+
+
+ +
+
+get_full_text() str#
+

Return the full text license code. If full_text is +not given, the full license code will be loaded +from link.

+

..versionadded:: 0.1.9

+

:raises connection_error : Exception: If the link cannot be followed or does not return + the expected output

+
+
Returns:
+

full_text – The full license text

+
+
Return type:
+

str

+
+
+
+ +
+
+to_dict(deep: bool = False) dict#
+

Return the model as a python dictionary.

+
+
Parameters:
+

deep (bool) – If True, all related objects will be included as +dictionary. Defaults to False

+
+
Returns:
+

obj – The Model as dict

+
+
Return type:
+

dict

+
+
+
+ +
+ +
+ + +
+ + + + + + + +
+ + + + + + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/models/location.html b/models/location.html new file mode 100644 index 00000000..a857a553 --- /dev/null +++ b/models/location.html @@ -0,0 +1,716 @@ + + + + + + + + + + + + Location Filter — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Location Filter#

+

The location filter module is a utility module, that is usually accessed via +The Entry.neighbors function or the +find_entry API. +However, the uitl module is importable and the functions can be used directly +for more advanced use cases.

+

Location utility functions can convert the various possible inputs +into a shapely.Polygon that can be used as a search area.

+
+
+metacatalog.util.location.around(entry, distance, unit='km', query=None, buffer_use_epsg=3857)#
+

Find other Entries around a given instance +by specifying a distance around the entry. If an sqlalchemy query is supplied, +the resulting query is returned, otherwise the filter area itself is returned.

+
+
Parameters:
+
    +
  • entry (Entry) – The Entry instance that is used to find neighbors. A list of Entry instances +is also accepted, as well as an EntryGroup, +which will resolve to a list of entries. A list will be converted to the Convex +Hull around all instances.

  • +
  • distance (int, float) – The search distance around the entry instance(s). The distance should be given +in meter, if km or miles is used, specify the unit accordingly. Also mind the +Note below.

  • +
  • unit (str) – Can be one of ['meter', 'km', 'mile', 'nautic']. The unit will be used to +convert the given distance to meter.

  • +
  • buffer_use_epsg (int) – The EPSG number of a projected CRS that shall be used during buffering. +Mind the Note below.

  • +
+
+
+
+

Note

+

metacatalog uses unprojected WGS84 coordinates for the contained coordinates. +For applying a buffer, the coordinates of the argument need to be transformed +into a projected coordinate reference system using meter as a unit. +After buffering, the search area is back-transformed to WGS84. This can have +accuracy implications. Nevertheless, this step is necessary, as the database +can then filter without reprojections of possibly large amounts of data. +By default the argument is transformed to Transversal Mercartor Projection, as +this has global coverage. Unfortunately, the transformation error can get +massive in some, especially small, study areas. Whenever possible overwrite the +CRS to be used for buffering by a local reference system like UTM.

+
+ +
+ +
+
+metacatalog.util.location.build_query(query: Query, area: Polygon) Query#
+

Small helper function to create the spatial filter.

+
+
Parameters:
+
    +
  • query (Query) – sqlalchemy orm query object. The filter will be appended +(in an AND manner) to the exisiting query.

  • +
  • area (Polygon) – shapely.geometry.Polygon of the search area. It will be +resolved by a Within spatial filter.

  • +
+
+
Returns:
+

query – The filtered (unexecuted) sqlalchemy ORM query object.

+
+
Return type:
+

Query

+
+
+
+ +
+
+metacatalog.util.location.get_search_shape(arg, buffer=None, buffer_use_epsg=3857) BaseGeometry#
+

Calculate a search shape from a whole bunch of arguments. +The search shape can optionally be buffered.

+
+

Note

+

Some arguments might resolve to a Point geometry. In these cases +make sure to use a buffer value, at least by a small value. +Otherwise the query will end up to search for exact matches, +which will always turn False, even for the Point itself.

+
+
+
Parameters:
+
    +
  • buffer (int, float) – Optional. If given, the search area resolved from the argument +will be buffered by the given value. Remind that buffer has to transform +the data to apply Euklidean buffers.

  • +
  • buffer_use_epsg (int) – The EPSG number of a projected CRS that shall be used during buffering. +Mind the Note below.

  • +
  • arg (list, str, byte, shapely.BaseGeometry) –

    A search area can be generate from a rich list of input arguments. +Depending on the data type and shape of arg, different search areas +are resolved.

    +
      +
    • str has to be a WKT of type Point or Polygon

    • +
    • byte has to be a WKB of type Point or Polygon

    • +
    • BaseGeometry any valid shapely Geometry, that implements the buffer function

    • +
    • +
      list has to be a list of int or float values. Resolving is based on shape:
        +
      • len(arg)==2 is used as Point(arg). Remind to buffer the Point

      • +
      • len(arg)==4 is a BoundingBox defined as [left, bottom, right, up]

      • +
      • 2 dimensional lists are converted to Polygons

      • +
      +
      +
      +
    • +
    +

  • +
+
+
+
+

Note

+

metacatalog uses unprojected WGS84 coordinates for the contained coordinates. +For applying a buffer, the coordinates of the argument need to be transformed +into a projected coordinate reference system using meter as a unit. +After buffering, the search area is back-transformed to WGS84. This can have +accuracy implications. Nevertheless, this step is necessary, as the database +can then filter without reprojections of possibly large amounts of data. +By default the argument is transformed to Transversal Mercartor Projection, as +this has global coverage. Unfortunately, the transformation error can get +massive in some, especially small, study areas. Whenever possible overwrite the +CRS to be used for buffering by a local reference system like UTM.

+
+
+
Returns:
+

area – The resolved Polygon, which can be used to create a spatial filter to +find Entries within the area.

+
+
Return type:
+

shapely.geometry.Polygon

+
+
+
+

See also

+

build_query, around

+
+
+ +
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/models/models.html b/models/models.html new file mode 100644 index 00000000..8c3f9bc2 --- /dev/null +++ b/models/models.html @@ -0,0 +1,584 @@ + + + + + + + + + + + + Database Models — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Database Models#

+

The metacatalog meta data model is split up into different +entities, each representend by a Python class. +Metacatalog uses sqlalchemy to model relationships between the +classes and create and populate an appropriate database instance +to store Records of these entities.

+
+

Note

+

Due to usage of the geoalchemy2 extension, which can currently only +be stored in a PostGIS enabled PostgreSQL database, only PostgreSQL +is supported. This may change in a future version.

+
+ +
+ + +
+ + + + + + + +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/models/person.html b/models/person.html new file mode 100644 index 00000000..998e6d26 --- /dev/null +++ b/models/person.html @@ -0,0 +1,797 @@ + + + + + + + + + + + + Authors & Contributors — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Authors & Contributors#

+
+
+class metacatalog.models.person.Person(**kwargs)#
+

Person represents all persons that are associated +in any kind to a dataset. This may be an author, who is +mandatory, but also an owners or processors can be +associated.

+
+

Note

+

In metatacatalog, an organisation_name is an optional, but +recommended information. On export to ISO 19115 persons without +affilated organisations can’t be exported. Thus, they should +not take the role of pointOfContact or author (first author), +because this would result in invalid ISO 19115 metadata then.

+
+
+
+id#
+

Unique id of the record. If not specified, the database will assign it.

+
+
Type:
+

int

+
+
+
+ +
+
+uuid#
+

Version 4 UUID string to identify the Entry across installations. +This field is read-only and will be assigned on creation. It is primarily +used to export Entry into ISO19115 metadata.

+
+
Type:
+

str

+
+
+
+ +
+
+first_name#
+
+

Changed in version 0.1.10: Now mandatory.

+
+

Person’s first name.

+
+
Type:
+

str

+
+
+
+ +
+
+last_name#
+

Person’s last name.

+
+
Type:
+

str

+
+
+
+ +
+
+organisation_name#
+

Optional, but highly recommended if applicable. This should be +the name of the head organisation, without department information. +The full affiliation can be defined in another attribute.

+
+
Type:
+

str

+
+
+
+ +
+
+affiliation#
+

Optional. The user may want to further specify a department or +working group for affiliation information.

+
+
Type:
+

str

+
+
+
+ +
+
+attribution#
+

Optional. The user may define an attribtion recommondation here, +which is associated to all datasets, the user is first author of. +If not given, the system running metacatalog should give automatic +and fallback information of how a dataset should be attributed.

+
+
Type:
+

str

+
+
+
+ +
+
+entries#
+

List of Entries the user is +associated to. This includes all kinds of associations, not only +author and coAuthor associations.

+
+
Type:
+

list

+
+
+
+ +
+
+classmethod from_dict(data: dict, session: Session) Person#
+

Create a new Person from a python dictionary.

+
+
Parameters:
+
    +
  • data (dict) – The dictionary containing the data

  • +
  • session (Session) – The database session

  • +
+
+
Returns:
+

obj – The new Person

+
+
Return type:
+

Person

+
+
+
+ +
+
+to_dict(deep: bool = False) dict#
+

To dict

+

Return the model as a python dictionary.

+
+
Parameters:
+

deep (bool) – If True, all related objects will be included as +dictionary. Defaults to False

+
+
Returns:
+

obj – The Model as dict

+
+
Return type:
+

dict

+
+
+
+ +
+ +
+
+class metacatalog.models.person.PersonAssociation(**kwargs)#
+
+
+to_dict(deep: bool = False) dict#
+

Return the model as a python dictionary.

+
+
Parameters:
+

deep (bool) – Parameter will be ignored, but is defined to use the same method +interface as all other models

+
+
Returns:
+

obj – The Model as dict

+
+
Return type:
+

dict

+
+
+
+ +
+ +
+
+class metacatalog.models.person.PersonRole(**kwargs)#
+
+
+to_dict(deep: bool = False) dict#
+

Return the model as a python dictionary.

+
+
Parameters:
+

deep (bool) – If True, all related objects will be included as +dictionary. Defaults to False

+
+
Returns:
+

obj – The Model as dict

+
+
Return type:
+

dict

+
+
+
+ +
+ +
+ + +
+ + + + + + + +
+ + + + + + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/models/result.html b/models/result.html new file mode 100644 index 00000000..e15b8441 --- /dev/null +++ b/models/result.html @@ -0,0 +1,785 @@ + + + + + + + + + + + + ResultSet — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

ResultSet#

+

The ImmutableResultSet class can be used to wrap a list of +Entry or +EntryGroup instances +a deliver a unified interface to load metadata from nested +and / or grouped results.

+

Example

+
>>> session = api.connect_database()
+>>> entry = api.find_entry(session, id=42)
+>>> res = ImmutableResultSet(entry)
+
+
+

This will load all sibling Entries into the result set. I.e., if +the composite entry has three members, the Composite EntryGroup +and two child Entries, the ImmutableResultSet will have three +UUIDs

+
>>> res.get('uuid')
+
+
+

>> [‘c1087040-5566-44b9-9852-ea600f73ae0c’, +>> ‘0350c985-4e43-4876-a03a-7b6fb2a3a4b6’, +>> ‘c6d6301b-8e1c-478f-9407-ce9a0c38dbb8’]

+

At the same time, if all members share a property, like their title +the ImmutableResultSet will merge matching content:

+
>>> res.get('title')
+
+
+

>> ‘Awesome Composite Dataset’

+

Note that the return type is string, like the original title, +not a list.

+

Alternatively, the API can return ImmutableResultSets if the +API has a as_result arugment. The example above can be reproduced +like:

+
>>> res = api.find_entry(session, id=42, as_result=True)
+
+
+
+

Reference#

+
+
+class metacatalog.util.results.ImmutableResultSet(instance: Union[Entry, EntryGroup, ImmutableResultSet])#
+
+
+property checksum: str#
+

Return the md5 checksum for this result set to easily +tell it appart from others. +The checksum is the md5 hash of all contained member checksums.

+
+

Changed in version 0.3.8: now hashing md5 of members instead of uuids

+
+
+ +
+
+property checksums: List[str]#
+
+

New in version 0.3.8.

+
+

Return all checksums of all members

+
+ +
+
+contains_uuid(uuid: str) bool#
+

Check if the given Entry or EntryGroup is +present in this result set

+
+ +
+
+property empty: bool#
+
+

New in version 0.4.1.

+
+

Returns True if there is no Entry +associated to this result.

+
+ +
+
+classmethod entry_set(entries: List[Entry])#
+

Return a set of entries to remove duplicates

+
+

Changed in version 0.3.8: Can now handle nested ResultSets

+
+
+ +
+
+classmethod expand_entry(entry: Entry)#
+

Expand this Entry to all siblings.

+
+

Changed in version 0.3.8: Split datasets are now nested

+
+
+

Changed in version 0.6.11: the calling entry is now always part of the expansion set.

+
+
+ +
+
+get(name: str, default=None)#
+

Get the given property from the result set. +This function will iterate all member and nested +Entries and return +the values for the given property from all childs. +It will return the set of all properties to remove +duplicated metadata (i.e. for composites and split- +datasets). If the set has a length of 1, the value +itself will be returned

+
+

Changed in version 0.3.8: get can now handle nested ImmutableResultSets

+
+
+
Parameters:
+

name (str) – The name (key) of the requested parameter

+
+
Returns:
+

result – The value for the requested property. If more than +one value is present, the of all values +will be returned.

+
+
Return type:
+

set, any

+
+
+
+ +
+
+get_data(verbose=False, merge=False, **kwargs) dict#
+

Return the data as a checksum indexed dict

+
+ +
+
+classmethod load_base_group(entry: Entry)#
+

Returns the base EntryGroup of the given Entry. +The base EntryGroup is the strongest grouping that requires expanding +of the result set, to load all siblings. +If there are no matching groups, None will be returned

+

Currently the order of importance is:

+
    +
  • 'Composite'

  • +
  • 'Split dataset'

  • +
+
+ +
+
+to_dict(orient: str = 'dict') dict#
+

Generate a full dictionary output of this result set.

+
+

Changed in version 0.6.8: Parameter orient added. The default value is ‘dict’, which returns +a dictionary with unique keys. +The value ‘uuids’ gives the dictionary with uuids as keys, this +should make it easier to i.e. loop over the uuids / entities of an +ImmutableResultSet.

+
+
+ +
+
+to_short_info() dict#
+

Get a short unified version of the results.

+
+ +
+
+property uuid: str#
+

Returns the first uuid in the ImmutableResultSet. +This is the uuid of the base group, if it exists or the uuid +of the first member. As uuids of members are sorted in +uuids(self), self.uuids[0] always returns the same +uuid for the same ImmutableResultSet.

+
+ +
+
+property uuids: List[str]#
+

Return all uuids that form this result set

+
+

Changed in version 0.6.8: If a member in in _members is an ImmutableResultSet, call this +method recursively

+
+
+

Changed in version 0.7.5: Members of ImmutableResultSets cannot be ImmutableResultSets +again, reverted recursive method call from version 0.6.8

+
+
+ +
+ +
+
+ + +
+ + + + + + + +
+ + + + + + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/models/variable.html b/models/variable.html new file mode 100644 index 00000000..d7655761 --- /dev/null +++ b/models/variable.html @@ -0,0 +1,802 @@ + + + + + + + + + + + + Variables — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + + + +
+ +
+ + +
+
+ + + + + +
+ +
+

Variables#

+
+
+class metacatalog.models.variable.Unit(**kwargs)#
+

Model to represent units.

+
+
+id#
+

Unique id of the record. If not specified, the database will assign it.

+
+
Type:
+

int

+
+
+
+ +
+
+name#
+

Full name of the Unit

+
+
Type:
+

str

+
+
+
+ +
+
+symbol#
+

A max. 12 letter symbol that is commonly used to represent the +unit

+
+
Type:
+

str

+
+
+
+ +
+
+si#
+

Optional. If applicable, the conversion if the unit into SI units. +If the unit is i.e. m/km the si would be m*1000^-1*m^-1

+
+
Type:
+

str

+
+
+
+ +
+
+variables#
+

Lazy loaded list of Variables that use the current unit

+
+
Type:
+

list

+
+
+
+ +
+
+to_dict(deep: bool = False) dict#
+

To dict

+

Return the model as a python dictionary.

+
+
Parameters:
+

deep (bool) – If True, all related objects will be included as +dictionary. Defaults to False

+
+
Returns:
+

obj – The Model as dict

+
+
Return type:
+

dict

+
+
+
+ +
+ +
+
+class metacatalog.models.variable.Variable(**kwargs)#
+

Model to represent variables. The variable is any kind of oberservation, +that can be represented by one data type. metacatalog does not take the +definition of variables too strict. It is however common to keep variables +as atomic as possbile.

+

However, technically, you can also create a new variable that describes a +combined data type and reference a newly created table via +DataSource. This can make sense if in the +scope and context of the metacatalog installation a sensor like a Decagon +5TE always records three parameters at a time like Temperature, Moisture +and Conductance. That can be implemented as a new ‘5TE’ variable and the +datasource would point to a table containing all three measurements.

+
+
+id#
+

Unique id of the record. If not specified, the database will assign it.

+
+
Type:
+

int

+
+
+
+ +
+
+name#
+

Full name of the Unit

+
+
Type:
+

str

+
+
+
+ +
+
+symbol#
+

A max. 12 letter symbol that is commonly used to represent the +unit

+
+
Type:
+

str

+
+
+
+ +
+
+si#
+

Optional. If applicable, the conversion if the unit into SI units. +If the unit is i.e. m/km the si would be m*1000^-1*m^-1

+
+
Type:
+

str

+
+
+
+ +
+
+variables#
+

Lazy loaded list of Variables that use the current unit

+
+
Type:
+

list

+
+
+
+ +
+
+column_names#
+
+

New in version 0.3.0.

+
+

List of default column names that will be displayed when exporting the data. +The columns are named in the same order as they appear in the list.

+
+
Type:
+

list

+
+
+
+ +
+
+classmethod from_dict(data: dict, session: Session) Variable#
+

Create a new Variable from a python dictionary.

+
+
Parameters:
+
    +
  • data (dict) – The dictionary containing the data

  • +
  • session (Session) – The database session

  • +
+
+
Returns:
+

obj – The new Variable

+
+
Return type:
+

Variable

+
+
+
+ +
+
+to_dict(deep: bool = False) dict#
+

To dict

+

Return the model as a python dictionary.

+
+
Parameters:
+

deep (bool) – If True, all related objects will be included as +dictionary. Defaults to False

+
+
Returns:
+

obj – The Model as dict

+
+
Return type:
+

dict

+
+
+
+ +
+ +
+ + +
+ + + + + + + +
+ + + + + + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/objects.inv b/objects.inv new file mode 100644 index 0000000000000000000000000000000000000000..d4a5836d8216c400b973ba733f21d67a752ce7a9 GIT binary patch literal 9439 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 + + + + + + + + Python Module Index — Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+ + + + + +
+ + +

Python Module Index

+ +
+ m +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
+ m
+ metacatalog +
    + metacatalog.api +
    + metacatalog.ext +
    + metacatalog.ext.base +
    + metacatalog.ext.export +
    + metacatalog.ext.io +
    + metacatalog.ext.standards_export +
    + metacatalog.models +
    + metacatalog.models.datasource +
    + metacatalog.models.entry +
    + metacatalog.models.entrygroup +
    + metacatalog.models.generic_data +
    + metacatalog.models.geometry_data +
    + metacatalog.models.keyword +
    + metacatalog.models.license +
    + metacatalog.models.person +
    + metacatalog.models.timeseries +
    + metacatalog.models.variable +
    + metacatalog.util.location +
    + metacatalog.util.results +
+ + +
+ + + + + +
+ +
+
+
+ +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + +
+ + +
+ + \ No newline at end of file diff --git a/search.html b/search.html new file mode 100644 index 00000000..3fc64bca --- /dev/null +++ b/search.html @@ -0,0 +1,504 @@ + + + + + + + + + + Search - Metacatalog 0.9.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
+
+
+
+ + + + +
+
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+ + +
+

Search

+ + + +
+
+ + + + + + +
+ +
+
+
+ +
+ + + +
+ + +
+
+ +
+ +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/searchindex.js b/searchindex.js new file mode 100644 index 00000000..5c2edb7a --- /dev/null +++ b/searchindex.js @@ -0,0 +1 @@ +Search.setIndex({"docnames": ["api/api", "api/api_add_entry", "api/api_add_group", "api/api_add_keyword", "api/api_add_license", "api/api_add_person", "api/api_add_thesaurus", "api/api_add_variable", "api/api_connect_database", "api/api_create_tables", "api/api_find_entry", "api/api_find_keyword", "api/api_find_license", "api/api_find_person", "api/api_find_thesaurus", "api/api_find_variable", "api/api_get_logger", "api/api_get_uuid", "api/api_populate_defaults", "api/api_show_attributes", "api/api_show_records", "cli/cli", "cli/cli_add", "cli/cli_create", "cli/cli_find", "cli/cli_init", "cli/cli_populate", "cli/cli_show", "cli/cli_uuid", "dev/dev", "dev/iso19115", "dev/tests", "ext/custom", "ext/export", "ext/ext", "ext/io", "ext/standards_export", "home/getting_started", "home/home", "home/install", "home/scheme", "index", "models/data", "models/datasource", "models/entry", "models/keyword", "models/license", "models/location", "models/models", "models/person", "models/result", "models/variable"], "filenames": ["api/api.rst", "api/api_add_entry.ipynb", "api/api_add_group.ipynb", "api/api_add_keyword.ipynb", "api/api_add_license.ipynb", "api/api_add_person.ipynb", "api/api_add_thesaurus.ipynb", "api/api_add_variable.ipynb", "api/api_connect_database.ipynb", "api/api_create_tables.ipynb", "api/api_find_entry.ipynb", "api/api_find_keyword.ipynb", "api/api_find_license.ipynb", "api/api_find_person.ipynb", "api/api_find_thesaurus.ipynb", "api/api_find_variable.ipynb", "api/api_get_logger.ipynb", "api/api_get_uuid.ipynb", "api/api_populate_defaults.ipynb", "api/api_show_attributes.ipynb", "api/api_show_records.ipynb", "cli/cli.rst", "cli/cli_add.ipynb", "cli/cli_create.ipynb", "cli/cli_find.ipynb", "cli/cli_init.ipynb", "cli/cli_populate.ipynb", "cli/cli_show.ipynb", "cli/cli_uuid.ipynb", "dev/dev.rst", "dev/iso19115.rst", "dev/tests.rst", "ext/custom.rst", "ext/export.rst", "ext/ext.rst", "ext/io.rst", "ext/standards_export.rst", "home/getting_started.rst", "home/home.rst", "home/install.rst", "home/scheme.rst", "index.rst", "models/data.rst", "models/datasource.rst", "models/entry.rst", "models/keyword.rst", "models/license.rst", "models/location.rst", "models/models.rst", "models/person.rst", "models/result.rst", "models/variable.rst"], "titles": ["API", "Add Entry", "Add EntryGroup", "Add keyword", "Add license", "Add Person", "Add Thesaurus", "Add variable and unit", "Connect database", "Create Tables", "Find Entry", "Find Keyword", "Find License", "Find person", "Find Thesaurus", "Find Variable", "Logging", "Get UUID", "Populate defaults", "Show Attributes", "Show Records", "CLI", "Add command", "Create Command", "Find Command", "Init command", "Populate command", "Show command", "Uuid command", "Developers", "ISO 19115", "E2E Tests", "Custom Extensions", "Export Extenstion", "Extensions", "Read / Write Extension", "Standards Export Extension", "Getting Started", "Home", "Installation", "Metadata Overview", "Welcome to Metacatalog\u2019s documentation!", "Data Tables", "DataSource", "Entry", "Controlled Keywords", "Data Licenses", "Location Filter", "Database Models", "Authors & Contributors", "ResultSet", "Variables"], "terms": {"The": [0, 1, 2, 3, 5, 6, 7, 8, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 30, 31, 32, 33, 34, 36, 38, 39, 40, 43, 44, 45, 46, 47, 48, 49, 50, 51], "submoul": 0, "contain": [0, 1, 3, 6, 19, 22, 30, 36, 40, 43, 44, 45, 47, 49, 50, 51], "all": [0, 1, 5, 8, 9, 10, 11, 13, 14, 17, 22, 23, 24, 30, 31, 32, 33, 35, 36, 38, 39, 40, 43, 44, 45, 46, 47, 49, 50, 51], "ar": [0, 1, 2, 3, 7, 8, 10, 11, 13, 14, 17, 18, 19, 20, 22, 23, 24, 26, 29, 30, 31, 32, 33, 36, 38, 40, 42, 43, 44, 45, 46, 47, 49, 50, 51], "avail": [0, 1, 4, 13, 16, 19, 22, 30, 33, 43, 44, 45], "python": [0, 11, 16, 18, 21, 22, 24, 33, 36, 38, 39, 43, 44, 45, 46, 48, 49, 51], "cli": [0, 22, 24, 32, 36, 38, 39, 41], "also": [0, 1, 2, 3, 5, 8, 15, 16, 17, 21, 22, 29, 30, 32, 33, 36, 38, 40, 42, 44, 45, 47, 49, 51], "reli": [0, 43], "therefor": [0, 10, 13, 18, 21, 30, 43], "us": [0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 26, 27, 28, 30, 31, 32, 33, 34, 35, 36, 38, 39, 40, 43, 44, 45, 46, 47, 48, 49, 50, 51], "i": [0, 1, 2, 3, 5, 7, 8, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 30, 31, 32, 33, 34, 35, 36, 38, 39, 40, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51], "recommend": [0, 1, 3, 7, 10, 18, 30, 33, 44, 46, 49], "wai": [0, 22, 30, 43, 46], "It": [0, 1, 3, 6, 7, 15, 18, 20, 22, 23, 24, 26, 30, 35, 36, 40, 43, 44, 45, 46, 47, 49, 50, 51], "like": [0, 6, 8, 13, 16, 18, 21, 22, 23, 31, 32, 33, 35, 39, 40, 43, 44, 46, 47, 50, 51], "from": [0, 1, 7, 8, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 23, 24, 26, 29, 30, 31, 32, 33, 35, 36, 38, 39, 40, 43, 44, 45, 46, 47, 49, 50, 51], "If": [0, 1, 3, 5, 6, 8, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 30, 31, 33, 36, 39, 40, 43, 44, 45, 46, 47, 49, 50, 51], "you": [0, 1, 3, 5, 6, 8, 12, 13, 14, 16, 17, 18, 19, 20, 21, 22, 23, 24, 26, 29, 30, 31, 32, 33, 35, 38, 39, 40, 42, 43, 44, 45, 51], "have": [0, 1, 2, 3, 5, 6, 13, 18, 22, 24, 26, 28, 30, 31, 38, 39, 40, 44, 45, 46, 47, 50], "other": [0, 10, 11, 13, 14, 16, 22, 24, 25, 30, 31, 33, 40, 44, 45, 47, 49, 50], "modul": [0, 11, 16, 18, 22, 23, 24, 25, 26, 27, 28, 47], "present": [0, 23, 44, 50], "call": [0, 6, 11, 16, 22, 26, 31, 32, 33, 35, 50], "mc_api": 0, "There": [0, 18, 22, 24, 30, 40, 43], "three": [0, 10, 22, 38, 40, 44, 50, 51], "connect": [0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 23, 25, 26, 27, 28, 36, 39, 41, 44], "databas": [0, 1, 2, 3, 4, 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, 36, 38, 39, 40, 41, 43, 44, 45, 46, 47, 49, 51], "creat": [0, 1, 7, 10, 11, 21, 22, 24, 25, 26, 30, 32, 33, 36, 40, 41, 43, 44, 45, 47, 48, 49, 51], "necessari": [0, 1, 16, 23, 38, 39, 40, 43, 44, 47], "tabl": [0, 18, 19, 20, 22, 24, 25, 26, 27, 30, 40, 41, 43, 44, 45, 48, 51], "setup": [0, 21], "These": [0, 1, 3, 8, 19, 20, 22, 43], "connect_databas": [0, 8, 11, 13, 14, 16, 17, 20, 50], "create_t": [0, 9], "populate_default": [0, 18], "follow": [0, 18, 22, 23, 24, 25, 26, 27, 28, 30, 31, 32, 33, 36, 37, 39, 40, 44, 45, 46], "similar": [0, 39, 40, 45], "pattern": [0, 45], "each": [0, 1, 3, 5, 15, 20, 24, 30, 31, 32, 38, 40, 43, 44, 46, 48], "them": [0, 3, 8, 10, 30, 40, 45], "expect": [0, 31, 36, 46], "session": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 28, 36, 44, 49, 50, 51], "first": [0, 1, 5, 8, 10, 13, 16, 22, 24, 25, 30, 39, 40, 43, 44, 49, 50], "argument": [0, 1, 8, 10, 21, 23, 25, 26, 27, 28, 32, 36, 43, 44, 47], "see": [0, 8, 10, 16, 22, 24, 30, 44], "name": [0, 1, 2, 3, 5, 6, 7, 8, 10, 11, 13, 14, 15, 16, 18, 19, 20, 22, 24, 26, 27, 30, 31, 33, 36, 39, 40, 43, 44, 45, 49, 50, 51], "prefix": [0, 33, 44], "an": [0, 1, 3, 4, 5, 6, 8, 10, 11, 12, 13, 14, 15, 16, 22, 24, 28, 30, 31, 32, 33, 34, 35, 36, 40, 43, 44, 45, 46, 47, 48, 49, 50], "action": [0, 8, 27, 30, 31], "identifi": [0, 1, 3, 5, 6, 10, 13, 22, 30, 36, 40, 43, 44, 45, 49], "find_": [0, 20], "find": [0, 17, 21, 22, 29, 30, 31, 39, 40, 41, 44, 47], "entiti": [0, 1, 3, 4, 5, 6, 7, 10, 17, 30, 44, 45, 48, 50], "exact": [0, 1, 10, 11, 12, 13, 14, 15, 21, 47], "match": [0, 1, 3, 5, 10, 11, 12, 13, 14, 15, 17, 19, 21, 22, 24, 47, 50], "filter": [0, 6, 10, 12, 14, 20, 24, 27, 41, 44, 45, 48], "add_": 0, "add": [0, 16, 21, 28, 32, 34, 35, 36, 38, 40, 41, 42, 43, 44, 45], "new": [0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 16, 17, 18, 21, 22, 30, 31, 32, 33, 35, 36, 43, 44, 45, 49, 50, 51], "entitit": 0, "show_": [0, 19, 20], "inspect": [0, 22], "raw": [0, 20, 22, 27, 33], "record": [0, 1, 4, 10, 11, 12, 13, 14, 15, 17, 21, 22, 24, 27, 28, 30, 41, 43, 44, 45, 46, 48, 49, 51], "metacatalog": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 32, 33, 34, 35, 36, 38, 40, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51], "api": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24, 26, 36, 38, 41, 44, 45, 47, 50], "add_entri": 1, "titl": [1, 2, 3, 4, 5, 6, 10, 12, 14, 20, 22, 30, 33, 40, 43, 44, 45, 46, 50], "str": [1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 32, 33, 36, 43, 44, 45, 46, 47, 49, 50, 51], "author": [1, 4, 5, 6, 10, 13, 22, 24, 30, 33, 40, 41, 44, 48], "union": [1, 2, 3, 5, 7, 36, 44, 50], "int": [1, 2, 3, 5, 7, 10, 13, 18, 20, 33, 36, 43, 44, 45, 46, 47, 49, 51], "locat": [1, 20, 22, 30, 33, 36, 40, 41, 43, 44, 48], "tupl": [1, 19, 20, 44], "float": [1, 43, 44, 47], "variabl": [1, 8, 10, 18, 22, 24, 26, 30, 31, 33, 40, 41, 44, 48, 50], "abstract": [1, 10, 20, 22, 30, 32, 33, 38, 40, 43, 44], "none": [1, 2, 3, 5, 6, 7, 8, 9, 10, 17, 18, 20, 24, 30, 32, 33, 36, 43, 44, 47, 50], "external_id": [1, 10, 20, 22, 33, 40, 44], "licens": [1, 10, 18, 22, 24, 26, 30, 33, 40, 41, 44, 48], "embargo": [1, 20, 22, 30, 33, 40], "bool": [1, 4, 10, 11, 12, 13, 14, 15, 17, 19, 20, 33, 36, 43, 44, 45, 46, 49, 50, 51], "fals": [1, 10, 11, 12, 13, 14, 15, 17, 19, 30, 33, 36, 43, 44, 45, 46, 47, 49, 50, 51], "is_parti": [1, 30, 44], "kwarg": [1, 4, 8, 11, 32, 33, 35, 42, 43, 44, 45, 46, 49, 50, 51], "metadata": [1, 10, 15, 20, 22, 28, 30, 33, 36, 38, 42, 44, 45, 49, 50], "thi": [1, 2, 3, 5, 6, 7, 10, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 35, 36, 38, 39, 40, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51], "method": [1, 8, 32, 36, 44, 45, 49, 50], "core": [1, 34, 44], "usual": [1, 3, 6, 8, 10, 38, 40, 43, 44, 45, 46, 47], "more": [1, 10, 22, 24, 25, 28, 30, 32, 33, 36, 38, 39, 40, 43, 44, 45, 46, 47, 50], "step": [1, 22, 23, 38, 40, 47], "which": [1, 6, 8, 10, 13, 21, 22, 23, 24, 26, 28, 30, 31, 32, 33, 36, 39, 40, 43, 44, 45, 47, 48, 49, 50], "need": [1, 10, 13, 16, 17, 19, 22, 23, 30, 31, 32, 33, 36, 39, 40, 43, 44, 47], "newli": [1, 7, 22, 44, 51], "id": [1, 2, 3, 5, 7, 10, 11, 12, 13, 14, 15, 17, 19, 20, 22, 24, 28, 30, 36, 40, 43, 44, 45, 46, 49, 50, 51], "Such": 1, "ad": [1, 2, 3, 4, 5, 6, 7, 10, 22, 24, 28, 30, 31, 32, 36, 38, 44, 45, 50], "contributor": [1, 5, 10, 13, 22, 24, 30, 40, 41, 44, 48], "mandatori": [1, 2, 30, 40, 46, 49], "data": [1, 7, 10, 18, 20, 21, 22, 24, 30, 31, 33, 34, 35, 36, 38, 40, 41, 43, 44, 47, 48, 49, 50, 51], "extremli": 1, "paramet": [1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 33, 36, 39, 43, 44, 45, 46, 47, 49, 50, 51], "sqlalchemi": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 18, 20, 36, 38, 44, 45, 47, 48], "version": [1, 2, 3, 5, 6, 7, 10, 11, 12, 13, 14, 15, 16, 17, 20, 22, 23, 24, 25, 26, 27, 28, 30, 33, 34, 36, 39, 40, 43, 44, 45, 46, 48, 49, 50, 51], "0": [1, 2, 3, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15, 16, 17, 23, 24, 30, 34, 36, 39, 40, 43, 44, 45, 46, 49, 50, 51], "2": [1, 2, 5, 10, 11, 13, 14, 16, 17, 20, 22, 23, 24, 26, 28, 30, 34, 36, 44, 47], "6": [1, 5, 8, 11, 13, 14, 20, 24, 30, 43, 44, 50], "person": [1, 10, 17, 19, 22, 24, 26, 30, 40, 41, 44, 49], "ha": [1, 3, 5, 15, 17, 18, 19, 21, 22, 23, 24, 30, 32, 33, 38, 39, 40, 42, 43, 44, 45, 46, 47, 50], "exist": [1, 2, 3, 5, 6, 11, 22, 30, 46, 50], "alreadi": [1, 3, 5, 17, 23, 45], "can": [1, 2, 3, 5, 6, 7, 8, 10, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22, 23, 24, 25, 26, 27, 28, 30, 31, 32, 33, 35, 36, 38, 39, 40, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51], "found": [1, 6, 8, 10, 14, 17, 22, 24, 28, 36, 45], "last_nam": [1, 5, 13, 19, 22, 40, 49], "organis": [1, 6, 13, 14, 45, 49], "organisation_nam": [1, 5, 13, 36, 49], "either": [1, 2, 3, 7, 8, 10, 13, 22, 24, 30, 44, 46], "wkt": [1, 10, 44, 47], "epsg": [1, 30, 44, 47], "4326": [1, 30, 44], "coordin": [1, 40, 44, 47], "x": [1, 44], "y": [1, 43, 44], "longitud": 1, "latitud": 1, "chang": [1, 10, 12, 13, 14, 15, 17, 22, 23, 30, 33, 36, 38, 39, 40, 43, 44, 45, 46, 48, 49, 50], "1": [1, 3, 5, 6, 8, 10, 11, 12, 13, 14, 15, 16, 17, 19, 20, 22, 23, 24, 25, 26, 27, 28, 30, 36, 40, 43, 44, 45, 46, 49, 50, 51], "A": [1, 3, 5, 10, 13, 22, 30, 32, 33, 40, 43, 44, 45, 47, 51], "point": [1, 4, 6, 10, 30, 40, 43, 44, 47, 51], "should": [1, 3, 21, 22, 24, 30, 31, 32, 38, 39, 40, 43, 44, 45, 46, 47, 49, 50], "specifi": [1, 10, 22, 23, 30, 33, 36, 40, 43, 44, 45, 46, 47, 49, 51], "here": [1, 6, 16, 22, 23, 29, 30, 32, 40, 43, 44, 45, 49], "physic": [1, 7, 15, 44], "measur": [1, 28, 44, 51], "differ": [1, 10, 15, 22, 24, 29, 30, 32, 35, 40, 44, 47, 48], "centroid": [1, 43, 44], "spatial": [1, 10, 30, 39, 40, 43, 44, 47], "extent": [1, 30, 40, 43, 44], "e": [1, 3, 5, 11, 13, 16, 22, 24, 30, 32, 33, 36, 40, 43, 44, 50, 51], "g": [1, 3, 22, 24, 32, 44], "discharg": [1, 44], "catchment": [1, 44], "otherwis": [1, 6, 18, 43, 44, 47], "datasourc": [1, 10, 22, 24, 26, 30, 33, 35, 40, 41, 44, 48, 51], "spatial_scal": [1, 30, 44], "full": [1, 3, 4, 5, 6, 7, 8, 10, 11, 12, 14, 22, 23, 24, 25, 26, 27, 28, 30, 36, 40, 43, 44, 45, 46, 49, 50, 51], "describ": [1, 10, 13, 15, 30, 35, 40, 43, 44, 45, 51], "descript": [1, 2, 6, 14, 30, 43, 44, 45], "Be": [1, 18], "possibl": [1, 10, 11, 12, 14, 16, 20, 30, 32, 33, 36, 40, 43, 44, 45, 46, 47], "anoth": [1, 15, 30, 33, 40, 44, 45, 49], "uniqu": [1, 10, 11, 12, 13, 14, 15, 40, 43, 44, 45, 46, 49, 50, 51], "suppli": [1, 18, 22, 24, 30, 47], "provid": [1, 10, 22, 24, 30, 36, 40, 42], "store": [1, 21, 22, 30, 36, 38, 40, 42, 43, 44, 45, 46, 48], "refer": [1, 6, 14, 22, 23, 25, 28, 30, 38, 39, 40, 41, 42, 44, 45, 46, 47, 51], "reason": [1, 30, 40, 44], "comment": [1, 20, 22, 33, 40, 44], "gener": [1, 30, 36, 38, 40, 47, 50], "purpos": [1, 23, 30, 45, 46], "ani": [1, 8, 16, 18, 21, 22, 23, 24, 25, 26, 27, 28, 30, 33, 40, 43, 44, 45, 47, 49, 50, 51], "vital": 1, "inform": [1, 10, 18, 19, 28, 30, 36, 39, 40, 43, 44, 46, 49], "understand": [1, 44, 46], "": [1, 2, 3, 5, 8, 10, 11, 12, 22, 23, 24, 29, 30, 43, 47, 49], "go": [1, 5, 30], "link": [1, 4, 6, 7, 23, 30, 43, 44, 46], "true": [1, 4, 10, 11, 12, 13, 14, 15, 17, 19, 20, 22, 24, 30, 33, 36, 43, 44, 45, 46, 49, 50, 51], "publicli": [1, 45], "until": [1, 24], "end": [1, 19, 20, 22, 30, 31, 36, 40, 43, 44, 47], "period": [1, 40], "year": [1, 30, 40], "modifi": [1, 30], "7": [1, 10, 11, 13, 16, 17, 20, 23, 24, 30, 36, 44, 50], "flag": [1, 21, 22, 23, 24, 25, 26, 27, 28, 33, 36], "mark": [1, 44], "partial": [1, 10, 30, 44], "embed": 1, "composit": [1, 2, 30, 40, 44, 46, 50], "entrygroup": [1, 10, 17, 24, 26, 28, 30, 41, 44, 47, 50], "type": [1, 2, 3, 4, 5, 6, 7, 11, 12, 13, 14, 15, 17, 19, 20, 30, 32, 33, 36, 43, 44, 45, 46, 47, 49, 50, 51], "mean": [1, 33, 40, 44, 45], "self": [1, 11, 32, 35, 44, 50], "complet": [1, 10, 27, 50], "return": [1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15, 17, 19, 20, 22, 23, 24, 25, 26, 27, 28, 30, 33, 35, 36, 43, 44, 45, 46, 47, 49, 50, 51], "instanc": [1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 18, 21, 22, 23, 24, 25, 26, 27, 28, 30, 31, 33, 36, 44, 45, 47, 48, 50], "add_details_to_entri": 1, "list": [1, 2, 3, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 22, 25, 26, 33, 40, 43, 44, 45, 47, 49, 50, 51], "dict": [1, 3, 10, 20, 22, 26, 33, 36, 43, 44, 45, 46, 49, 50, 51], "associ": [1, 2, 3, 5, 10, 14, 30, 44, 45, 49, 50], "kei": [1, 4, 10, 18, 22, 24, 30, 33, 36, 40, 43, 44, 45, 50], "valu": [1, 3, 10, 11, 17, 18, 22, 24, 26, 28, 30, 36, 40, 43, 44, 45, 47, 50], "pair": [1, 10, 22, 24, 40, 44], "one": [1, 2, 3, 10, 11, 12, 13, 14, 15, 19, 21, 22, 24, 30, 31, 33, 36, 40, 43, 44, 45, 47, 50, 51], "mani": [1, 8, 11, 12, 13, 14, 16, 18, 22, 23, 24, 26, 30, 39, 40, 44], "singl": [1, 3, 5, 30, 43], "load": [1, 3, 5, 6, 8, 10, 13, 16, 17, 20, 23, 26, 33, 34, 35, 36, 40, 43, 44, 45, 46, 50, 51], "assum": [1, 3, 5, 22, 23, 24, 26, 28, 44], "pass": [1, 3, 5, 8, 10, 21, 23, 25, 26, 27, 28, 32, 33, 36, 43, 44], "object": [1, 3, 5, 6, 8, 10, 11, 12, 13, 14, 15, 16, 17, 18, 22, 24, 28, 30, 33, 36, 43, 44, 45, 46, 47, 49, 51], "8": [1, 7, 10, 11, 12, 13, 15, 16, 20, 22, 24, 30, 31, 33, 36, 40, 43, 44, 50], "structur": [1, 21, 40, 44], "where": [1, 10, 20, 22, 27, 30, 31, 33, 36, 44], "option": [1, 2, 5, 6, 10, 11, 13, 14, 21, 22, 23, 24, 25, 26, 27, 28, 30, 31, 33, 40, 43, 44, 46, 47, 49, 51], "omit": [1, 5, 7, 18, 26, 30, 44], "mix": 1, "py": [1, 11, 21], "class": [1, 11, 16, 20, 22, 24, 26, 30, 32, 33, 35, 36, 38, 42, 43, 44, 45, 46, 48, 49, 50, 51], "model": [1, 2, 3, 6, 7, 14, 16, 17, 18, 19, 20, 22, 24, 26, 28, 30, 32, 33, 35, 38, 40, 41, 42, 43, 44, 45, 46, 49, 51], "add_keywords_to_entri": [1, 3, 45], "alia": [1, 3, 44], "between": [1, 3, 5, 10, 30, 31, 38, 40, 44, 45, 48], "prepopul": [1, 3], "might": [1, 3, 10, 15, 18, 22, 24, 27, 30, 31, 39, 47], "want": [1, 3, 6, 8, 23, 49], "shape": [1, 3, 5, 39, 47], "renam": [1, 3, 43], "In": [1, 3, 8, 10, 22, 23, 24, 26, 30, 36, 38, 39, 40, 43, 44, 47, 49], "case": [1, 2, 3, 10, 22, 24, 30, 36, 39, 43, 47], "reciv": [1, 3], "instead": [1, 3, 10, 11, 12, 13, 14, 15, 22, 23, 24, 25, 26, 27, 28, 43, 46, 50], "deprec": [1, 3, 10, 30], "sinc": [1, 3, 10, 13, 34, 44], "4": [1, 3, 5, 6, 7, 10, 11, 13, 14, 16, 17, 19, 20, 23, 24, 28, 30, 33, 40, 44, 45, 47, 49, 50], "5": [1, 3, 8, 13, 16, 17, 20, 24, 28, 30, 40, 43, 50], "remov": [1, 3, 31, 50], "futur": [1, 3, 10, 15, 18, 22, 24, 30, 31, 44, 48], "releas": [1, 3, 10, 22, 23, 24, 30, 44], "void": [1, 3, 5], "entri": [2, 11, 12, 13, 15, 17, 20, 22, 24, 28, 30, 32, 33, 35, 36, 38, 41, 43, 45, 46, 47, 48, 49, 50], "done": [2, 16, 22, 31, 33, 44], "add_group": 2, "endpoint": [2, 3, 17, 19, 20, 22, 24], "function": [2, 6, 8, 10, 17, 19, 20, 22, 29, 31, 32, 33, 34, 35, 36, 38, 39, 41, 43, 44, 45, 47, 50], "take": [2, 49, 51], "specif": [2, 6, 13, 17, 29, 30, 43, 44], "addition": [2, 36], "add_project": 2, "special": [2, 42], "best": [2, 38, 45], "interfac": [2, 3, 32, 35, 38, 41, 44, 49, 50], "make_composit": [2, 44], "group_typ": 2, "entry_id": [2, 45], "correctli": [2, 24], "versionad": [2, 10, 14, 46], "entrygrouptyp": [2, 10, 26, 30, 41, 44], "tp": 2, "add_keyword": 3, "path": [3, 7, 11, 17, 22, 28, 30, 31, 33, 36, 43, 44, 45], "thesauru": [3, 7, 11, 18, 24, 26, 30, 41, 45], "element": [3, 27, 40, 44], "sequenc": [3, 18], "topic": [3, 30, 45], "term": [3, 30, 36, 38, 45, 46], "variable_level_1": [3, 45], "etc": [3, 30, 44], "10": [3, 5, 6, 10, 11, 13, 14, 16, 18, 20, 24, 30, 36, 44, 45, 49], "distribut": [3, 4, 30, 45, 46], "control": [3, 7, 10, 28, 30, 41, 43, 44, 48], "thesaurusnam": [3, 30, 45], "highli": [3, 10, 44, 46, 49], "design": [3, 24, 30, 31, 40, 44], "custom": [3, 29, 33, 43, 44, 45], "split": [3, 40, 44, 48, 50], "automat": [3, 30, 45, 49], "conveni": [3, 40, 45], "part": [3, 30, 34, 44, 45, 50], "receiv": 3, "uuid": [3, 5, 6, 10, 11, 13, 14, 30, 33, 36, 40, 41, 44, 45, 49, 50], "thu": [3, 10, 14, 19, 33, 40, 44, 49], "deconstruct": 3, "add_licens": 4, "short_titl": [4, 10, 12, 24, 46], "short": [4, 6, 13, 14, 40, 43, 45, 46, 50], "max": [4, 7, 46, 51], "40": [4, 17, 44, 46], "letter": [4, 7, 44, 45, 46, 51], "summari": [4, 46], "give": [4, 5, 28, 30, 38, 40, 43, 44, 46, 49, 50], "given": [4, 8, 9, 10, 11, 13, 14, 17, 19, 22, 23, 24, 25, 26, 27, 28, 30, 33, 36, 43, 44, 45, 46, 47, 49, 50], "full_text": [4, 30, 46], "legal": [4, 30, 46], "code": [4, 31, 46], "onlin": 4, "resourc": [4, 28, 30, 44, 45], "by_attribut": [4, 12, 24, 46], "doe": [4, 10, 23, 24, 30, 31, 33, 36, 40, 44, 45, 46, 51], "requir": [4, 24, 29, 30, 31, 33, 50], "attribut": [4, 5, 10, 12, 13, 14, 15, 22, 24, 27, 33, 35, 40, 41, 44, 45, 49], "default": [4, 7, 10, 16, 17, 21, 22, 23, 24, 25, 26, 29, 30, 32, 33, 35, 36, 40, 41, 43, 44, 45, 46, 47, 49, 50, 51], "share_alik": [4, 12, 46], "redistribut": 4, "under": [4, 21, 22, 30, 36, 40, 46], "same": [4, 7, 15, 16, 21, 22, 23, 24, 26, 30, 33, 40, 43, 44, 46, 49, 50, 51], "commercial_us": [4, 12, 46], "permit": 4, "commerci": 4, "add_person": 5, "first_nam": [5, 13, 19, 22, 40, 49], "organisation_abbrev": [5, 13], "affili": [5, 19, 22, 40, 49], "real": [5, 40], "institut": [5, 22], "Then": [5, 8, 10, 22, 30], "goe": [5, 30], "column": [5, 7, 20, 22, 24, 30, 40, 43, 51], "activ": [5, 22, 23, 24, 25, 26, 27, 28, 32, 33, 36, 44], "set": [5, 6, 8, 10, 15, 16, 18, 20, 22, 24, 26, 28, 30, 31, 33, 39, 40, 43, 44, 46, 50], "both": [5, 30], "string": [5, 8, 10, 12, 13, 15, 19, 22, 23, 24, 25, 26, 27, 28, 30, 33, 43, 44, 45, 49, 50], "global": [5, 6, 14, 44, 45, 47], "do": [5, 6, 8, 16, 18, 30, 31, 32, 38, 40, 43, 45], "last": [5, 10, 11, 13, 16, 30, 40, 43, 44, 49], "null": [5, 24, 46], "highly_recommend": 5, "applic": [5, 13, 22, 24, 30, 31, 39, 40, 44, 49, 51], "head": [5, 13, 30, 49], "whithout": 5, "depart": [5, 13, 49], "abbrevi": [5, 13, 46], "famou": 5, "karlsruh": 5, "technologi": 5, "better": 5, "known": [5, 46], "kit": 5, "1024": [5, 19], "byte": [5, 47], "includ": [5, 10, 18, 22, 28, 30, 31, 33, 36, 40, 43, 44, 45, 46, 49, 51], "group": [5, 13, 30, 33, 36, 40, 44, 45, 46, 49, 50], "recommond": [5, 13, 49], "dataset": [5, 13, 15, 30, 36, 40, 43, 44, 46, 49, 50], "add_organis": 5, "add_persons_to_entri": 5, "role": [5, 44, 49], "personrol": [5, 13, 22, 24, 26, 30, 41, 44, 49], "order": [5, 7, 20, 40, 43, 44, 45, 50, 51], "further": [5, 13, 23, 30, 43, 44, 46, 49], "defin": [5, 8, 15, 20, 26, 30, 32, 42, 43, 47, 49], "respect": 5, "ascend": 5, "respec": 5, "after": [5, 16, 30, 31, 39, 40, 44, 47], "add_thesauru": 6, "url": [6, 14, 30, 43, 45], "keyword": [6, 7, 10, 14, 17, 18, 22, 24, 26, 28, 30, 40, 41, 43, 44, 48], "thesaurii": [6, 14, 45], "well": [6, 31, 36, 39, 43, 44, 47], "assign": [6, 40, 43, 44, 45, 46, 49, 51], "ones": 6, "qualifi": [6, 45], "just": [6, 16, 21, 22, 24, 25, 30, 32, 40], "distributor": [6, 30], "maintain": [6, 45], "futher": [6, 10], "detail": [6, 10, 13, 22, 30, 33, 39, 40, 44], "about": [6, 30, 36, 38, 40, 43, 44], "scope": [6, 30, 44, 45, 51], "implement": [6, 11, 30, 31, 32, 33, 38, 40, 42, 44, 45, 47, 51], "perman": [6, 45, 46], "overview": [6, 41], "page": [6, 45, 46], "xml": [6, 14, 33, 36, 40, 44, 45], "represent": [6, 7, 24, 33, 36, 43, 44], "mai": [6, 13, 14, 30, 33, 43, 44, 45, 48, 49], "placehold": [6, 45], "onli": [6, 8, 10, 11, 12, 13, 14, 15, 16, 18, 22, 27, 30, 36, 38, 39, 40, 43, 44, 45, 46, 48, 49], "add_vari": 7, "symbol": [7, 15, 22, 51], "column_nam": [7, 51], "64": [7, 43], "try": [7, 11, 30, 39], "correct": [7, 24, 30, 31, 46], "avoid": [7, 15], "dublic": 7, "3": [7, 10, 11, 14, 16, 20, 23, 24, 28, 30, 36, 43, 44, 46, 50, 51], "displai": [7, 43, 51], "when": [7, 17, 24, 30, 36, 39, 43, 51], "export": [7, 8, 22, 24, 27, 30, 34, 40, 43, 44, 49, 51], "thei": [7, 10, 13, 30, 31, 40, 43, 45, 46, 49, 51], "appear": [7, 30, 43, 51], "strongli": 7, "improv": 7, "findabl": 7, "add_unit": 7, "si": [7, 22, 40, 51], "12": [7, 20, 23, 24, 27, 30, 44, 51], "arg": [8, 11, 30, 32, 36, 43, 44, 47], "accept": [8, 22, 24, 47], "sqlachemi": 8, "create_engin": 8, "save_connect": 8, "empti": [8, 40, 43, 50], "addit": [8, 10, 40, 45], "check": [8, 11, 28, 29, 38, 44, 50], "down": [8, 43, 44], "len": [8, 14, 47], "import": [8, 10, 11, 13, 14, 16, 17, 19, 20, 22, 24, 26, 30, 31, 32, 33, 35, 36, 38, 40, 41, 43, 44, 46, 47, 50], "print": [8, 11, 13, 14, 16, 17, 22, 23, 24, 25, 26, 27, 28, 32, 44], "bind": [8, 46], "lt": [8, 11, 13, 16, 17, 22, 24, 26], "orm": [8, 47], "0x7f97b40b0250": 8, "gt": [8, 11, 13, 16, 17, 22, 24, 26, 28], "engin": [8, 11, 22, 28], "postgresql": [8, 22, 28, 31, 38, 39, 48], "postgr": [8, 22, 28, 31, 39], "localhost": [8, 22, 28, 39], "5432": [8, 22, 28, 31, 39], "product": [8, 20, 40], "scenario": [8, 20, 23, 26], "don": [8, 19], "t": [8, 11, 13, 19, 22, 27, 49], "cleartext": 8, "kind": [8, 10, 17, 22, 23, 24, 25, 26, 27, 28, 35, 44, 49, 51], "file": [8, 11, 16, 22, 23, 24, 25, 26, 27, 28, 31, 33, 36, 40, 42, 43, 44], "even": [8, 47], "trust": 8, "environ": [8, 21, 31], "window": [8, 21, 39], "postgres_password": [8, 31], "notmyrealpassword": 8, "linux": [8, 39], "ci": 8, "travi": 8, "cirleci": 8, "github": [8, 31], "repositori": [8, 45], "rememb": [8, 22], "yaml": 8, "config": [8, 11, 23, 44], "section": [8, 22, 23, 29, 30, 38], "envirn": 8, "often": [8, 24], "public": [8, 20, 22, 30, 33, 40, 44], "env": [8, 11], "istead": 8, "json": [8, 17, 22, 24, 28, 32, 33, 36, 40, 43, 44], "uri": [8, 23, 30, 44, 46], "directli": [8, 13, 22, 44, 45, 47], "o": [8, 11, 23, 30, 34, 43], "dbuser": 8, "pw": 8, "metacatalg": 8, "format": [8, 14, 22, 30, 33, 36, 40, 44], "0x7f9785755090": 8, "ipython": [8, 11], "consol": [8, 39, 44], "cach": 8, "command": [8, 11, 36, 38, 39, 41], "sqlite": 8, "your": [8, 18, 30, 31, 39, 42, 44], "main": [10, 32, 38, 40, 44], "most": [10, 11, 13, 21, 22, 44, 46], "atom": [10, 15, 51], "than": [10, 22, 30, 31, 33, 40, 44, 50], "togeth": [10, 30, 33, 39, 40], "n": [10, 16, 17, 22, 30, 43], "m": [10, 21, 22, 30, 36, 39, 43, 51], "relat": [10, 13, 22, 28, 30, 40, 43, 44, 45, 46, 49, 51], "depend": [10, 18, 23, 30, 31, 36, 43, 44, 47, 50], "find_entri": [10, 13, 44, 47, 50], "return_iter": [10, 11, 12, 13, 14, 15], "typing_extens": [10, 11, 12, 13, 14, 15, 17], "liter": [10, 11, 12, 13, 14, 15, 17, 40], "as_result": [10, 17, 50], "queri": [10, 11, 12, 13, 14, 15, 23, 31, 44, 45, 47], "immutableresultset": [10, 17, 33, 36, 50], "meta": [10, 22, 38, 44, 48], "mutual": [10, 30, 40], "exclus": [10, 30, 40], "now": [10, 12, 13, 15, 17, 18, 28, 30, 39, 44, 45, 49, 50], "allow": [10, 11, 12, 13, 15, 18, 30, 40], "wildcard": [10, 12, 13, 14, 15], "invert": [10, 12, 13, 14, 15], "prepend": [10, 12, 13, 14, 15], "14": [10, 20, 22, 24, 30], "9": [10, 20, 23, 24, 30, 44, 46], "By": [10, 28, 30, 31, 44, 47], "include_parti": 10, "make": [10, 15, 22, 29, 30, 31, 39, 40, 43, 44, 45, 46, 47, 50, 51], "sens": [10, 15, 31, 40, 44, 51], "combin": [10, 21, 43, 51], "lazi": [10, 33, 36, 40, 51], "arug": [10, 50], "coauthor": [10, 30, 40, 44, 49], "introduc": [10, 44], "replac": [10, 27, 39, 44], "its": [10, 36, 44], "behavior": [10, 29], "integ": [10, 11, 12, 13, 14, 15, 18, 19, 20, 30], "request": [10, 11, 12, 13, 14, 15, 19, 20, 24, 28, 30, 33, 44, 50], "Will": [10, 11, 12, 13, 14, 15, 27, 30, 37, 43], "13": [10, 11, 17, 20, 22, 24, 30, 44], "ignor": [10, 11, 13, 14, 16, 17, 18, 25, 26, 49], "attibut": 10, "text": [10, 22, 23, 24, 25, 26, 27, 28, 30, 40, 44, 46], "oper": [10, 30, 34, 35, 43, 44, 45], "sure": [10, 29, 30, 39, 45, 46, 47], "phrase": 10, "extern": [10, 40], "attrinbut": 10, "latest": [10, 44], "number": [10, 27, 40, 44, 45, 46, 47], "integr": [10, 18, 38, 44], "project": [10, 30, 31, 40, 44, 47], "hi": 10, "For": [10, 22, 32, 40, 42, 43, 44, 47], "co": [10, 30, 40, 44], "anyon": [10, 40], "repalc": 10, "tag": [10, 28, 33, 40, 44, 45], "against": [10, 31], "OR": 10, "search": [10, 13, 24, 28, 31, 38, 44, 47], "through": [10, 16, 29], "dictioniar": 10, "AND": [10, 24, 47], "respons": [10, 30, 45], "useful": 10, "miss": [10, 22, 23], "sibl": [10, 33, 36, 40, 50], "merg": [10, 17, 40, 50], "by_geometri": 10, "interpret": 10, "center": [10, 40], "buffer": [10, 47], "distanc": [10, 30, 44, 47], "meter": [10, 40, 43, 44, 47], "four": 10, "bound": [10, 13, 30, 40, 43], "box": [10, 30, 40, 43], "2d": 10, "two": [10, 22, 24, 25, 30, 35, 40, 44, 45, 50], "construct": 10, "polygon": [10, 40, 43, 47], "final": [10, 13, 16, 22, 33], "geometri": [10, 40, 43, 44, 47], "appli": [10, 30, 40, 43, 44, 47], "result": [10, 24, 27, 30, 31, 33, 43, 44, 45, 47, 49, 50], "reuslt": 10, "iter": [10, 11, 12, 13, 14, 15, 50], "themselv": [10, 11, 12, 13, 14, 15, 24, 45], "find_group": 10, "find_group_typ": 10, "collect": [10, 19, 40], "find_keyword": 11, "multipl": [11, 12, 14, 24, 44], "full_path": [11, 17, 30, 45], "thesaurus_nam": 11, "origin": [11, 21, 22, 30, 38, 40, 50], "At": [11, 22, 30, 50], "current": [11, 21, 24, 28, 30, 31, 33, 36, 43, 44, 45, 48, 50, 51], "stage": 11, "gcmd": [11, 14, 28, 40, 45], "scienc": [11, 14, 17, 28, 40, 45], "commanderror": 11, "traceback": [11, 22, 23, 24, 25, 26, 27, 28], "recent": [11, 44], "input": [11, 35, 43, 47], "f0273587aac": 11, "dropbox": 11, "db": [11, 16, 26, 31, 43], "55": 11, "34": [11, 17, 28], "56": 11, "get": [11, 13, 16, 21, 39, 41, 44, 47, 50], "57": [11, 20], "get_sess": 11, "58": 11, "59": 11, "98": 11, "99": 11, "els": [11, 20, 30, 33, 44], "build": [11, 19, 20, 24, 32, 33, 45], "100": [11, 23], "get_engin": 11, "101": 11, "102": 11, "81": 11, "alemb": 11, "82": 11, "83": 11, "check_database_vers": 11, "84": 11, "except": [11, 40, 46], "runtimeerror": 11, "85": 11, "missmatch": 11, "migrat": [11, 16, 23], "31": 11, "32": 11, "join": [11, 13], "basepath": 11, "39": [11, 14, 19, 20, 22, 24, 26, 27], "ini": 11, "33": 11, "script_": 11, "script": [11, 21], "scriptdirectori": 11, "from_config": 11, "35": 11, "miniconda3": 11, "py37": 11, "lib": 11, "python3": 11, "site": [11, 40], "packag": [11, 31, 33, 34, 39], "py3": 11, "egg": 11, "base": [11, 30, 32, 33, 40, 42, 47, 50], "cl": [11, 32, 44], "147": 11, "version_loc": 11, "148": 11, "timezon": 11, "get_main_opt": 11, "149": 11, "hook_config": 11, "get_sect": 11, "post_write_hook": 11, "150": 11, "151": 11, "__init__": [11, 32], "dir": 11, "file_templ": 11, "truncate_slug_length": 11, "sourceless": 11, "output_encod": 11, "68": 11, "doesn": 11, "r": 11, "pleas": [11, 30, 31, 38, 40], "69": 11, "init": [11, 21, 22, 23, 24, 26, 28, 32, 39, 41], "70": 11, "folder": [11, 31, 36], "abspath": 11, "71": 11, "72": 11, "home": [11, 36, 41], "mirko": 11, "doc": [11, 16, 41], "sourc": [11, 30, 42, 44], "kw": 11, "temperatur": [11, 51], "find_licens": 12, "find_person": 13, "is_organis": 13, "without": [13, 33, 40, 47, 49], "Not": [13, 30], "attribt": [13, 49], "user": [13, 19, 20, 22, 23, 24, 25, 26, 27, 28, 30, 31, 39, 43, 45, 46, 49], "find_organis": 13, "find_rol": 13, "contribut": [13, 29, 30], "delet": [13, 31, 35, 44], "persist": [13, 16, 44], "alfr": [13, 22, 24], "assoc": 13, "neumann": [13, 22], "18": [13, 24, 30], "19": [13, 22, 24, 30], "20": [13, 22, 23, 24, 30, 36], "start": [13, 30, 31, 40], "side": [13, 44], "find_thesauru": 14, "retun": 14, "No": [14, 30], "decript": 14, "field": [14, 44, 49], "some": [14, 18, 22, 24, 26, 29, 30, 31, 38, 42, 47], "pprint": [14, 22, 24, 26], "to_dict": [14, 17, 32, 33, 43, 44, 45, 46, 49, 50, 51], "nasa": [14, 40, 45], "clime": 14, "master": [14, 45], "dictionari": [14, 20, 22, 24, 30, 33, 36, 43, 44, 45, 46, 49, 50, 51], "earth": [14, 17, 28, 40], "http": [14, 16, 30, 40], "gcmdservic": 14, "gsfc": 14, "gov": [14, 30, 40], "km": [14, 44, 47, 51], "concept": 14, "concept_schem": 14, "sciencekeyword": 14, "2e54668d": 14, "8fae": 14, "429f": 14, "a511": 14, "efe529420b12": 14, "d": [14, 31], "3206": 14, "terminologi": 15, "rework": [15, 44], "taken": 15, "strict": [15, 36, 40, 51], "compound": 15, "entitii": [15, 22, 24], "upload": [15, 31, 40, 44, 45], "convert": [15, 20, 30, 33, 35, 43, 47], "misinterpret": 15, "find_vari": 15, "vriabl": 15, "find_unit": 15, "With": [16, 23, 33, 36], "buildin": 16, "org": 16, "howto": 16, "html": [16, 44], "__": [16, 22, 24, 28], "expos": [16, 43, 45], "handler": 16, "henc": 16, "standard": [16, 30, 33, 39, 40], "procedur": 16, "level": [16, 22, 35, 36, 43, 45], "logger": 16, "On": [16, 31, 39, 40, 49], "creation": [16, 20, 22, 24, 30, 44, 49], "consecut": 16, "getlogg": 16, "would": [16, 30, 31, 43, 49, 51], "simpli": [16, 31, 35], "get_logg": 16, "info": [16, 25, 44], "messag": [16, 21, 22, 23, 24, 25, 26, 27, 28], "warn": [16, 30], "error": [16, 22, 23, 24, 25, 26, 27, 28, 39, 47], "seriou": [16, 31], "rais": [16, 36, 46], "document": [16, 22, 25, 30, 35, 43], "within": [16, 30, 32, 40, 47], "same_logg": 16, "few": [16, 19, 30], "load_last": 16, "2021": 16, "06": 16, "07t10": 16, "08": 16, "658991": 16, "02": 16, "22": [16, 22], "334275": 16, "07t07": 16, "42": [16, 50], "686526": 16, "As": [16, 18, 22, 30, 31, 35, 40, 44, 45, 50], "were": [16, 22, 24, 40], "wa": [16, 17, 22, 24, 26, 30, 31, 43, 44], "setlevel": 16, "debug": 16, "time": [16, 23, 24, 26, 29, 30, 40, 43, 50, 51], "09": 16, "01": 16, "311257": 16, "get_uuid": 17, "util": [17, 47, 50], "across": [17, 28, 44, 45, 49], "we": [17, 22, 23, 28, 29, 30], "easili": [17, 18, 28, 44, 50], "popul": [17, 21, 22, 23, 24, 25, 39, 41, 48], "885735f3": [17, 28], "121e": [17, 28], "4ca0": [17, 28], "ac8b": [17, 28], "f37dbc972f03": [17, 28], "figur": 17, "out": [17, 24, 29, 33], "what": [17, 18, 30, 31, 40], "terrestri": [17, 28], "hydrospher": [17, 28], "25": [17, 28, 44], "while": [17, 30, 38, 40, 42, 43], "capabl": [17, 45], "child": [17, 30, 33, 43, 46, 50], "recurs": [17, 45, 50], "rebuild": 17, "veri": [17, 19, 24, 30, 33], "help": [17, 29, 30, 32, 33, 41], "kw_dict": 17, "deep": [17, 43, 44, 45, 46, 49, 51], "parent": [17, 30, 32, 33, 36, 43, 45], "dump": [17, 22, 32, 44], "indent": [17, 33, 44], "children": [17, 28], "nchildren": 17, "099ab1a": [17, 28], "f4d2": [17, 28], "48cc": [17, 28], "be2f": [17, 28], "86bd58ffc4ca": [17, 28], "734f8f27": [17, 28], "6976": [17, 28], "4b67": [17, 28], "8794": [17, 28], "c7fc79d6161e": [17, 28], "50b8fe04": [17, 28], "9149": [17, 28], "4b7f": [17, 28], "a8b2": [17, 28], "b33b1e3aa192": [17, 28], "5debb283": [17, 28], "51e4": [17, 28], "435e": [17, 28], "b2a2": [17, 28], "e8e2a977220d": [17, 28], "8c02f5d1": [17, 28], "ce86": [17, 28], "4bf5": [17, 28], "84d5": [17, 28], "b3496cdba6ad": [17, 28], "thesaurus_id": [17, 28, 30, 45], "glacier": 17, "ic": 17, "sheet": 17, "ground": [17, 30, 40, 43], "water": [17, 28], "snow": 17, "surfac": [17, 28], "qualiti": [17, 30, 40], "chemistri": 17, "support": [17, 30, 33, 36, 40, 43, 44, 48], "ignore_t": 18, "bump_sequ": 18, "10000": 18, "lookup": [18, 22, 26, 30], "auxiliari": [18, 21], "actual": [18, 19, 20, 22, 31, 33, 35, 40, 42, 43, 44], "read": [18, 30, 44, 49], "subdirectori": 18, "insid": [18, 32], "adapt": 18, "know": 18, "unit": [18, 22, 24, 26, 40, 41, 44, 47, 51], "datatyp": [18, 26, 30, 41, 43, 44], "pre": 18, "polul": 18, "datasource_typ": [18, 22, 24, 26], "entrygroup_typ": [18, 26], "person_rol": [18, 22, 24, 26], "awar": 18, "primari": 18, "high": 18, "000": 18, "conflict": 18, "meant": [19, 20, 30, 38], "admin": [19, 20, 30, 43, 45], "deploy": [19, 20], "realli": [19, 20, 24], "show_attribut": 19, "table_nam": [19, 20], "add_typ": 19, "column_t": 19, "colmun_data_typ": 19, "length": [19, 50], "128": 19, "show_record": 20, "limit": [20, 27, 30, 39, 46], "as_dict": 20, "content": [20, 22, 33, 36, 50], "posit": [20, 22, 24, 27, 28, 36, 44], "sql": [20, 27, 39, 43, 44], "claus": [20, 27], "output": [20, 22, 23, 24, 25, 26, 27, 28, 33, 35, 44, 46, 50], "repres": [20, 24, 30, 36, 40, 43, 44, 46, 49, 51], "backup": 20, "dure": [20, 39, 47], "develop": [20, 22, 23, 24, 25, 26, 27, 28, 41], "To": [20, 30, 32, 35, 43, 44, 49, 51], "sap": [20, 24], "flow": [20, 24], "hohe": [20, 24], "holz": 20, "tree": [20, 43], "022": 20, "20mm": 20, "east": [20, 43], "212": 20, "01010000006806f1811d0b4a4002452c62d8712640": 20, "geom": [20, 22, 30, 40], "latest_version_id": [20, 22, 44], "yet": [20, 22, 30, 44], "license_id": [20, 22, 44], "variable_id": [20, 22], "datasource_id": [20, 22], "embargo_end": [20, 22, 33], "datetim": [20, 43, 44], "2022": [20, 22], "15": [20, 22, 23, 24, 30, 43], "43": 20, "712110": 20, "2020": [20, 22], "712195": 20, "lastupd": [20, 22, 30, 33, 44], "17": [20, 24, 30], "47": 20, "540713": 20, "north": [20, 43], "west": [20, 43], "214": [20, 28], "749350": 20, "749407": 20, "565270": 20, "029": 20, "246": 20, "0101000000761c3f541a0b4a4099f04bfdbc712640": 20, "770584": 20, "770633": 20, "576064": 20, "048": 20, "2015": 20, "269": 20, "01010000008ba8893e1f0b4a400b24287e8c712640": 20, "nan": 20, "789248": 20, "789294": 20, "587474": 20, "050": 20, "275": 20, "0101000000c11da8531e0b4a404a26a77686712640": 20, "807256": 20, "807302": 20, "598154": 20, "056": 20, "282": 20, "01010000003fa7203f1b0b4a4036e50aef72712640": 20, "825347": 20, "825393": 20, "608043": 20, "057": 20, "292": 20, "01010000009335ea211a0b4a40d6743dd175712640": 20, "844292": 20, "844339": 20, "618236": 20, "058": 20, "306": 20, "0101000000a46c91b41b0b4a40807edfbf79712640": 20, "863693": 20, "863740": 20, "627976": 20, "106": 20, "commerti": 20, "326": 20, "0101000000a87004a9140b4a40b79c4b7155712640": 20, "sensor": [20, 51], "depth": 20, "884135": 20, "884185": 20, "637880": 20, "108": 20, "333": 20, "0101000000d828eb37130b4a403c855ca967712640": 20, "904529": 20, "904578": 20, "647861": 20, "instal": [21, 23, 30, 31, 44, 45, 49, 51], "regist": [21, 35, 36], "anaconda": 21, "run": [21, 23, 25, 26, 30, 35, 39, 49], "work": [21, 29, 30, 33, 36, 40, 41, 49], "fine": [21, 24, 39], "howev": [21, 22, 23, 24, 30, 47, 51], "experi": 21, "problem": 21, "seem": 21, "happen": 21, "quit": [21, 42], "frequent": 21, "entrypoint": [21, 38], "hood": [21, 22], "exactli": [21, 24, 30, 44], "execut": [21, 22, 24, 26, 32, 35, 44], "h": [21, 22, 23, 24, 25, 26, 27, 28, 43], "sub": 21, "show": [21, 22, 23, 24, 25, 26, 28, 41, 44], "render": [21, 36], "usag": [21, 23, 27, 30, 40, 46, 48], "manag": [21, 30, 31, 38, 44], "exit": [21, 22, 23, 24, 25, 26, 27, 28], "subcommand": [22, 23, 24, 25, 26, 27, 28], "shown": [22, 23, 24, 25, 26, 27, 28], "bash": [22, 23, 24, 25, 26, 27, 28], "verbos": [22, 23, 24, 25, 26, 27, 28, 50], "quiet": [22, 23, 24, 25, 26, 27, 28, 32], "dev": [22, 23, 24, 25, 26, 27, 28], "logfil": [22, 23, 24, 25, 26, 27, 28], "csv": [22, 24, 30], "txt": [22, 30], "v": [22, 23, 24, 25, 26, 27, 28], "c": [22, 23, 24, 25, 26, 27, 28, 30], "syntax": [22, 23, 24, 25, 26, 27, 28, 39], "driver": [22, 23, 24, 25, 26, 27, 28, 39], "password": [22, 23, 24, 25, 26, 27, 28, 31, 39], "host": [22, 23, 24, 25, 26, 27, 28, 39, 45], "port": [22, 23, 24, 25, 26, 27, 28, 31, 39], "extend": [22, 23, 24, 25, 26, 27, 28, 30], "q": [22, 23, 24, 25, 26, 27, 28], "suppress": [22, 23, 24, 25, 26, 27, 28], "mode": [22, 23, 24, 25, 26, 27, 28], "unexpect": [22, 23, 24, 25, 26, 27, 28], "handl": [22, 23, 24, 25, 26, 27, 28, 30, 42, 43, 44, 50], "screen": [22, 23, 24, 25, 26, 27, 28], "written": [22, 23, 24, 25, 26, 27, 28, 44], "stdout": [22, 23, 24, 25, 26, 27, 28], "filenam": 22, "header": [22, 40], "whitespac": [22, 43], "separ": [22, 30, 33, 45], "quot": 22, "accordingli": [22, 30, 47], "must": [22, 39, 40], "matchin": 22, "cli_creat": [22, 24], "ipynb": [22, 24, 28], "cli_popul": [22, 24], "cli_init": [22, 24, 28], "successfulli": [22, 24], "map": [22, 24, 26, 30, 44], "enit": [22, 24], "_map": [22, 24], "entity_map": 22, "datasourcetyp": [22, 24, 26, 30, 41, 43, 44], "due": [22, 24, 30, 48], "spell": [22, 24], "becaus": [22, 24, 30, 49], "context": [22, 24, 30, 31, 40, 44, 45, 51], "forc": [22, 24, 30], "least": [22, 24, 30, 32, 44, 47], "save": [22, 24, 32, 36, 43], "much": [22, 30, 40], "lower": 22, "semant": [22, 30], "workflow": 22, "individu": [22, 30], "cli_connect": [22, 24], "obvious": 22, "comma": 22, "itself": [22, 34, 38, 43, 44, 45, 46, 47, 50], "foo": [22, 32], "f": [22, 36, 44], "bar": 22, "b": 22, "nnfoo": 22, "nbar": 22, "easier": [22, 29, 50], "approach": 22, "familiar": 22, "task": [22, 31, 38], "cannot": [22, 24, 30, 40, 46, 50], "furthermor": [22, 24, 33], "place": [22, 23, 40, 44], "violat": 22, "constraint": 22, "prior": 22, "befor": [22, 26, 43], "typic": [22, 31], "involv": 22, "anyth": [22, 32, 39], "second": [22, 24, 32], "reflect": [22, 23, 30], "experiment": 22, "fulli": [22, 44], "illustr": 22, "awesom": [22, 24, 50], "our": 22, "nawesom": 22, "dummi": [22, 32], "test": [22, 32], "37": 22, "422051": 22, "122": 22, "084615": 22, "open": [22, 24, 29, 30, 32, 38, 39, 40, 44, 46], "w": [22, 32, 44], "j": [22, 32], "rm": 22, "And": 22, "cli_find": 22, "cli_show": 22, "21": 22, "01010000003f": 22, "05": 22, "45": 22, "24": 22, "827462": 22, "827531": 22, "827539": 22, "possbil": [22, 44, 51], "intend": [22, 30], "workaround": 22, "fail": 23, "fresh": 23, "updat": [23, 30, 33], "neither": 23, "nor": [23, 30], "postgi": [23, 38, 39, 48], "extens": [23, 30, 33, 39, 41, 44, 48], "grant": [23, 30], "right": [23, 30, 47], "super": [23, 35], "addiont": 23, "system": [23, 30, 34, 40, 44, 47, 49], "websit": [23, 39], "explan": 23, "everyth": 23, "extenst": 23, "verifi": [23, 44], "succeed": 23, "yield": 23, "select": [23, 31], "postgis_full_vers": 23, "r16526": 23, "pgsql": 23, "geo": [23, 44], "capi": 23, "11": [23, 24, 30, 43, 50], "27a5e771": 23, "proj": 23, "rel": 23, "august": 23, "2016": 23, "gdal": 23, "2017": 23, "libxml": 23, "libjson": 23, "libprotobuf": 23, "raster": [23, 30], "rather": [23, 26, 38, 39, 45], "BY": [24, 40], "wrap": [24, 50], "line": [24, 36, 38, 41], "clase": 24, "plan": [24, 30], "turn": [24, 33, 44, 47], "state": 24, "table_map": 24, "entry_group": 24, "spcifi": 24, "lot": [24, 31], "made": [24, 30, 43], "offer": [24, 35, 38], "granular": [24, 45], "stack": [24, 39], "top": [24, 36, 43], "effect": [24, 44], "logic": [24, 44], "perform": [24, 35, 43, 44], "odbl": [24, 40], "common": [24, 30, 31, 33, 38, 40, 43, 44, 45, 51], "v1": [24, 40], "creativ": [24, 40], "intern": [24, 40, 42, 43], "sharealik": 24, "noncommer": 24, "noncommerci": [24, 40], "hol": 24, "16": [24, 30], "instanst": 26, "importable_t": 26, "below": [26, 30, 42, 44, 47], "truncat": [27, 44], "valid": [27, 30, 36, 43, 47], "l": 27, "carefulli": 27, "sign": [27, 44, 45], "been": [28, 30, 39, 44], "cli_add": 28, "alwai": [28, 30, 33, 36, 40, 44, 47, 50, 51], "reqeust": 28, "how": [28, 29, 30, 40, 41, 44, 45, 49], "transform": [28, 40, 44, 47], "u": [28, 30, 31], "1baa552d": 28, "c563": 28, "43fb": 28, "b618": 28, "54651f8b07e6": 28, "959f1861": 28, "a776": 28, "41b1": 28, "ba6b": 28, "d23c71d4d1eb": 28, "9d86cd70": 28, "062a": 28, "4c39": 28, "b3f3": 28, "226abebc07f7": 28, "c84b61fe": 28, "720a": 28, "4240": 28, "b6c8": 28, "8dcc9ae24a36": 28, "6220": 28, "process": [28, 30, 39], "3609b843": 28, "d840": 28, "460c": 28, "b1a3": 28, "d4fcc69a32f6": 28, "36a2999b": 28, "2255": 28, "4d4e": 28, "a249": 28, "40df3b7b3aaf": 28, "269c7277": 28, "fa8f": 28, "4c1c": 28, "bd8b": 28, "ab772c1df4e5": 28, "7fdc339e": 28, "017f": 28, "4e4b": 28, "89a3": 28, "12e441a40bad": 28, "960037c5": 28, "57b1": 28, "4cdf": 28, "84be": 28, "4542beee7d5a": 28, "d4e8b5c5": 28, "9203": 28, "4982": 28, "82bc": 28, "2611b517ffdb": 28, "c6c0c5dd": 28, "c0ca": 28, "4670": 28, "bbaa": 28, "c22d39e73570": 28, "5cb5d5b9": 28, "0c0b": 28, "497f": 28, "a4ea": 28, "a8cece52d13d": 28, "6f52de55": 28, "f5f2": 28, "45c0": 28, "b83f": 28, "59dbfb1fe221": 28, "42aa1fa1": 28, "56a9": 28, "4e96": 28, "8063": 28, "077bd7ba88d8": 28, "84784fef": 28, "5b76": 28, "45a0": 28, "91e0": 28, "28788e09fea6": 28, "04922ba6": 28, "8f00": 28, "4f54": 28, "b80c": 28, "ce2414c91e2": 28, "f6a54329": 28, "486b": 28, "4d5f": 28, "b105": 28, "c639cec42351": 28, "tos": 29, "administr": 29, "fit": 29, "soon": [29, 32, 37], "overwrit": [29, 31, 40, 47], "so": 29, "whole": [30, 31, 43, 44, 47], "translat": [30, 33], "long": 30, "codit": 30, "fill": [30, 43, 45, 46], "publish": [30, 36, 40, 45], "codelist": 30, "everi": 30, "cardin": 30, "fileidentifi": 30, "languag": 30, "en": 30, "639": 30, "multi": [30, 40], "characterset": 30, "encod": [30, 39, 40, 43], "parentidentifi": 30, "hierachylevel": 30, "md_scopecod": 30, "hierachylevelnam": 30, "condit": 30, "contact": [30, 31, 36], "datestamp": 30, "19103": 30, "edit": [30, 31, 38, 44], "metadatastandardnam": 30, "iso19115": [30, 33, 36, 40, 44, 49], "metadatastandardvers": 30, "2019": 30, "entir": 30, "local": [30, 44, 47], "utf8": [30, 39], "19139": 30, "metadatalinkageurl": 30, "spatialrepresentationinfo": 30, "inspir": 30, "elev": 30, "referencesysteminfo": 30, "metadataextensioninfo": 30, "identificationinfo": 30, "md_identif": 30, "occar": 30, "contentinfo": 30, "anywai": 30, "distributioninfo": 30, "dataqualityinfo": 30, "implemend": 30, "portrayalcatalogueinfo": 30, "outsid": [30, 45], "applicationschemainfo": 30, "citat": [30, 33, 40, 44], "details_t": [30, 44], "fmt": [30, 44], "markdown": [30, 44], "statu": 30, "md_progresscod": 30, "pointofcontact": [30, 49], "doubl": 30, "resourcemainten": 30, "graphicoverview": 30, "descriptivekeyword": 30, "idea": 30, "resourcespecificusag": 30, "resourceconstraint": 30, "md_constrain": 30, "aggregationinfo": 30, "md_aggregationinform": 30, "spatialrepresentationtyp": 30, "vector": 30, "md_spatialrepresentation_typecod": 30, "spatialresolut": 30, "utf": [30, 33, 40, 43], "topiccategori": 30, "md_topiccategorycod": 30, "mappabl": 30, "environmentdescript": 30, "spatialscal": [30, 40, 41, 43], "temporalscal": [30, 40, 41, 43], "tempor": [30, 40, 43], "scale": [30, 40, 43], "supplementalinform": 30, "hierachi": 30, "theme": 30, "thesaurusurl": 30, "abl": 30, "citedresponsibleparti": 30, "organisationnam": [30, 36], "thesaurus_organis": 30, "contactinfo": 30, "onlineresourc": 30, "linkag": [30, 36], "thesaurus_url": 30, "denomin": 30, "resolut": [30, 40, 43], "extra": [30, 36], "extract": 30, "specificusag": 30, "usercontactinfo": 30, "univers": [30, 40], "aggregatedatasetidentifi": 30, "associated_group": [30, 33], "md_identifi": 30, "associationtyp": 30, "uselimit": 30, "redun": 30, "meet": 30, "ong": 30, "debat": 30, "gdi": 30, "de": 30, "moment": 30, "But": [30, 38], "leav": 30, "blank": 30, "dgi": 30, "duplic": [30, 44, 50], "useconstraint": 30, "satisfi": 30, "accessconstraint": 30, "md_restrictionscod": 30, "otherrestrict": 30, "note": [30, 33, 36, 40, 44, 45, 47, 50], "otherconstraint": 30, "framework": [30, 31], "unifi": [30, 50], "und": 30, "suggest": 30, "german": 30, "geodata": 30, "infrastructur": 30, "restrict": 30, "wish": 30, "privat": [30, 40], "secur": 30, "authentif": 30, "middlewar": 30, "issu": [30, 39], "classif": 30, "unclassifi": 30, "classifi": 30, "seri": 30, "dq_scope": 30, "report": 30, "dq_element": 30, "lineag": 30, "li_lineag": 30, "still": [30, 43, 44], "discuss": 30, "deriv": [30, 40, 44], "geograph": 30, "ep": 30, "wgs84": [30, 40, 44, 47], "next": [30, 39], "revis": 30, "technic": [30, 40, 51], "cr": [30, 40, 44, 47], "referenc": [30, 40, 43, 44, 46], "resolv": [30, 47], "amount": [30, 31, 39, 40, 47], "portay": 30, "relationship": [30, 40, 43, 48], "deliveri": 30, "almost": [30, 32], "append": [30, 35, 44, 47], "determin": [30, 36, 44], "owner": [30, 40, 49], "testdata": 30, "distributionformat": 30, "md_format": 30, "abov": [30, 50], "md_distributor": 30, "transferopt": 30, "md_digitaltransferopt": 30, "ci_onlineresourc": 30, "who": [30, 40, 49], "extendedroleinform": 30, "nm_entries_detail": 30, "realiz": 30, "shortnam": 30, "stem": [30, 44], "codelistel": 30, "domaincod": 30, "digit": 30, "definit": [30, 51], "oblig": 30, "md_obligationcod": 30, "characterstr": 30, "maximumoccur": 30, "domainvalu": 30, "arbitrari": [30, 40, 44], "parentent": 30, "rule": [30, 33], "rational": 30, "geographicel": 30, "temporalel": 30, "verticalel": 30, "ex_verticalel": 30, "io": [30, 35], "19108": 30, "rang": 30, "properti": [30, 33, 43, 44, 50], "alternatetitl": 30, "date": [30, 44], "ci_dat": 30, "editiond": 30, "could": [30, 39], "metacatlog": 30, "presentationform": 30, "ci_seri": 30, "othercitationdetail": 30, "collectivetitl": 30, "isbn": 30, "issn": 30, "cover": [30, 31, 40], "noaa": 30, "www": 30, "ngdc": 30, "wiki": [30, 45], "index": [30, 33, 44, 50], "php": 30, "iso_19115_and_19115": 30, "2_codelist_dictionari": 30, "adopt": 30, "expiri": 30, "inforc": 30, "lastrevis": 30, "nextupd": 30, "supersed": 30, "unavail": 30, "validitybegin": 30, "validityexpir": 30, "environment": [30, 38, 44], "mapdigit": 30, "modeldigit": 30, "tabledigit": 30, "physicalsampl": 30, "_after_": 30, "organ": 30, "whose": 30, "come": 30, "principl": [30, 44], "investig": 30, "synonym": 30, "mainli": 30, "memo": 30, "custodian": 30, "account": 30, "ensur": 30, "appropri": [30, 48], "care": 30, "mainten": 30, "aggreg": 30, "signal": 30, "ownership": 30, "initi": [30, 32], "triag": 30, "answer": 30, "question": 30, "principalinvestig": 30, "lead": [30, 39], "research": 30, "laboratori": 30, "leader": 30, "princip": 30, "repeat": 30, "processor": [30, 49], "manner": [30, 46, 47], "prepar": [30, 40, 44], "resourceprovid": 30, "alloc": 30, "sponsor": 30, "sponsorship": 30, "consum": 30, "denot": 30, "collabor": 30, "parti": [30, 44, 45], "assist": 30, "deserv": 30, "recognit": 30, "credit": 30, "acknowledg": 30, "editor": [30, 40], "editori": 30, "systemat": 30, "funder": 30, "financ": 30, "mediat": 30, "access": [30, 39, 47], "whom": 30, "rightshold": 30, "he": 30, "stakehold": 30, "interest": 30, "affect": [30, 32, 44], "crossrefer": 30, "largerworkcit": 30, "mislead": 30, "partofseamlessdatabas": 30, "imag": 30, "stereom": 30, "iscomposedof": 30, "revisionof": 30, "initiativetypecod": 30, "platform": 30, "around": [30, 41, 44, 47], "share": [30, 40, 50], "grid": [30, 43], "cell": [30, 43], "dedic": 30, "put": 30, "visibl": 30, "geometr": [30, 40], "dimens": 30, "optin": 30, "decid": [30, 31], "icreas": 31, "suit": 31, "assur": [31, 46], "unittest": 31, "mock": 31, "magnitud": 31, "larger": 31, "javascript": 31, "pytest": 31, "compar": 31, "minor": 31, "man": 31, "spot": 31, "coverag": [31, 47], "me": 31, "via": [31, 33, 36, 40, 44, 45, 46, 47, 51], "patient": 31, "pip": [31, 39], "cov": 31, "test_": 31, "z": 31, "postgres_us": 31, "postgres_port": 31, "dbname": 31, "track": 31, "finish": [31, 35], "cleanup": 31, "copi": [31, 32, 46], "upgrad": 31, "yourself": [31, 38], "left": [31, 47], "mayb": 31, "hunder": 31, "drop": 31, "chunk": 31, "convent": 31, "sudo": [31, 39], "psql": [31, 39], "atc": 31, "datnam": 31, "pg_databas": 31, "basic": 32, "mechan": 32, "inherit": [32, 43, 44], "metacatalogextensioninterfac": [32, 44], "dummyextens": 32, "def": [32, 35, 44], "init_extens": [32, 35, 44], "ext": [32, 33, 35, 36, 44], "savetofileextens": 32, "classmethod": [32, 33, 35, 36, 44, 49, 50, 51], "setattr": 32, "savetojson": 32, "complic": 32, "exampl": [32, 36, 39, 43, 44, 45, 50], "re": [32, 39, 50], "statement": 32, "old": 32, "printextens": 32, "new_init": 32, "init_cli": [32, 36], "subpars": [32, 36], "_subparsersact": 32, "argumentpars": 32, "parser": [32, 36], "hold": [32, 40, 44], "myext": 32, "upper": 32, "mypars": 32, "add_pars": 32, "foobar": [32, 40], "add_argu": 32, "nonsens": 32, "set_default": 32, "func": 32, "exportextens": [33, 44], "produc": [33, 36], "fall": 33, "back": [33, 47], "nativ": 33, "fast_xml": 33, "pickl": 33, "netcdf": 33, "dicttoxml": 33, "great": [33, 38], "adjust": 33, "lxml": 33, "reserv": 33, "pack": 33, "decod": 33, "underli": 33, "xarrai": 33, "particularli": 33, "no_data": 33, "fast": 33, "serializ": [33, 43, 44], "larg": [33, 44, 47], "hardcod": [33, 50], "overwritten": [33, 40], "entry_kei": 33, "use_kei": 33, "k": 33, "startswith": 33, "temp": 33, "flat_kei": 33, "delimit": [33, 40], "nest": [33, 40, 50], "flat": 33, "expand": [33, 50], "get_data": [33, 44, 50], "serial": 33, "un": 33, "formerli": 33, "location_shap": 33, "latest_vers": 33, "plain_keyword_dict": 33, "clean": 33, "featur": 34, "ioextens": [35, 44], "crud": 35, "todo": 35, "after_append": 35, "abstractmethod": 35, "templat": [35, 36], "iointerfac": [35, 44], "ioextensioninterfac": 35, "after_delet": 35, "delt": [35, 44], "import_": 35, "after_import": 35, "after_read": 35, "standards_export": 36, "standardsexportextens": 36, "iso": [36, 40, 43, 49], "19115": [36, 49], "datacit": [36, 40], "jinja": 36, "template_path": 36, "elementtre": 36, "create_standards_xml": 36, "catalog": 36, "write": [36, 44], "cli_create_standards_xml": 36, "directori": 36, "form": [36, 43, 50], "iso19115_": 36, "irs_uuid": 36, "datacite_": 36, "id_or_uuid": 36, "config_dict": 36, "configur": 36, "deliverypoint": 36, "citi": 36, "administrativearea": 36, "postalcod": 36, "countri": 36, "electronicmailaddress": 36, "linkage_nam": 36, "linkage_descript": 36, "auto": 36, "etre": 36, "jinja2": 36, "syntact": 36, "never": 36, "provic": 36, "doi": [36, 44], "notimplementederror": 36, "xml_etre": 36, "entry_or_resultset": 36, "runner": 36, "schema": 36, "j2": 36, "paramt": 36, "standards_export_contact": 36, "configfil": 36, "v0": 36, "tool": 38, "primarili": [38, 44, 45, 49], "submodul": [38, 44], "learn": [38, 44], "freedom": 38, "robust": 38, "autom": 38, "quickli": 38, "less": 38, "balanc": 38, "usabl": 38, "offici": [39, 45], "look": [39, 44], "apt": 39, "outdat": 39, "up": [39, 40, 47, 48], "big": 39, "v2": 39, "chosen": 39, "gui": 39, "pgadmin": 39, "pypi": 39, "previous": 39, "yourpassword": 39, "filenotfounderror": 39, "conda": 39, "sumamr": 40, "user_provid": 40, "ye": 40, "campain": 40, "ie": 40, "cite": [40, 46], "style": [40, 44], "natur": 40, "english": 40, "searchabl": 40, "prooven": 40, "becom": [40, 45], "righthold": 40, "properli": 40, "decis": 40, "scheme": 40, "wherev": [40, 44], "odc": 40, "cc": 40, "nc": 40, "discourag": 40, "triplet": [40, 43], "compon": 40, "8601": [40, 43], "durat": [40, 43], "decrib": 40, "observation_start": [40, 43], "observation_end": [40, 43], "columnar": 40, "ratio": 40, "observ": [40, 43], "timestep": 40, "smaller": 40, "1hour": 40, "30min": 40, "timestamp": [40, 43, 44], "estim": 40, "approxim": 40, "whenev": [40, 47], "remot": [40, 42], "truth": [40, 43], "data_nam": [40, 43], "endcod": 40, "kept": 40, "earthdata": 40, "idn": 40, "hierach": [40, 45], "additon": 40, "mykei": 40, "additin": 40, "sheme": 40, "own": [40, 44], "again": [40, 50], "indic": 40, "along": [40, 44], "label": 40, "commonli": [40, 43, 51], "caus": 40, "charact": 40, "network": 40, "builtin": [41, 44], "log": 41, "entrygroupassoci": [41, 44], "resultset": [41, 48], "build_queri": [41, 47], "get_search_shap": [41, 47], "keywordassoci": [41, 45], "personassoci": [41, 49], "timeseri": [41, 42, 44], "genericgeometrydata": [41, 42], "geometrytimeseri": [41, 42], "datapoint": [41, 42], "datapoint2d": [41, 42], "guid": 41, "flexibl": 42, "additionali": 42, "geometry_data": 42, "generic_data": 42, "filepath": 43, "tablenam": 43, "pars": 43, "type_id": [43, 44], "foreign": [43, 44, 45], "over": [43, 50], "create_scal": 43, "scale_dimens": 43, "commit": [43, 44], "load_arg": 43, "save_args_from_dict": 43, "args_dict": 43, "adder": 43, "obj": [43, 44, 45, 46, 49, 51], "reader": 43, "sever": 43, "writer": 43, "children_list": 43, "parent_list": 43, "space": 43, "resoult": 43, "size": 43, "resolution_str": 43, "versionchang": 43, "calcul": [43, 47], "south": 43, "geoalchemy2": [43, 48], "fraction": 43, "footprint": 43, "substitut": 43, "standalon": 43, "minut": 43, "non": [43, 45], "min": 43, "15min": 43, "built": [43, 44], "p": 43, "dt": 43, "togheth": 43, "10min": 43, "5min": 43, "exhaust": 43, "That": [44, 45, 51], "512": 44, "oid": 44, "unqu": 44, "storag": 44, "solut": 44, "exernal_id": 44, "unproject": [44, 47], "wkb": [44, 47], "compliant": 44, "datimetim": 44, "manual": 44, "free": 44, "informatio": 44, "bibliograph": 44, "bibliographi": 44, "thrid": 44, "softwar": 44, "act": 44, "sort": [44, 50], "One": [44, 46], "strategi": 44, "eddi": 44, "covari": 44, "secondli": 44, "add_detail": 44, "transact": 44, "append_data": 44, "behaviour": 44, "checksum": [44, 50], "md5": [44, 50], "create_datasourc": 44, "datacourc": 44, "resid": 44, "delete_data": 44, "delete_sourc": 44, "details_dict": 44, "plain": [44, 46], "latex": 44, "consid": 44, "rawjsonextens": 44, "stringifi": 44, "load_extens": 44, "testfil": 44, "from_dict": [44, 49, 51], "import_data": 44, "io_interfac": 44, "keyword_list": 44, "keywords_dict": 44, "stand": 44, "alon": 44, "insepar": 44, "neighbor": [44, 47], "buffer_epsg": 44, "3857": [44, 47], "as_sql": 44, "maximum": 44, "mile": [44, 47], "nautic": [44, 47], "identif": 44, "cartesian": 44, "transvers": [44, 47], "mercartor": [44, 47], "uncertainti": 44, "small": [44, 47], "area": [44, 47], "plain_keywords_dict": 44, "plain_keywords_list": 44, "set_new_author": 44, "new_author": 44, "boolean": 44, "uncommit": 44, "ship": [44, 46], "autogener": 44, "utc": 44, "major": 45, "metadataset": 45, "word": 45, "ideal": 45, "third": 45, "cross": 45, "parent_id": 45, "uppercas": 45, "ancestor": 45, "contruct": 45, "onr": 45, "tagged_entri": 45, "categori": 45, "variable_level_2": 45, "variable_level_3": 45, "detailed_vari": 45, "keyword_id": 45, "serv": 45, "climat": 45, "practic": 45, "distiributor": 45, "obviou": 45, "matadata": 45, "placehod": 45, "isnstanc": 45, "specfi": 45, "server": 45, "gpl": 46, "agreement": 46, "contract": 46, "acknowleg": 46, "fiendli": 46, "commer": 46, "get_full_text": 46, "connection_error": 46, "uitl": 47, "advanc": 47, "variou": 47, "buffer_use_epsg": 47, "convex": 47, "hull": 47, "mind": 47, "shall": 47, "accuraci": 47, "implic": 47, "nevertheless": 47, "reproject": 47, "possibli": 47, "unfortun": 47, "massiv": 47, "especi": 47, "studi": 47, "utm": 47, "helper": 47, "exisit": 47, "unexecut": 47, "basegeometri": 47, "bunch": 47, "remind": 47, "euklidean": 47, "rich": 47, "boundingbox": 47, "bottom": 47, "dimension": 47, "representend": 48, "enabl": 48, "metatacatalog": 49, "affil": 49, "invalid": 49, "fallback": 49, "deliv": 50, "member": 50, "c1087040": 50, "5566": 50, "44b9": 50, "9852": 50, "ea600f73ae0c": 50, "0350c985": 50, "4e43": 50, "4876": 50, "a03a": 50, "7b6fb2a3a4b6": 50, "c6d6301b": 50, "8e1c": 50, "478f": 50, "9407": 50, "ce9a0c38dbb8": 50, "altern": 50, "reproduc": 50, "tell": 50, "appart": 50, "hash": 50, "contains_uuid": 50, "entry_set": 50, "expand_entri": 50, "expans": 50, "load_base_group": 50, "strongest": 50, "orient": 50, "loop": 50, "to_short_info": 50, "dynam": 50, "_member": 50, "revert": 50, "convers": 51, "1000": 51, "oberserv": 51, "too": 51, "keep": 51, "decagon": 51, "5te": 51, "moistur": 51, "conduct": 51}, "objects": {"metacatalog": [[0, 0, 0, "-", "api"], [34, 0, 0, "-", "ext"], [48, 0, 0, "-", "models"]], "metacatalog.api": [[1, 1, 1, "", "add_details_to_entries"], [1, 1, 1, "", "add_entry"], [2, 1, 1, "", "add_group"], [3, 1, 1, "", "add_keyword"], [3, 1, 1, "", "add_keywords_to_entries"], [4, 1, 1, "", "add_license"], [5, 1, 1, "", "add_person"], [5, 1, 1, "", "add_persons_to_entries"], [2, 1, 1, "", "add_project"], [6, 1, 1, "", "add_thesaurus"], [7, 1, 1, "", "add_unit"], [7, 1, 1, "", "add_variable"], [8, 1, 1, "", "connect_database"], [9, 1, 1, "", "create_tables"], [6, 2, 1, "", "description"], [10, 1, 1, "", "find_entry"], [10, 1, 1, "", "find_group"], [10, 1, 1, "", "find_group_type"], [11, 1, 1, "", "find_keyword"], [12, 1, 1, "", "find_license"], [13, 1, 1, "", "find_person"], [13, 1, 1, "", "find_role"], [14, 1, 1, "", "find_thesaurus"], [15, 1, 1, "", "find_unit"], [15, 1, 1, "", "find_variable"], [16, 1, 1, "", "get_logger"], [17, 1, 1, "", "get_uuid"], [6, 2, 1, "", "name"], [6, 2, 1, "", "organisation"], [18, 1, 1, "", "populate_defaults"], [6, 2, 1, "", "session"], [19, 1, 1, "", "show_attributes"], [20, 1, 1, "", "show_records"], [6, 2, 1, "", "title"], [6, 2, 1, "", "url"], [6, 2, 1, "", "uuid"]], "metacatalog.ext": [[32, 0, 0, "-", "base"], [33, 0, 0, "-", "export"], [35, 0, 0, "-", "io"], [36, 0, 0, "-", "standards_export"]], "metacatalog.ext.base": [[32, 3, 1, "", "MetacatalogExtensionInterface"]], "metacatalog.ext.base.MetacatalogExtensionInterface": [[32, 4, 1, "", "init_cli"]], "metacatalog.ext.export": [[33, 3, 1, "", "ExportExtension"]], "metacatalog.ext.export.ExportExtension": [[33, 4, 1, "", "fast_xml"], [33, 4, 1, "", "flat_keys"], [33, 4, 1, "", "get_data"], [33, 4, 1, "", "json"], [33, 4, 1, "", "netcdf"], [33, 4, 1, "", "pickle"], [33, 4, 1, "", "to_dict"]], "metacatalog.ext.io": [[35, 3, 1, "", "IOExtension"]], "metacatalog.ext.io.IOExtension": [[35, 4, 1, "", "append"], [35, 4, 1, "", "delete"], [35, 4, 1, "", "import_"], [35, 4, 1, "", "init_extension"], [35, 4, 1, "", "read"]], "metacatalog.ext.standards_export": [[36, 3, 1, "", "StandardsExportExtension"]], "metacatalog.ext.standards_export.StandardsExportExtension": [[36, 4, 1, "", "cli_create_standards_xml"], [36, 4, 1, "", "create_standards_xml"], [36, 4, 1, "", "init_cli"], [36, 4, 1, "", "standards_export"]], "metacatalog.models": [[43, 0, 0, "-", "datasource"], [44, 0, 0, "-", "entry"], [44, 0, 0, "-", "entrygroup"], [42, 0, 0, "-", "generic_data"], [42, 0, 0, "-", "geometry_data"], [45, 0, 0, "-", "keyword"], [46, 0, 0, "-", "license"], [49, 0, 0, "-", "person"], [42, 0, 0, "-", "timeseries"], [51, 0, 0, "-", "variable"]], "metacatalog.models.datasource": [[43, 3, 1, "", "DataSource"], [43, 3, 1, "", "DataSourceType"], [43, 3, 1, "", "DataType"], [43, 3, 1, "", "SpatialScale"], [43, 3, 1, "", "TemporalScale"]], "metacatalog.models.datasource.DataSource": [[43, 2, 1, "", "args"], [43, 4, 1, "", "create_scale"], [43, 2, 1, "", "data_names"], [43, 2, 1, "", "encoding"], [43, 2, 1, "", "id"], [43, 4, 1, "", "load_args"], [43, 2, 1, "", "path"], [43, 4, 1, "", "save_args_from_dict"], [43, 4, 1, "", "to_dict"], [43, 2, 1, "", "type"], [43, 2, 1, "", "type_id"]], "metacatalog.models.datasource.DataSourceType": [[43, 2, 1, "", "description"], [43, 2, 1, "", "id"], [43, 2, 1, "", "name"], [43, 2, 1, "", "title"], [43, 4, 1, "", "to_dict"]], "metacatalog.models.datasource.DataType": [[43, 4, 1, "", "children_list"], [43, 2, 1, "", "description"], [43, 2, 1, "", "id"], [43, 2, 1, "", "name"], [43, 4, 1, "", "parent_list"], [43, 2, 1, "", "title"], [43, 4, 1, "", "to_dict"]], "metacatalog.models.datasource.SpatialScale": [[43, 2, 1, "", "extent"], [43, 2, 1, "", "id"], [43, 2, 1, "", "resolution"], [43, 2, 1, "", "support"], [43, 4, 1, "", "to_dict"]], "metacatalog.models.datasource.TemporalScale": [[43, 2, 1, "", "id"], [43, 2, 1, "", "observation_end"], [43, 2, 1, "", "observation_start"], [43, 2, 1, "", "resolution"], [43, 2, 1, "", "support"], [43, 4, 1, "", "to_dict"]], "metacatalog.models.entry": [[44, 3, 1, "", "Entry"]], "metacatalog.models.entry.Entry": [[44, 2, 1, "", "abstract"], [44, 4, 1, "", "add_details"], [44, 4, 1, "", "append_data"], [44, 2, 1, "", "author"], [44, 2, 1, "", "authors"], [44, 5, 1, "", "checksum"], [44, 2, 1, "", "citation"], [44, 2, 1, "", "comment"], [44, 4, 1, "", "create_datasource"], [44, 2, 1, "", "creation"], [44, 4, 1, "", "delete_data"], [44, 4, 1, "", "details_dict"], [44, 4, 1, "", "details_table"], [44, 2, 1, "", "end"], [44, 4, 1, "", "export"], [44, 2, 1, "", "external_id"], [44, 4, 1, "", "from_dict"], [44, 4, 1, "", "get_data"], [44, 2, 1, "", "id"], [44, 4, 1, "", "import_data"], [44, 2, 1, "", "io_interface"], [44, 2, 1, "", "is_partial"], [44, 4, 1, "", "keyword_list"], [44, 4, 1, "", "keywords_dict"], [44, 2, 1, "", "latest_version_id"], [44, 2, 1, "", "license"], [44, 2, 1, "", "license_id"], [44, 2, 1, "", "location"], [44, 4, 1, "", "make_composite"], [44, 4, 1, "", "neighbors"], [44, 4, 1, "", "plain_keywords_dict"], [44, 4, 1, "", "plain_keywords_list"], [44, 4, 1, "", "set_new_author"], [44, 2, 1, "", "title"], [44, 4, 1, "", "to_dict"], [44, 2, 1, "", "uuid"], [44, 2, 1, "", "version"]], "metacatalog.models.entrygroup": [[44, 3, 1, "", "EntryGroup"], [44, 3, 1, "", "EntryGroupAssociation"], [44, 3, 1, "", "EntryGroupType"]], "metacatalog.models.entrygroup.EntryGroup": [[44, 5, 1, "", "checksum"], [44, 2, 1, "", "description"], [44, 4, 1, "", "export"], [44, 2, 1, "", "id"], [44, 2, 1, "", "lastUpdate"], [44, 2, 1, "", "publication"], [44, 2, 1, "", "title"], [44, 4, 1, "", "to_dict"], [44, 2, 1, "", "type"], [44, 2, 1, "", "type_id"], [44, 2, 1, "", "uuid"]], "metacatalog.models.entrygroup.EntryGroupType": [[44, 4, 1, "", "to_dict"]], "metacatalog.models.generic_data": [[42, 3, 1, "", "DataPoint"], [42, 3, 1, "", "DataPoint2D"]], "metacatalog.models.geometry_data": [[42, 3, 1, "", "GenericGeometryData"], [42, 3, 1, "", "GeometryTimeseries"]], "metacatalog.models.keyword": [[45, 3, 1, "", "Keyword"], [45, 3, 1, "", "KeywordAssociation"], [45, 3, 1, "", "Thesaurus"]], "metacatalog.models.keyword.Keyword": [[45, 2, 1, "", "full_path"], [45, 2, 1, "", "id"], [45, 2, 1, "", "parent_id"], [45, 4, 1, "", "path"], [45, 2, 1, "", "tagged_entries"], [45, 2, 1, "", "thesaurusName"], [45, 2, 1, "", "thesaurus_id"], [45, 4, 1, "", "to_dict"], [45, 2, 1, "", "uuid"], [45, 2, 1, "", "value"]], "metacatalog.models.keyword.KeywordAssociation": [[45, 2, 1, "", "entry_id"], [45, 2, 1, "", "keyword_id"]], "metacatalog.models.keyword.Thesaurus": [[45, 2, 1, "", "description"], [45, 2, 1, "", "id"], [45, 2, 1, "", "name"], [45, 2, 1, "", "organisation"], [45, 2, 1, "", "title"], [45, 4, 1, "", "to_dict"], [45, 2, 1, "", "url"], [45, 2, 1, "", "uuid"]], "metacatalog.models.license": [[46, 3, 1, "", "License"]], "metacatalog.models.license.License": [[46, 2, 1, "", "by_attribution"], [46, 2, 1, "", "commercial_use"], [46, 2, 1, "", "full_text"], [46, 4, 1, "", "get_full_text"], [46, 2, 1, "", "id"], [46, 2, 1, "", "link"], [46, 2, 1, "", "share_alike"], [46, 2, 1, "", "short_title"], [46, 2, 1, "", "summary"], [46, 2, 1, "", "title"], [46, 4, 1, "", "to_dict"]], "metacatalog.models.person": [[49, 3, 1, "", "Person"], [49, 3, 1, "", "PersonAssociation"], [49, 3, 1, "", "PersonRole"]], "metacatalog.models.person.Person": [[49, 2, 1, "", "affiliation"], [49, 2, 1, "", "attribution"], [49, 2, 1, "", "entries"], [49, 2, 1, "", "first_name"], [49, 4, 1, "", "from_dict"], [49, 2, 1, "", "id"], [49, 2, 1, "", "last_name"], [49, 2, 1, "", "organisation_name"], [49, 4, 1, "", "to_dict"], [49, 2, 1, "", "uuid"]], "metacatalog.models.person.PersonAssociation": [[49, 4, 1, "", "to_dict"]], "metacatalog.models.person.PersonRole": [[49, 4, 1, "", "to_dict"]], "metacatalog.models.timeseries": [[42, 3, 1, "", "Timeseries"]], "metacatalog.models.variable": [[51, 3, 1, "", "Unit"], [51, 3, 1, "", "Variable"]], "metacatalog.models.variable.Unit": [[51, 2, 1, "", "id"], [51, 2, 1, "", "name"], [51, 2, 1, "", "si"], [51, 2, 1, "", "symbol"], [51, 4, 1, "", "to_dict"], [51, 2, 1, "", "variables"]], "metacatalog.models.variable.Variable": [[51, 2, 1, "", "column_names"], [51, 4, 1, "", "from_dict"], [51, 2, 1, "", "id"], [51, 2, 1, "", "name"], [51, 2, 1, "", "si"], [51, 2, 1, "", "symbol"], [51, 4, 1, "", "to_dict"], [51, 2, 1, "", "variables"]], "metacatalog.util": [[47, 0, 0, "-", "location"], [50, 0, 0, "-", "results"]], "metacatalog.util.location": [[47, 1, 1, "", "around"], [47, 1, 1, "", "build_query"], [47, 1, 1, "", "get_search_shape"]], "metacatalog.util.results": [[50, 3, 1, "", "ImmutableResultSet"]], "metacatalog.util.results.ImmutableResultSet": [[50, 5, 1, "", "checksum"], [50, 5, 1, "", "checksums"], [50, 4, 1, "", "contains_uuid"], [50, 5, 1, "", "empty"], [50, 4, 1, "", "entry_set"], [50, 4, 1, "", "expand_entry"], [50, 4, 1, "", "get"], [50, 4, 1, "", "get_data"], [50, 4, 1, "", "load_base_group"], [50, 4, 1, "", "to_dict"], [50, 4, 1, "", "to_short_info"], [50, 5, 1, "", "uuid"], [50, 5, 1, "", "uuids"]]}, "objtypes": {"0": "py:module", "1": "py:function", "2": "py:attribute", "3": "py:class", "4": "py:method", "5": "py:property"}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "function", "Python function"], "2": ["py", "attribute", "Python attribute"], "3": ["py", "class", "Python class"], "4": ["py", "method", "Python method"], "5": ["py", "property", "Python property"]}, "titleterms": {"api": 0, "metacatalog": [0, 31, 39, 41], "overview": [0, 21, 30, 34, 40], "import": 0, "function": [0, 16], "command": [0, 21, 22, 23, 24, 25, 26, 27, 28], "add": [1, 2, 3, 4, 5, 6, 7, 22], "entri": [1, 3, 5, 10, 44], "detail": 1, "keyword": [1, 3, 11, 45], "entrygroup": 2, "group": [2, 10], "project": 2, "licens": [4, 12, 46], "person": [5, 13], "thesauru": [6, 14], "variabl": [7, 15, 51], "unit": [7, 15], "connect": [8, 22, 24], "databas": [8, 48], "exampl": [8, 11, 14, 17, 19, 20, 22, 24, 28], "store": 8, "default": [8, 18], "without": 8, "password": 8, "creat": [9, 23, 39], "tabl": [9, 23, 39, 42], "find": [10, 11, 12, 13, 14, 15, 24], "type": 10, "help": [11, 14, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28], "role": [13, 30], "associ": 13, "log": 16, "get": [17, 37], "uuid": [17, 28], "funtion": 17, "popul": [18, 26], "show": [19, 20, 27], "attribut": 19, "record": 20, "cli": 21, "line": 21, "interfac": 21, "builtin": 21, "prerequisit": [22, 23, 26, 28, 39], "usag": [22, 24, 25, 26, 28], "entiti": [22, 24, 40], "pass": [22, 24], "argument": [22, 24], "oper": 22, "relat": [23, 26], "see": [23, 25, 26], "also": [23, 25, 26], "prerequist": 24, "init": 25, "develop": 29, "guid": 29, "iso": 30, "19115": 30, "field": 30, "md_metadata": 30, "md_indentif": 30, "md_dataidentif": 30, "md_browsegraph": 30, "md_keyword": 30, "md_representativefract": 30, "md_resolut": 30, "md_usag": 30, "md_aggregateinform": 30, "md_constraint": 30, "md_legalconstraint": 30, "md_securityconstraint": 30, "dq_dataqu": 30, "md_maintenaceinform": 30, "md_spatialrepresent": 30, "md_referencesystem": 30, "md_contentinform": 30, "md_portrayalcataloguerefer": 30, "md_distribut": 30, "md_metadataextensioninform": 30, "md_extendedelementinform": 30, "md_applicationschema": 30, "ex_ext": 30, "ex_geographicext": 30, "ex_temporalext": 30, "ex_verticalext": 30, "ci_cit": 30, "ci_responsibleparti": 30, "code": 30, "list": 30, "ci_datetypecod": 30, "ci_presentationformcod": 30, "ci_rolecod": 30, "dq_evaluationmethodtypecod": 30, "ds_associationtypecod": 30, "ds_initiativetypecod": 30, "md_cellgeometrycod": 30, "md_charactersetcod": 30, "md_classificationcod": 30, "md_coveragecontenttypecod": 30, "md_datatypecod": 30, "md_dimensionnametypecod": 30, "md_geometricobjecttypecod": 30, "md_imagingconditioncod": 30, "md_keywordtypecod": 30, "e2": 31, "test": 31, "run": 31, "local": 31, "clean": 31, "up": 31, "custom": 32, "extens": [32, 34, 35, 36], "export": [33, 36], "extenst": 33, "read": 35, "write": 35, "standard": 36, "start": 37, "home": 38, "how": 38, "doc": 38, "work": 38, "instal": 39, "metadata": 40, "avail": 40, "metdata": 40, "descript": 40, "welcom": 41, "": 41, "document": 41, "content": 41, "data": [42, 46], "datasourc": 43, "control": 45, "locat": 47, "filter": 47, "model": 48, "author": 49, "contributor": 49, "resultset": 50, "refer": 50, "todo": 50}, "envversion": {"sphinx.domains.c": 2, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 8, "sphinx.domains.index": 1, "sphinx.domains.javascript": 2, "sphinx.domains.math": 2, "sphinx.domains.python": 3, "sphinx.domains.rst": 2, "sphinx.domains.std": 2, "sphinx.ext.todo": 2, "nbsphinx": 4, "sphinx": 57}, "alltitles": {"API": [[0, "api"]], "Metacatalog API Overview": [[0, "module-metacatalog.api"]], "Importing": [[0, "importing"]], "Functions": [[0, "functions"]], "Command Overview": [[0, "command-overview"], [0, null], [21, "command-overview"], [21, null]], "Add Entry": [[1, "Add-Entry"]], "Entry": [[1, "Entry"], [10, "Entry"], [44, "module-metacatalog.models.entry"]], "Add Entry details": [[1, "Add-Entry-details"]], "Add keywords to entries": [[1, "Add-keywords-to-entries"], [3, "Add-keywords-to-entries"]], "Add EntryGroup": [[2, "Add-EntryGroup"]], "Group": [[2, "Group"]], "Project": [[2, "Project"]], "Add keyword": [[3, "Add-keyword"]], "Keyword": [[3, "Keyword"]], "Add license": [[4, "Add-license"]], "Add Person": [[5, "Add-Person"]], "Person": [[5, "Person"], [13, "Person"]], "Add persons to entries": [[5, "Add-persons-to-entries"]], "Add Thesaurus": [[6, "Add-Thesaurus"]], "Add variable and unit": [[7, "Add-variable-and-unit"]], "variable": [[7, "variable"]], "unit": [[7, "unit"]], "Connect database": [[8, "Connect-database"]], "Example": [[8, "Example"], [11, "Example"], [14, "Example"], [17, "Example"], [19, "Example"], [20, "Example"], [22, "Example"], [24, "Example"], [28, "Example"]], "Stored default connection": [[8, "Stored-default-connection"]], "Connect without storing passwords": [[8, "Connect-without-storing-passwords"]], "Create Tables": [[9, "Create-Tables"], [39, "create-tables"]], "Find Entry": [[10, "Find-Entry"]], "Entry-Group": [[10, "Entry-Group"]], "Group type": [[10, "Group-type"]], "Find Keyword": [[11, "Find-Keyword"]], "Help": [[11, "Help"], [14, "Help"], [19, "Help"], [20, "Help"], [22, "Help"], [23, "Help"], [24, "Help"], [25, "Help"], [26, "Help"], [27, "Help"], [28, "Help"]], "Find License": [[12, "Find-License"]], "Find person": [[13, "Find-person"]], "Role": [[13, "Role"]], "Associations": [[13, "Associations"]], "Find Thesaurus": [[14, "Find-Thesaurus"]], "Find Variable": [[15, "Find-Variable"]], "Variable": [[15, "Variable"]], "Unit": [[15, "Unit"]], "Logging": [[16, "Logging"]], "Function": [[16, "Function"]], "Get UUID": [[17, "Get-UUID"]], "Funtion": [[17, "Funtion"]], "Populate defaults": [[18, "Populate-defaults"]], "Show Attributes": [[19, "Show-Attributes"]], "Show Records": [[20, "Show-Records"]], "CLI": [[21, "cli"]], "Command Line Interface": [[21, "command-line-interface"]], "Builtin Help": [[21, "builtin-help"]], "Add command": [[22, "Add-command"]], "Prerequisites": [[22, "Prerequisites"], [23, "Prerequisites"], [26, "Prerequisites"], [28, "Prerequisites"], [39, "prerequisites"]], "Usage": [[22, "Usage"], [24, "Usage"], [25, "Usage"], [26, "Usage"], [28, "Usage"]], "entity": [[22, "entity"], [24, "entity"]], "connection": [[22, "connection"], [24, "connection"]], "passing arguments": [[22, "passing-arguments"], [24, "passing-arguments"]], "Operations": [[22, "Operations"]], "Create Command": [[23, "Create-Command"]], "Creating tables": [[23, "Creating-tables"]], "Related commands": [[23, "Related-commands"], [26, "Related-commands"]], "See Also": [[23, "See-Also"], [25, "See-Also"]], "Find Command": [[24, "Find-Command"]], "Prerequists": [[24, "Prerequists"]], "Init command": [[25, "Init-command"]], "Populate command": [[26, "Populate-command"]], "See also": [[26, "See-also"]], "Show command": [[27, "Show-command"]], "Uuid command": [[28, "Uuid-command"]], "Developers": [[29, "developers"]], "Guide": [[29, "guide"]], "ISO 19115": [[30, "iso-19115"]], "Overview": [[30, "overview"], [34, "module-metacatalog.ext"]], "ISO 19115 Fields": [[30, "iso-19115-fields"]], "MD_Metadata": [[30, "md-metadata"]], "MD_Indentification": [[30, "md-indentification"]], "MD_DataIdentification": [[30, "md-dataidentification"]], "MD_BrowseGraphic": [[30, "md-browsegraphic"]], "MD_Keywords": [[30, "md-keywords"]], "MD_RepresentativeFraction": [[30, "md-representativefraction"]], "MD_Resolution": [[30, "md-resolution"]], "MD_Usage": [[30, "md-usage"]], "MD_AggregateInformation": [[30, "md-aggregateinformation"]], "MD_Constraints": [[30, "md-constraints"]], "MD_LegalConstraints": [[30, "md-legalconstraints"]], "MD_SecurityConstraints": [[30, "md-securityconstraints"]], "DQ_DataQuality": [[30, "dq-dataquality"]], "MD_MaintenaceInformation": [[30, "md-maintenaceinformation"]], "MD_SpatialRepresentation": [[30, "md-spatialrepresentation"]], "MD_ReferenceSystem": [[30, "md-referencesystem"]], "MD_ContentInformation": [[30, "md-contentinformation"]], "MD_PortrayalCatalogueReference": [[30, "md-portrayalcataloguereference"]], "MD_Distribution": [[30, "md-distribution"]], "MD_MetadataExtensionInformation": [[30, "md-metadataextensioninformation"]], "MD_ExtendedElementInformation": [[30, "md-extendedelementinformation"]], "MD_ApplicationSchema": [[30, "md-applicationschema"]], "Ex_Extent": [[30, "ex-extent"]], "EX_GeographicExtent": [[30, "ex-geographicextent"]], "EX_TemporalExtent": [[30, "ex-temporalextent"]], "EX_VerticalExtent": [[30, "ex-verticalextent"]], "CI_Citation": [[30, "ci-citation"]], "CI_ResponsibleParty": [[30, "ci-responsibleparty"]], "Code-Lists": [[30, "code-lists"]], "CI_DateTypeCode": [[30, "ci-datetypecode"]], "CI_PresentationFormCode": [[30, "ci-presentationformcode"]], "CI_RoleCode": [[30, "ci-rolecode"]], "Roles": [[30, "id5"]], "DQ_EvaluationMethodTypeCode": [[30, "dq-evaluationmethodtypecode"]], "DS_AssociationTypeCode": [[30, "ds-associationtypecode"]], "DS_InitiativeTypeCode": [[30, "ds-initiativetypecode"]], "MD_CellGeometryCode": [[30, "md-cellgeometrycode"]], "MD_CharacterSetCode": [[30, "md-charactersetcode"]], "MD_ClassificationCode": [[30, "md-classificationcode"]], "MD_CoverageContentTypeCode": [[30, "md-coveragecontenttypecode"]], "MD_DatatypeCode": [[30, "md-datatypecode"]], "MD_DimensionNameTypeCode": [[30, "md-dimensionnametypecode"]], "MD_GeometricObjectTypeCode": [[30, "md-geometricobjecttypecode"]], "MD_ImagingConditionCode": [[30, "md-imagingconditioncode"]], "MD_KeywordTypeCode": [[30, "md-keywordtypecode"]], "E2E Tests": [[31, "e2e-tests"]], "Testing in metacatalog": [[31, "testing-in-metacatalog"]], "Run": [[31, "run"]], "Running tests locally": [[31, "running-tests-locally"]], "Clean up": [[31, "clean-up"]], "Custom Extensions": [[32, "module-metacatalog.ext.base"]], "Export Extenstion": [[33, "module-metacatalog.ext.export"]], "Extensions": [[34, "extensions"]], "Read / Write Extension": [[35, "module-metacatalog.ext.io"]], "Standards Export Extension": [[36, "standards-export-extension"]], "Getting Started": [[37, "getting-started"]], "Home": [[38, "home"]], "How the docs work": [[38, "how-the-docs-work"]], "Installation": [[39, "installation"]], "Install metacatalog": [[39, "install-metacatalog"]], "Metadata Overview": [[40, "metadata-overview"]], "Available metdata entities": [[40, "available-metdata-entities"]], "Metadata descriptions": [[40, "id1"]], "Welcome to Metacatalog\u2019s documentation!": [[41, "welcome-to-metacatalog-s-documentation"]], "Contents:": [[41, null]], "Data Tables": [[42, "data-tables"]], "DataSource": [[43, "module-metacatalog.models.datasource"]], "Controlled Keywords": [[45, "module-metacatalog.models.keyword"]], "Data Licenses": [[46, "module-metacatalog.models.license"]], "Location Filter": [[47, "location-filter"]], "Database Models": [[48, "module-metacatalog.models"]], "Models:": [[48, null]], "Authors & Contributors": [[49, "module-metacatalog.models.person"]], "ResultSet": [[50, "module-metacatalog.util.results"]], "Reference": [[50, "reference"]], "Todo": [[50, "id1"]], "Variables": [[51, "module-metacatalog.models.variable"]]}, "indexentries": {"metacatalog.api": [[0, "module-metacatalog.api"]], "module": [[0, "module-metacatalog.api"], [32, "module-metacatalog.ext.base"], [33, "module-metacatalog.ext.export"], [34, "module-metacatalog.ext"], [35, "module-metacatalog.ext.io"], [36, "module-metacatalog.ext.standards_export"], [42, "module-metacatalog.models.generic_data"], [42, "module-metacatalog.models.geometry_data"], [42, "module-metacatalog.models.timeseries"], [43, "module-metacatalog.models.datasource"], [44, "module-metacatalog.models.entry"], [44, "module-metacatalog.models.entrygroup"], [45, "module-metacatalog.models.keyword"], [46, "module-metacatalog.models.license"], [47, "module-metacatalog.util.location"], [48, "module-metacatalog.models"], [49, "module-metacatalog.models.person"], [50, "module-metacatalog.util.results"], [51, "module-metacatalog.models.variable"]], "add_details_to_entries() (in module metacatalog.api)": [[1, "metacatalog.api.add_details_to_entries"]], "add_entry() (in module metacatalog.api)": [[1, "metacatalog.api.add_entry"]], "add_group() (in module metacatalog.api)": [[2, "metacatalog.api.add_group"]], "add_project() (in module metacatalog.api)": [[2, "metacatalog.api.add_project"]], "add_keyword() (in module metacatalog.api)": [[3, "metacatalog.api.add_keyword"]], "add_keywords_to_entries() (in module metacatalog.api)": [[3, "metacatalog.api.add_keywords_to_entries"]], "add_license() (in module metacatalog.api)": [[4, "metacatalog.api.add_license"]], "add_person() (in module metacatalog.api)": [[5, "metacatalog.api.add_person"]], "add_persons_to_entries() (in module metacatalog.api)": [[5, "metacatalog.api.add_persons_to_entries"]], "add_thesaurus() (in module metacatalog.api)": [[6, "metacatalog.api.add_thesaurus"]], "description (in module metacatalog.api)": [[6, "metacatalog.api.description"]], "name (in module metacatalog.api)": [[6, "metacatalog.api.name"]], "organisation (in module metacatalog.api)": [[6, "metacatalog.api.organisation"]], "session (in module metacatalog.api)": [[6, "metacatalog.api.session"]], "title (in module metacatalog.api)": [[6, "metacatalog.api.title"]], "url (in module metacatalog.api)": [[6, "metacatalog.api.url"]], "uuid (in module metacatalog.api)": [[6, "metacatalog.api.uuid"]], "add_unit() (in module metacatalog.api)": [[7, "metacatalog.api.add_unit"]], "add_variable() (in module metacatalog.api)": [[7, "metacatalog.api.add_variable"]], "connect_database() (in module metacatalog.api)": [[8, "metacatalog.api.connect_database"]], "create_tables() (in module metacatalog.api)": [[9, "metacatalog.api.create_tables"]], "find_entry() (in module metacatalog.api)": [[10, "metacatalog.api.find_entry"]], "find_group() (in module metacatalog.api)": [[10, "metacatalog.api.find_group"]], "find_group_type() (in module metacatalog.api)": [[10, "metacatalog.api.find_group_type"]], "find_keyword() (in module metacatalog.api)": [[11, "metacatalog.api.find_keyword"]], "find_license() (in module metacatalog.api)": [[12, "metacatalog.api.find_license"]], "find_person() (in module metacatalog.api)": [[13, "metacatalog.api.find_person"]], "find_role() (in module metacatalog.api)": [[13, "metacatalog.api.find_role"]], "find_thesaurus() (in module metacatalog.api)": [[14, "metacatalog.api.find_thesaurus"]], "find_unit() (in module metacatalog.api)": [[15, "metacatalog.api.find_unit"]], "find_variable() (in module metacatalog.api)": [[15, "metacatalog.api.find_variable"]], "get_logger() (in module metacatalog.api)": [[16, "metacatalog.api.get_logger"]], "get_uuid() (in module metacatalog.api)": [[17, "metacatalog.api.get_uuid"]], "populate_defaults() (in module metacatalog.api)": [[18, "metacatalog.api.populate_defaults"]], "show_attributes() (in module metacatalog.api)": [[19, "metacatalog.api.show_attributes"]], "show_records() (in module metacatalog.api)": [[20, "metacatalog.api.show_records"]], "metacatalogextensioninterface (class in metacatalog.ext.base)": [[32, "metacatalog.ext.base.MetacatalogExtensionInterface"]], "init_cli() (metacatalog.ext.base.metacatalogextensioninterface class method)": [[32, "metacatalog.ext.base.MetacatalogExtensionInterface.init_cli"]], "metacatalog.ext.base": [[32, "module-metacatalog.ext.base"]], "exportextension (class in metacatalog.ext.export)": [[33, "metacatalog.ext.export.ExportExtension"]], "fast_xml() (metacatalog.ext.export.exportextension class method)": [[33, "metacatalog.ext.export.ExportExtension.fast_xml"]], "flat_keys() (metacatalog.ext.export.exportextension class method)": [[33, "metacatalog.ext.export.ExportExtension.flat_keys"]], "get_data() (metacatalog.ext.export.exportextension class method)": [[33, "metacatalog.ext.export.ExportExtension.get_data"]], "json() (metacatalog.ext.export.exportextension class method)": [[33, "metacatalog.ext.export.ExportExtension.json"]], "metacatalog.ext.export": [[33, "module-metacatalog.ext.export"]], "netcdf() (metacatalog.ext.export.exportextension class method)": [[33, "metacatalog.ext.export.ExportExtension.netcdf"]], "pickle() (metacatalog.ext.export.exportextension class method)": [[33, "metacatalog.ext.export.ExportExtension.pickle"]], "to_dict() (metacatalog.ext.export.exportextension class method)": [[33, "metacatalog.ext.export.ExportExtension.to_dict"]], "metacatalog.ext": [[34, "module-metacatalog.ext"]], "ioextension (class in metacatalog.ext.io)": [[35, "metacatalog.ext.io.IOExtension"]], "append() (metacatalog.ext.io.ioextension method)": [[35, "metacatalog.ext.io.IOExtension.append"]], "delete() (metacatalog.ext.io.ioextension method)": [[35, "metacatalog.ext.io.IOExtension.delete"]], "import_() (metacatalog.ext.io.ioextension method)": [[35, "metacatalog.ext.io.IOExtension.import_"]], "init_extension() (metacatalog.ext.io.ioextension class method)": [[35, "metacatalog.ext.io.IOExtension.init_extension"]], "metacatalog.ext.io": [[35, "module-metacatalog.ext.io"]], "read() (metacatalog.ext.io.ioextension method)": [[35, "metacatalog.ext.io.IOExtension.read"]], "standardsexportextension (class in metacatalog.ext.standards_export)": [[36, "metacatalog.ext.standards_export.StandardsExportExtension"]], "cli_create_standards_xml() (metacatalog.ext.standards_export.standardsexportextension class method)": [[36, "metacatalog.ext.standards_export.StandardsExportExtension.cli_create_standards_xml"]], "create_standards_xml() (metacatalog.ext.standards_export.standardsexportextension method)": [[36, "metacatalog.ext.standards_export.StandardsExportExtension.create_standards_xml"]], "init_cli() (metacatalog.ext.standards_export.standardsexportextension class method)": [[36, "metacatalog.ext.standards_export.StandardsExportExtension.init_cli"]], "metacatalog.ext.standards_export": [[36, "module-metacatalog.ext.standards_export"]], "standards_export() (metacatalog.ext.standards_export.standardsexportextension class method)": [[36, "metacatalog.ext.standards_export.StandardsExportExtension.standards_export"]], "datapoint (class in metacatalog.models.generic_data)": [[42, "metacatalog.models.generic_data.DataPoint"]], "datapoint2d (class in metacatalog.models.generic_data)": [[42, "metacatalog.models.generic_data.DataPoint2D"]], "genericgeometrydata (class in metacatalog.models.geometry_data)": [[42, "metacatalog.models.geometry_data.GenericGeometryData"]], "geometrytimeseries (class in metacatalog.models.geometry_data)": [[42, "metacatalog.models.geometry_data.GeometryTimeseries"]], "timeseries (class in metacatalog.models.timeseries)": [[42, "metacatalog.models.timeseries.Timeseries"]], "metacatalog.models.generic_data": [[42, "module-metacatalog.models.generic_data"]], "metacatalog.models.geometry_data": [[42, "module-metacatalog.models.geometry_data"]], "metacatalog.models.timeseries": [[42, "module-metacatalog.models.timeseries"]], "datasource (class in metacatalog.models.datasource)": [[43, "metacatalog.models.datasource.DataSource"]], "datasourcetype (class in metacatalog.models.datasource)": [[43, "metacatalog.models.datasource.DataSourceType"]], "datatype (class in metacatalog.models.datasource)": [[43, "metacatalog.models.datasource.DataType"]], "spatialscale (class in metacatalog.models.datasource)": [[43, "metacatalog.models.datasource.SpatialScale"]], "temporalscale (class in metacatalog.models.datasource)": [[43, "metacatalog.models.datasource.TemporalScale"]], "args (metacatalog.models.datasource.datasource attribute)": [[43, "metacatalog.models.datasource.DataSource.args"]], "children_list() (metacatalog.models.datasource.datatype method)": [[43, "metacatalog.models.datasource.DataType.children_list"]], "create_scale() (metacatalog.models.datasource.datasource method)": [[43, "metacatalog.models.datasource.DataSource.create_scale"]], "data_names (metacatalog.models.datasource.datasource attribute)": [[43, "metacatalog.models.datasource.DataSource.data_names"]], "description (metacatalog.models.datasource.datasourcetype attribute)": [[43, "metacatalog.models.datasource.DataSourceType.description"]], "description (metacatalog.models.datasource.datatype attribute)": [[43, "metacatalog.models.datasource.DataType.description"]], "encoding (metacatalog.models.datasource.datasource attribute)": [[43, "metacatalog.models.datasource.DataSource.encoding"]], "extent (metacatalog.models.datasource.spatialscale attribute)": [[43, "metacatalog.models.datasource.SpatialScale.extent"]], "id (metacatalog.models.datasource.datasource attribute)": [[43, "metacatalog.models.datasource.DataSource.id"]], "id (metacatalog.models.datasource.datasourcetype attribute)": [[43, "metacatalog.models.datasource.DataSourceType.id"]], "id (metacatalog.models.datasource.datatype attribute)": [[43, "metacatalog.models.datasource.DataType.id"]], "id (metacatalog.models.datasource.spatialscale attribute)": [[43, "metacatalog.models.datasource.SpatialScale.id"]], "id (metacatalog.models.datasource.temporalscale attribute)": [[43, "metacatalog.models.datasource.TemporalScale.id"]], "load_args() (metacatalog.models.datasource.datasource method)": [[43, "metacatalog.models.datasource.DataSource.load_args"]], "metacatalog.models.datasource": [[43, "module-metacatalog.models.datasource"]], "name (metacatalog.models.datasource.datasourcetype attribute)": [[43, "metacatalog.models.datasource.DataSourceType.name"]], "name (metacatalog.models.datasource.datatype attribute)": [[43, "metacatalog.models.datasource.DataType.name"]], "observation_end (metacatalog.models.datasource.temporalscale attribute)": [[43, "metacatalog.models.datasource.TemporalScale.observation_end"]], "observation_start (metacatalog.models.datasource.temporalscale attribute)": [[43, "metacatalog.models.datasource.TemporalScale.observation_start"]], "parent_list() (metacatalog.models.datasource.datatype method)": [[43, "metacatalog.models.datasource.DataType.parent_list"]], "path (metacatalog.models.datasource.datasource attribute)": [[43, "metacatalog.models.datasource.DataSource.path"]], "resolution (metacatalog.models.datasource.spatialscale attribute)": [[43, "metacatalog.models.datasource.SpatialScale.resolution"]], "resolution (metacatalog.models.datasource.temporalscale attribute)": [[43, "metacatalog.models.datasource.TemporalScale.resolution"]], "save_args_from_dict() (metacatalog.models.datasource.datasource method)": [[43, "metacatalog.models.datasource.DataSource.save_args_from_dict"]], "support (metacatalog.models.datasource.spatialscale attribute)": [[43, "metacatalog.models.datasource.SpatialScale.support"]], "support (metacatalog.models.datasource.temporalscale attribute)": [[43, "metacatalog.models.datasource.TemporalScale.support"]], "title (metacatalog.models.datasource.datasourcetype attribute)": [[43, "metacatalog.models.datasource.DataSourceType.title"]], "title (metacatalog.models.datasource.datatype attribute)": [[43, "metacatalog.models.datasource.DataType.title"]], "to_dict() (metacatalog.models.datasource.datasource method)": [[43, "metacatalog.models.datasource.DataSource.to_dict"]], "to_dict() (metacatalog.models.datasource.datasourcetype method)": [[43, "metacatalog.models.datasource.DataSourceType.to_dict"]], "to_dict() (metacatalog.models.datasource.datatype method)": [[43, "metacatalog.models.datasource.DataType.to_dict"]], "to_dict() (metacatalog.models.datasource.spatialscale method)": [[43, "metacatalog.models.datasource.SpatialScale.to_dict"]], "to_dict() (metacatalog.models.datasource.temporalscale method)": [[43, "metacatalog.models.datasource.TemporalScale.to_dict"]], "type (metacatalog.models.datasource.datasource attribute)": [[43, "metacatalog.models.datasource.DataSource.type"]], "type_id (metacatalog.models.datasource.datasource attribute)": [[43, "metacatalog.models.datasource.DataSource.type_id"]], "entry (class in metacatalog.models.entry)": [[44, "metacatalog.models.entry.Entry"]], "entrygroup (class in metacatalog.models.entrygroup)": [[44, "metacatalog.models.entrygroup.EntryGroup"]], "entrygroupassociation (class in metacatalog.models.entrygroup)": [[44, "metacatalog.models.entrygroup.EntryGroupAssociation"]], "entrygrouptype (class in metacatalog.models.entrygroup)": [[44, "metacatalog.models.entrygroup.EntryGroupType"]], "abstract (metacatalog.models.entry.entry attribute)": [[44, "metacatalog.models.entry.Entry.abstract"]], "add_details() (metacatalog.models.entry.entry method)": [[44, "metacatalog.models.entry.Entry.add_details"]], "append_data() (metacatalog.models.entry.entry method)": [[44, "metacatalog.models.entry.Entry.append_data"]], "author (metacatalog.models.entry.entry attribute)": [[44, "metacatalog.models.entry.Entry.author"]], "authors (metacatalog.models.entry.entry attribute)": [[44, "metacatalog.models.entry.Entry.authors"]], "checksum (metacatalog.models.entry.entry property)": [[44, "metacatalog.models.entry.Entry.checksum"]], "checksum (metacatalog.models.entrygroup.entrygroup property)": [[44, "metacatalog.models.entrygroup.EntryGroup.checksum"]], "citation (metacatalog.models.entry.entry attribute)": [[44, "metacatalog.models.entry.Entry.citation"]], "comment (metacatalog.models.entry.entry attribute)": [[44, "metacatalog.models.entry.Entry.comment"]], "create_datasource() (metacatalog.models.entry.entry method)": [[44, "metacatalog.models.entry.Entry.create_datasource"]], "creation (metacatalog.models.entry.entry attribute)": [[44, "metacatalog.models.entry.Entry.creation"]], "delete_data() (metacatalog.models.entry.entry method)": [[44, "metacatalog.models.entry.Entry.delete_data"]], "description (metacatalog.models.entrygroup.entrygroup attribute)": [[44, "metacatalog.models.entrygroup.EntryGroup.description"]], "details_dict() (metacatalog.models.entry.entry method)": [[44, "metacatalog.models.entry.Entry.details_dict"]], "details_table() (metacatalog.models.entry.entry method)": [[44, "metacatalog.models.entry.Entry.details_table"]], "end (metacatalog.models.entry.entry attribute)": [[44, "metacatalog.models.entry.Entry.end"]], "export() (metacatalog.models.entry.entry method)": [[44, "metacatalog.models.entry.Entry.export"]], "export() (metacatalog.models.entrygroup.entrygroup method)": [[44, "metacatalog.models.entrygroup.EntryGroup.export"]], "external_id (metacatalog.models.entry.entry attribute)": [[44, "metacatalog.models.entry.Entry.external_id"]], "from_dict() (metacatalog.models.entry.entry class method)": [[44, "metacatalog.models.entry.Entry.from_dict"]], "get_data() (metacatalog.models.entry.entry method)": [[44, "metacatalog.models.entry.Entry.get_data"]], "id (metacatalog.models.entry.entry attribute)": [[44, "metacatalog.models.entry.Entry.id"]], "id (metacatalog.models.entrygroup.entrygroup attribute)": [[44, "metacatalog.models.entrygroup.EntryGroup.id"]], "import_data() (metacatalog.models.entry.entry method)": [[44, "metacatalog.models.entry.Entry.import_data"]], "io_interface (metacatalog.models.entry.entry attribute)": [[44, "metacatalog.models.entry.Entry.io_interface"]], "is_partial (metacatalog.models.entry.entry attribute)": [[44, "metacatalog.models.entry.Entry.is_partial"]], "keyword_list() (metacatalog.models.entry.entry method)": [[44, "metacatalog.models.entry.Entry.keyword_list"]], "keywords_dict() (metacatalog.models.entry.entry method)": [[44, "metacatalog.models.entry.Entry.keywords_dict"]], "lastupdate (metacatalog.models.entrygroup.entrygroup attribute)": [[44, "metacatalog.models.entrygroup.EntryGroup.lastUpdate"]], "latest_version_id (metacatalog.models.entry.entry attribute)": [[44, "metacatalog.models.entry.Entry.latest_version_id"]], "license (metacatalog.models.entry.entry attribute)": [[44, "metacatalog.models.entry.Entry.license"]], "license_id (metacatalog.models.entry.entry attribute)": [[44, "metacatalog.models.entry.Entry.license_id"]], "location (metacatalog.models.entry.entry attribute)": [[44, "metacatalog.models.entry.Entry.location"]], "make_composite() (metacatalog.models.entry.entry method)": [[44, "metacatalog.models.entry.Entry.make_composite"]], "metacatalog.models.entry": [[44, "module-metacatalog.models.entry"]], "metacatalog.models.entrygroup": [[44, "module-metacatalog.models.entrygroup"]], "neighbors() (metacatalog.models.entry.entry method)": [[44, "metacatalog.models.entry.Entry.neighbors"]], "plain_keywords_dict() (metacatalog.models.entry.entry method)": [[44, "metacatalog.models.entry.Entry.plain_keywords_dict"]], "plain_keywords_list() (metacatalog.models.entry.entry method)": [[44, "metacatalog.models.entry.Entry.plain_keywords_list"]], "publication (metacatalog.models.entrygroup.entrygroup attribute)": [[44, "metacatalog.models.entrygroup.EntryGroup.publication"]], "set_new_author() (metacatalog.models.entry.entry method)": [[44, "metacatalog.models.entry.Entry.set_new_author"]], "title (metacatalog.models.entry.entry attribute)": [[44, "metacatalog.models.entry.Entry.title"]], "title (metacatalog.models.entrygroup.entrygroup attribute)": [[44, "metacatalog.models.entrygroup.EntryGroup.title"]], "to_dict() (metacatalog.models.entry.entry method)": [[44, "metacatalog.models.entry.Entry.to_dict"]], "to_dict() (metacatalog.models.entrygroup.entrygroup method)": [[44, "metacatalog.models.entrygroup.EntryGroup.to_dict"]], "to_dict() (metacatalog.models.entrygroup.entrygrouptype method)": [[44, "metacatalog.models.entrygroup.EntryGroupType.to_dict"]], "type (metacatalog.models.entrygroup.entrygroup attribute)": [[44, "metacatalog.models.entrygroup.EntryGroup.type"]], "type_id (metacatalog.models.entrygroup.entrygroup attribute)": [[44, "metacatalog.models.entrygroup.EntryGroup.type_id"]], "uuid (metacatalog.models.entry.entry attribute)": [[44, "metacatalog.models.entry.Entry.uuid"]], "uuid (metacatalog.models.entrygroup.entrygroup attribute)": [[44, "metacatalog.models.entrygroup.EntryGroup.uuid"]], "version (metacatalog.models.entry.entry attribute)": [[44, "metacatalog.models.entry.Entry.version"]], "keyword (class in metacatalog.models.keyword)": [[45, "metacatalog.models.keyword.Keyword"]], "keywordassociation (class in metacatalog.models.keyword)": [[45, "metacatalog.models.keyword.KeywordAssociation"]], "thesaurus (class in metacatalog.models.keyword)": [[45, "metacatalog.models.keyword.Thesaurus"]], "description (metacatalog.models.keyword.thesaurus attribute)": [[45, "metacatalog.models.keyword.Thesaurus.description"]], "entry_id (metacatalog.models.keyword.keywordassociation attribute)": [[45, "metacatalog.models.keyword.KeywordAssociation.entry_id"]], "full_path (metacatalog.models.keyword.keyword attribute)": [[45, "metacatalog.models.keyword.Keyword.full_path"]], "id (metacatalog.models.keyword.keyword attribute)": [[45, "metacatalog.models.keyword.Keyword.id"]], "id (metacatalog.models.keyword.thesaurus attribute)": [[45, "metacatalog.models.keyword.Thesaurus.id"]], "keyword_id (metacatalog.models.keyword.keywordassociation attribute)": [[45, "metacatalog.models.keyword.KeywordAssociation.keyword_id"]], "metacatalog.models.keyword": [[45, "module-metacatalog.models.keyword"]], "name (metacatalog.models.keyword.thesaurus attribute)": [[45, "metacatalog.models.keyword.Thesaurus.name"]], "organisation (metacatalog.models.keyword.thesaurus attribute)": [[45, "metacatalog.models.keyword.Thesaurus.organisation"]], "parent_id (metacatalog.models.keyword.keyword attribute)": [[45, "metacatalog.models.keyword.Keyword.parent_id"]], "path() (metacatalog.models.keyword.keyword method)": [[45, "metacatalog.models.keyword.Keyword.path"]], "tagged_entries (metacatalog.models.keyword.keyword attribute)": [[45, "metacatalog.models.keyword.Keyword.tagged_entries"]], "thesaurusname (metacatalog.models.keyword.keyword attribute)": [[45, "metacatalog.models.keyword.Keyword.thesaurusName"]], "thesaurus_id (metacatalog.models.keyword.keyword attribute)": [[45, "metacatalog.models.keyword.Keyword.thesaurus_id"]], "title (metacatalog.models.keyword.thesaurus attribute)": [[45, "metacatalog.models.keyword.Thesaurus.title"]], "to_dict() (metacatalog.models.keyword.keyword method)": [[45, "metacatalog.models.keyword.Keyword.to_dict"]], "to_dict() (metacatalog.models.keyword.thesaurus method)": [[45, "metacatalog.models.keyword.Thesaurus.to_dict"]], "url (metacatalog.models.keyword.thesaurus attribute)": [[45, "metacatalog.models.keyword.Thesaurus.url"]], "uuid (metacatalog.models.keyword.keyword attribute)": [[45, "metacatalog.models.keyword.Keyword.uuid"]], "uuid (metacatalog.models.keyword.thesaurus attribute)": [[45, "metacatalog.models.keyword.Thesaurus.uuid"]], "value (metacatalog.models.keyword.keyword attribute)": [[45, "metacatalog.models.keyword.Keyword.value"]], "license (class in metacatalog.models.license)": [[46, "metacatalog.models.license.License"]], "by_attribution (metacatalog.models.license.license attribute)": [[46, "metacatalog.models.license.License.by_attribution"]], "commercial_use (metacatalog.models.license.license attribute)": [[46, "metacatalog.models.license.License.commercial_use"]], "full_text (metacatalog.models.license.license attribute)": [[46, "metacatalog.models.license.License.full_text"]], "get_full_text() (metacatalog.models.license.license method)": [[46, "metacatalog.models.license.License.get_full_text"]], "id (metacatalog.models.license.license attribute)": [[46, "metacatalog.models.license.License.id"]], "link (metacatalog.models.license.license attribute)": [[46, "metacatalog.models.license.License.link"]], "metacatalog.models.license": [[46, "module-metacatalog.models.license"]], "share_alike (metacatalog.models.license.license attribute)": [[46, "metacatalog.models.license.License.share_alike"]], "short_title (metacatalog.models.license.license attribute)": [[46, "metacatalog.models.license.License.short_title"]], "summary (metacatalog.models.license.license attribute)": [[46, "metacatalog.models.license.License.summary"]], "title (metacatalog.models.license.license attribute)": [[46, "metacatalog.models.license.License.title"]], "to_dict() (metacatalog.models.license.license method)": [[46, "metacatalog.models.license.License.to_dict"]], "around() (in module metacatalog.util.location)": [[47, "metacatalog.util.location.around"]], "build_query() (in module metacatalog.util.location)": [[47, "metacatalog.util.location.build_query"]], "get_search_shape() (in module metacatalog.util.location)": [[47, "metacatalog.util.location.get_search_shape"]], "metacatalog.util.location": [[47, "module-metacatalog.util.location"]], "metacatalog.models": [[48, "module-metacatalog.models"]], "person (class in metacatalog.models.person)": [[49, "metacatalog.models.person.Person"]], "personassociation (class in metacatalog.models.person)": [[49, "metacatalog.models.person.PersonAssociation"]], "personrole (class in metacatalog.models.person)": [[49, "metacatalog.models.person.PersonRole"]], "affiliation (metacatalog.models.person.person attribute)": [[49, "metacatalog.models.person.Person.affiliation"]], "attribution (metacatalog.models.person.person attribute)": [[49, "metacatalog.models.person.Person.attribution"]], "entries (metacatalog.models.person.person attribute)": [[49, "metacatalog.models.person.Person.entries"]], "first_name (metacatalog.models.person.person attribute)": [[49, "metacatalog.models.person.Person.first_name"]], "from_dict() (metacatalog.models.person.person class method)": [[49, "metacatalog.models.person.Person.from_dict"]], "id (metacatalog.models.person.person attribute)": [[49, "metacatalog.models.person.Person.id"]], "last_name (metacatalog.models.person.person attribute)": [[49, "metacatalog.models.person.Person.last_name"]], "metacatalog.models.person": [[49, "module-metacatalog.models.person"]], "organisation_name (metacatalog.models.person.person attribute)": [[49, "metacatalog.models.person.Person.organisation_name"]], "to_dict() (metacatalog.models.person.person method)": [[49, "metacatalog.models.person.Person.to_dict"]], "to_dict() (metacatalog.models.person.personassociation method)": [[49, "metacatalog.models.person.PersonAssociation.to_dict"]], "to_dict() (metacatalog.models.person.personrole method)": [[49, "metacatalog.models.person.PersonRole.to_dict"]], "uuid (metacatalog.models.person.person attribute)": [[49, "metacatalog.models.person.Person.uuid"]], "immutableresultset (class in metacatalog.util.results)": [[50, "metacatalog.util.results.ImmutableResultSet"]], "checksum (metacatalog.util.results.immutableresultset property)": [[50, "metacatalog.util.results.ImmutableResultSet.checksum"]], "checksums (metacatalog.util.results.immutableresultset property)": [[50, "metacatalog.util.results.ImmutableResultSet.checksums"]], "contains_uuid() (metacatalog.util.results.immutableresultset method)": [[50, "metacatalog.util.results.ImmutableResultSet.contains_uuid"]], "empty (metacatalog.util.results.immutableresultset property)": [[50, "metacatalog.util.results.ImmutableResultSet.empty"]], "entry_set() (metacatalog.util.results.immutableresultset class method)": [[50, "metacatalog.util.results.ImmutableResultSet.entry_set"]], "expand_entry() (metacatalog.util.results.immutableresultset class method)": [[50, "metacatalog.util.results.ImmutableResultSet.expand_entry"]], "get() (metacatalog.util.results.immutableresultset method)": [[50, "metacatalog.util.results.ImmutableResultSet.get"]], "get_data() (metacatalog.util.results.immutableresultset method)": [[50, "metacatalog.util.results.ImmutableResultSet.get_data"]], "load_base_group() (metacatalog.util.results.immutableresultset class method)": [[50, "metacatalog.util.results.ImmutableResultSet.load_base_group"]], "metacatalog.util.results": [[50, "module-metacatalog.util.results"]], "to_dict() (metacatalog.util.results.immutableresultset method)": [[50, "metacatalog.util.results.ImmutableResultSet.to_dict"]], "to_short_info() (metacatalog.util.results.immutableresultset method)": [[50, "metacatalog.util.results.ImmutableResultSet.to_short_info"]], "uuid (metacatalog.util.results.immutableresultset property)": [[50, "metacatalog.util.results.ImmutableResultSet.uuid"]], "uuids (metacatalog.util.results.immutableresultset property)": [[50, "metacatalog.util.results.ImmutableResultSet.uuids"]], "unit (class in metacatalog.models.variable)": [[51, "metacatalog.models.variable.Unit"]], "variable (class in metacatalog.models.variable)": [[51, "metacatalog.models.variable.Variable"]], "column_names (metacatalog.models.variable.variable attribute)": [[51, "metacatalog.models.variable.Variable.column_names"]], "from_dict() (metacatalog.models.variable.variable class method)": [[51, "metacatalog.models.variable.Variable.from_dict"]], "id (metacatalog.models.variable.unit attribute)": [[51, "metacatalog.models.variable.Unit.id"]], "id (metacatalog.models.variable.variable attribute)": [[51, "metacatalog.models.variable.Variable.id"]], "metacatalog.models.variable": [[51, "module-metacatalog.models.variable"]], "name (metacatalog.models.variable.unit attribute)": [[51, "metacatalog.models.variable.Unit.name"]], "name (metacatalog.models.variable.variable attribute)": [[51, "metacatalog.models.variable.Variable.name"]], "si (metacatalog.models.variable.unit attribute)": [[51, "metacatalog.models.variable.Unit.si"]], "si (metacatalog.models.variable.variable attribute)": [[51, "metacatalog.models.variable.Variable.si"]], "symbol (metacatalog.models.variable.unit attribute)": [[51, "metacatalog.models.variable.Unit.symbol"]], "symbol (metacatalog.models.variable.variable attribute)": [[51, "metacatalog.models.variable.Variable.symbol"]], "to_dict() (metacatalog.models.variable.unit method)": [[51, "metacatalog.models.variable.Unit.to_dict"]], "to_dict() (metacatalog.models.variable.variable method)": [[51, "metacatalog.models.variable.Variable.to_dict"]], "variables (metacatalog.models.variable.unit attribute)": [[51, "metacatalog.models.variable.Unit.variables"]], "variables (metacatalog.models.variable.variable attribute)": [[51, "metacatalog.models.variable.Variable.variables"]]}}) \ No newline at end of file