├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── citrination_client ├── __init__.py ├── base │ ├── __init__.py │ ├── base_client.py │ ├── errors.py │ ├── response_handling.py │ └── tests │ │ ├── test_base_client.py │ │ └── test_response_handling.py ├── client.py ├── data │ ├── __init__.py │ ├── client.py │ ├── dataset.py │ ├── dataset_file.py │ ├── dataset_version.py │ ├── ingest │ │ ├── __init__.py │ │ ├── client.py │ │ ├── ingester.py │ │ └── ingester_list.py │ ├── routes.py │ ├── tests │ │ ├── test_data_client.py │ │ ├── test_dataset.py │ │ ├── test_dataset_file.py │ │ ├── test_dataset_version.py │ │ ├── test_files │ │ │ ├── data │ │ │ │ ├── data_holder │ │ │ │ │ ├── 1 │ │ │ │ │ ├── 2 │ │ │ │ │ └── 3 │ │ │ │ ├── keys_and_values.json │ │ │ │ ├── level2 │ │ │ │ │ ├── level2.json │ │ │ │ │ └── level3 │ │ │ │ │ │ └── level3.json │ │ │ │ ├── orienteering │ │ │ │ │ └── flags.txt │ │ │ │ ├── revolver │ │ │ │ │ ├── A.csv │ │ │ │ │ ├── B.json │ │ │ │ │ └── commas.csv │ │ │ │ └── weird_extensions.woodle │ │ │ └── empty │ │ └── test_upload_result.py │ └── upload_result.py ├── models │ ├── __init__.py │ ├── client.py │ ├── columns │ │ ├── __init__.py │ │ ├── alloy_composition.py │ │ ├── base.py │ │ ├── categorical.py │ │ ├── column_factory.py │ │ ├── descriptor_converter.py │ │ ├── formulation.py │ │ ├── inorganic_chemical_formula.py │ │ ├── integer.py │ │ ├── organic_chemical_formula.py │ │ ├── real.py │ │ ├── tests │ │ │ ├── test_alloy_composition.py │ │ │ ├── test_base.py │ │ │ ├── test_categorical.py │ │ │ ├── test_descriptor_converter.py │ │ │ ├── test_int.py │ │ │ ├── test_real.py │ │ │ └── test_vector.py │ │ └── vector.py │ ├── data_view_status.py │ ├── design │ │ ├── __init__.py │ │ ├── constraints │ │ │ ├── __init__.py │ │ │ ├── base.py │ │ │ ├── categorical.py │ │ │ ├── elemental_composition.py │ │ │ ├── elemental_inclusion.py │ │ │ ├── int_range.py │ │ │ ├── int_value.py │ │ │ ├── real_range.py │ │ │ ├── real_value.py │ │ │ └── tests │ │ │ │ └── test_constraints.py │ │ ├── design_results.py │ │ ├── design_run.py │ │ ├── process_status.py │ │ ├── target.py │ │ └── tests │ │ │ └── test_target.py │ ├── event.py │ ├── predicted_value.py │ ├── prediction_result.py │ ├── projection.py │ ├── routes.py │ ├── service_status.py │ ├── tests │ │ ├── test_event.py │ │ ├── test_models_client.py │ │ ├── test_predicted_value.py │ │ ├── test_prediction_result.py │ │ └── test_service_status.py │ └── tsne.py ├── search │ ├── __init__.py │ ├── client.py │ ├── core │ │ ├── __init__.py │ │ ├── query │ │ │ ├── __init__.py │ │ │ ├── base_returning_query.py │ │ │ ├── boolean_filter.py │ │ │ ├── data_query.py │ │ │ ├── data_scope.py │ │ │ ├── filter.py │ │ │ └── multi_query.py │ │ └── result │ │ │ ├── __init__.py │ │ │ └── base_search_result.py │ ├── dataset │ │ ├── __init__.py │ │ ├── query │ │ │ ├── __init__.py │ │ │ ├── dataset_query.py │ │ │ └── dataset_returning_query.py │ │ ├── result │ │ │ ├── __init__.py │ │ │ ├── dataset_multi_search_result.py │ │ │ ├── dataset_multi_search_result_element.py │ │ │ ├── dataset_search_hit.py │ │ │ └── dataset_search_result.py │ │ └── tests │ │ │ └── test_dataset_query.py │ ├── file │ │ ├── __init__.py │ │ ├── query │ │ │ ├── __init__.py │ │ │ ├── file_query.py │ │ │ └── file_returning_query.py │ │ └── result │ │ │ ├── __init__.py │ │ │ ├── file_multi_search_result.py │ │ │ ├── file_multi_search_result_element.py │ │ │ ├── file_search_hit.py │ │ │ └── file_search_result.py │ ├── pif │ │ ├── __init__.py │ │ ├── query │ │ │ ├── __init__.py │ │ │ ├── chemical │ │ │ │ ├── __init__.py │ │ │ │ ├── chemical_field_query.py │ │ │ │ ├── chemical_filter.py │ │ │ │ └── composition_query.py │ │ │ ├── core │ │ │ │ ├── __init__.py │ │ │ │ ├── base_field_query.py │ │ │ │ ├── base_object_query.py │ │ │ │ ├── classification_query.py │ │ │ │ ├── display_item_query.py │ │ │ │ ├── field_query.py │ │ │ │ ├── file_reference_query.py │ │ │ │ ├── id_query.py │ │ │ │ ├── name_query.py │ │ │ │ ├── pages_query.py │ │ │ │ ├── process_step_query.py │ │ │ │ ├── property_query.py │ │ │ │ ├── quantity_query.py │ │ │ │ ├── reference_query.py │ │ │ │ ├── source_query.py │ │ │ │ └── value_query.py │ │ │ ├── extraction_sort.py │ │ │ ├── pif_system_query.py │ │ │ └── pif_system_returning_query.py │ │ ├── result │ │ │ ├── __init__.py │ │ │ ├── pif_multi_search_result.py │ │ │ ├── pif_multi_search_result_element.py │ │ │ ├── pif_search_hit.py │ │ │ └── pif_search_result.py │ │ └── tests │ │ │ └── test_pif_query.py │ ├── query_encoder.py │ └── routes.py ├── util │ ├── __init__.py │ ├── config.py │ ├── credentials.py │ ├── env.py │ ├── maths.py │ ├── quote_finder.py │ └── tests │ │ ├── mock_credentials │ │ ├── test_credentials.py │ │ └── test_maths.py └── views │ ├── __init__.py │ ├── advanced_data_view_builder.py │ ├── base_data_view_builder.py │ ├── client.py │ ├── data_view_builder.py │ ├── descriptors │ ├── __init__.py │ ├── alloy_composition_descriptor.py │ ├── categorical_descriptor.py │ ├── descriptor.py │ ├── formulation_descriptor.py │ ├── inorganic_descriptor.py │ ├── int_descriptor.py │ ├── organic_descriptor.py │ ├── real_descriptor.py │ └── tests │ │ └── test_descriptors.py │ ├── model_report.py │ ├── model_template │ ├── __init__.py │ └── client.py │ ├── search_template │ ├── __init__.py │ └── client.py │ └── tests │ ├── available_columns.json │ ├── column_descriptors.json │ ├── ml_template.json │ ├── test_advanced_data_view_builder.py │ ├── test_data_view_builder.py │ ├── test_model_template_builder.py │ └── test_search_template.json ├── docs ├── Makefile └── source │ ├── code_samples │ ├── data │ │ ├── custom_ingest_with_arguments.py │ │ ├── custom_ingest_without_arguments.py │ │ ├── file_urls.py │ │ ├── find_ingester_by_id.py │ │ ├── find_ingester_using_where.py │ │ ├── get_ingest_status.py │ │ ├── get_pif.py │ │ ├── get_pif_with_metadata.py │ │ ├── ingester_arguments.py │ │ ├── instantiation.py │ │ ├── list_files.py │ │ ├── permissions.py │ │ ├── upload_dir_no_dest.py │ │ ├── upload_dir_with_dest.py │ │ ├── upload_file_with_dest.py │ │ ├── upload_no_dest.py │ │ ├── upload_with_template_csv_ingester.py │ │ └── version.py │ ├── general │ │ ├── client_serialization.py │ │ └── initialization.py │ ├── migrating │ │ ├── activate_venv │ │ ├── deactivate │ │ └── pip_install │ ├── models │ │ ├── design.py │ │ ├── predict.py │ │ └── tsne.py │ ├── search │ │ ├── dataset_search.py │ │ ├── generate_simple_query.py │ │ └── pif_search.py │ └── views │ │ ├── advanced_data_view_builder_1.py │ │ ├── advanced_data_view_builder_2.py │ │ ├── advanced_data_view_builder_3.py │ │ ├── advanced_data_view_builder_4.py │ │ ├── advanced_data_view_builder_relation_graph.png │ │ ├── advanced_data_view_builder_relations.py │ │ ├── data_view_builder.py │ │ ├── data_view_builder_formulation_desc.py │ │ ├── data_view_builder_relation_graph.png │ │ ├── data_view_builder_relations.py │ │ ├── descriptor_keys.py │ │ ├── fetching_ml_config_defaults_1.py │ │ ├── fetching_ml_config_defaults_2.py │ │ ├── fetching_ml_config_defaults_3.py │ │ ├── instantiation.py │ │ ├── model_reports.py │ │ ├── relation_graph.png │ │ └── relation_graphs.py │ ├── conf.py │ ├── index.rst │ ├── modules │ ├── citrination_client.rst │ ├── data │ │ ├── data_client.rst │ │ ├── datasets.rst │ │ └── ingest.rst │ ├── data_management.rst │ ├── models.rst │ ├── models │ │ ├── design.rst │ │ ├── models_client.rst │ │ ├── predictions.rst │ │ └── tsne.rst │ ├── modules.rst │ ├── search.rst │ ├── search │ │ ├── citrination_client.search.rst │ │ ├── core_query.rst │ │ ├── core_result.rst │ │ ├── dataset_query.rst │ │ ├── dataset_result.rst │ │ ├── file_query.rst │ │ ├── file_result.rst │ │ ├── pif_chemical_query.rst │ │ ├── pif_query.rst │ │ ├── pif_query_core.rst │ │ └── pif_result.rst │ ├── views.rst │ └── views │ │ ├── data_views_client.rst │ │ ├── ml_config_builder.rst │ │ └── model_report.rst │ └── tutorial │ ├── data_examples.rst │ ├── initialization.rst │ ├── introduction.rst │ ├── models_examples.rst │ ├── search_examples.rst │ ├── tutorial.rst │ └── view_examples.rst ├── requirements.txt ├── setup.cfg └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.swp 3 | .idea 4 | build 5 | dist 6 | *.egg* 7 | docs/build 8 | docs/tmpsrc 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | env: 3 | global: 4 | - CITRINATION_SITE=https://citrination.com 5 | - secure: qcH/fSQwZf+GDM0HVm8j8kKTgYljNxkFogyI56s1kv8sB9FFpg8GVncf+ot8IT743YFr4TP/csofwFApo/yBu4fcIzy83zXXaJ0SbRDAEObLiHrT4WLhw7DjzmXIt0EIw1q6VPrMcrDaP6KbEWBZC+Yt72Hv4g4EO0l6BdLhRyeHamlAosFjCeI7H2+B3FX8UnJEvsBQSI3Qc2u//sYFq9lYmDFKsO5Wze25CGULWlOSHwbO9g5tzGDQOmJQHCk3qJZxANM+CD/xeGJz0G26tXB0JXcvZLPskoWjtN3GNYPgbUlwV/oquYwPuB3wkpZHmrQ2Fb1wJ6Fl52JPNeeV6FXfNW2PtmlLHuymrszfw1pavnpkKCPPZUFGZbmb2yjSGVLIym+wWsxzAuOLiWU7R57lCSVmN2nyMvr4FqM/ljiT3sJo26/FVBeui8pMmTxYa/8Hm9kun2jXwBLxiP14FSo4rc/jM6I5OeXwNq3rT005ZVrRCSVUSvXAOHhovYpuE2kibsHW8Vn1gmxB1I3RWFCKr658tgleUgi6R+WvtDH+rZ3B77Nop4t5ojVK8jplIgBBs85pF/TBoz+r0lWLKyWMZpaBLfHHpoILraWTar9HCzR8T3n/izRVMYuw45DkrtgMe1o1cjpPJNBCncVpx65elgwRcYRk16xwL6kLp/w= 6 | python: 7 | - '2.7' 8 | - '3.4' 9 | - '3.5' 10 | - '3.6' 11 | install: 12 | - pip install pytest 13 | - pip install requests_mock 14 | - python setup.py install 15 | script: 16 | - py.test citrination_client --verbose 17 | deploy: 18 | provider: pypi 19 | user: CitrineInformatics 20 | password: 21 | secure: YER1lIwYpEBLCtLssBF+CW/lCfAblkBBDtHC7ESCNr6sQwEKHTbF7ihvxyld55Y9DmnTXssFYLShFOvC5CZ7A+b7LZMmFdGc5f2n0X+6FA+paOJ5+6BXzFaP3X84XQsvy7wluz0f58roQq+o//8I+d5WPrrKczmNJUyAq7V74AhWdgpITgnBlomyNUS01HxX9qMDw1eVKOQv2Qccs+xgwkWPtlQw542oMHFb0SwRPSWm0GMY2ykyT4CehOLZqkye0EKs5K75BFtlTtXUjT+bPEfHoYk4kJmcb4wiSisXuLCt4eJcSYiBB7bOTZLC/DmNLMeakknIBQ14cRACcJ+0gcLX0sNPt/BZrcdJhUeYtRc/CLYHYv8m42KhtLicU/K6907HlDf1/KmJhhn2B/sOf7XKyP3ztxJnR/ZXEbSsgtWSY4+W6KKW3kWcyyabfCDp3lZYlKG3I+13MYDooaL4p/h8knjBx6dgOCS9Jbr99IvAA3u/aRFCayseCqEtIFjJF4WbR7rySbPHwL5WremmlDi+SZpe143ALZyqNTVP89k14sztARntkbCqrY9QTw5KYSFxPMr3RETz+3q4PI9i4fYOO6XCU0wWJnL3cYxLRh7bj0mRk76lsGD9o3ZLWyQud7zWtsz+TPHUAUvuHYYsFHUgID768UngKs/59JcELFA= 22 | on: 23 | python: '3.6' 24 | distributions: sdist bdist_wheel 25 | repo: CitrineInformatics/python-citrination-client 26 | branch: master 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Python Citrination Client 2 | [![Build Status](https://travis-ci.org/CitrineInformatics/python-citrination-client.svg?branch=master)](https://travis-ci.org/CitrineInformatics/python-citrination-client) 3 | 4 | ## About 5 | 6 | This package is an implementation of the Citrination API. 7 | 8 | You can learn about the functionalities of the python-citrination-client through the following [documentation](http://citrineinformatics.github.io/python-citrination-client/). 9 | 10 | Tutorials and examples presented using Jupyter notebooks can be found in [community-tools](https://github.com/CitrineInformatics/community-tools) and [learn-citrination](https://github.com/CitrineInformatics/learn-citrination). 11 | 12 | ## Installation 13 | 14 | ### Requirements 15 | * Python >= 2.7.10 or >= 3.4 16 | 17 | ### Setup 18 | 19 | ```shell 20 | $ pip install citrination-client 21 | ``` 22 | 23 | There are known issues installing libraries that depend on six on OSX. If you 24 | have issues installing this library, run the following command: 25 | 26 | ``` 27 | $ pip install --ignore-installed six citrination-client 28 | ``` 29 | 30 | ### Legacy information 31 | 32 | In order to interact with older versions of Citrination, please clone 33 | the legacy branch on this repo. 34 | 35 | 36 | ### Troubleshooting 37 | 38 | It is possible that you will run into problems if you are using an older 39 | version of OpenSSL and running MacOSX. If the following error happens when 40 | using the requests library to retrieve information from citrination: 41 | 42 | ``` 43 | requests.exceptions.SSLError: ("bad handshake: Error([('SSL routines', 'SSL23_GET_SERVER_HELLO', 'sslv3 alert handshake failure')],)",) 44 | ``` 45 | 46 | Check to see that you are using a current version of OpenSSL. This error was 47 | first encountered using `OpenSSL 0.9.8zh 14 Jan 2016`, and was resolved by 48 | upgrading to `OpenSSL 1.0.2j 26 Sep 2016`. 49 | 50 | After upgrading OpenSSL, make sure that your python installation is using the 51 | correct version: 52 | 53 | ``` 54 | python -c "import ssl; print ssl.OPENSSL_VERSION" 55 | ``` 56 | 57 | Some useful guides can be found [here](http://stackoverflow.com/questions/24323858/python-referencing-old-ssl-version) and [here](https://comeroutewithme.com/2016/03/13/python-osx-openssl-issue/). 58 | -------------------------------------------------------------------------------- /citrination_client/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | 4 | from citrination_client.base import * 5 | from citrination_client.search import * 6 | from citrination_client.data import * 7 | from citrination_client.models import * 8 | from citrination_client.views.descriptors import * 9 | from .client import CitrinationClient 10 | from pkg_resources import get_distribution, DistributionNotFound 11 | 12 | def __get_version(): 13 | """ 14 | Returns the version of this package, whether running from source or install 15 | 16 | :return: The version of this package 17 | """ 18 | try: 19 | # Try local first, if missing setup.py, then use pkg info 20 | here = os.path.abspath(os.path.dirname(__file__)) 21 | with open(os.path.join(here, "../setup.py")) as fp: 22 | version_file = fp.read() 23 | version_match = re.search(r"version=['\"]([^'\"]*)['\"]", 24 | version_file, re.M) 25 | if version_match: 26 | return version_match.group(1) 27 | except IOError: 28 | pass 29 | 30 | try: 31 | _dist = get_distribution('citrination_client') 32 | # Normalize case for Windows systems 33 | # Using realpath in case directories are symbolic links 34 | dist_loc = os.path.realpath(os.path.normcase(_dist.location)) 35 | here = os.path.realpath(os.path.normcase(__file__)) 36 | if not here.startswith(os.path.join(dist_loc, 'citrination_client')): 37 | # not installed, but there is another version that *is* 38 | raise DistributionNotFound 39 | except DistributionNotFound: 40 | raise RuntimeError("Unable to find version string.") 41 | else: 42 | return _dist.version 43 | 44 | __version__ = __get_version() 45 | -------------------------------------------------------------------------------- /citrination_client/base/__init__.py: -------------------------------------------------------------------------------- 1 | from .base_client import BaseClient 2 | from .errors import * -------------------------------------------------------------------------------- /citrination_client/base/errors.py: -------------------------------------------------------------------------------- 1 | class CitrinationClientError(Exception): 2 | 3 | def __init__(self, message=None, server_response=None): 4 | if message is not None and server_response is not None: 5 | message = "{}\nCitrination returned: {}".format(message, server_response) 6 | super(CitrinationClientError, self).__init__(message) 7 | 8 | class APIVersionMismatchException(CitrinationClientError): 9 | 10 | def __init__(self, message="Version mismatch with Citrination identified, server_response=None. Please check for available PyCC updates", server_response=None): 11 | super(APIVersionMismatchException, self).__init__(message) 12 | 13 | class FeatureUnavailableException(CitrinationClientError): 14 | 15 | def __init__(self, message="This feature is unavailable on your Citrination deployment", server_response=None): 16 | super(FeatureUnavailableException, self).__init__(message) 17 | 18 | class UnauthorizedAccessException(CitrinationClientError): 19 | 20 | def __init__(self, message="Access to an unauthorized resource requested", server_response=None): 21 | super(UnauthorizedAccessException, self).__init__(message) 22 | 23 | class ResourceNotFoundException(CitrinationClientError): 24 | 25 | def __init__(self, message="Resource not found", server_response=None): 26 | super(ResourceNotFoundException, self).__init__(message) 27 | 28 | class CitrinationServerErrorException(CitrinationClientError): 29 | 30 | def __init__(self, message=None, server_response=None): 31 | super(CitrinationServerErrorException, self).__init__(message) 32 | 33 | class RequestTimeoutException(CitrinationClientError): 34 | 35 | def __init__(self, message="Request to Citrination host timed out", server_response=None): 36 | super(RequestTimeoutException, self).__init__(message) 37 | 38 | class RateLimitingException(CitrinationClientError): 39 | 40 | def __init__(self, message="Rate limit hit, throttle requests", server_response=None): 41 | super(RateLimitingException, self).__init__(message) 42 | 43 | class RequestPayloadTooLarge(CitrinationClientError): 44 | 45 | def __init__(self, message="Request payload too large", server_response=None): 46 | super(RequestPayloadTooLarge, self).__init__(message) 47 | 48 | class InvalidOptionError(CitrinationClientError): 49 | def __init__(self, message): 50 | super(InvalidOptionError, self).__init__(message) 51 | -------------------------------------------------------------------------------- /citrination_client/base/tests/test_base_client.py: -------------------------------------------------------------------------------- 1 | from citrination_client.base import BaseClient 2 | from citrination_client.base.errors import CitrinationClientError 3 | from citrination_client import __version__ 4 | 5 | def test_none_api_key(): 6 | """ 7 | Ensures that an error is thrown if a client is instantiated 8 | without an API key 9 | """ 10 | try: 11 | client = BaseClient(None, "mycitrinationsite") 12 | assert False 13 | except CitrinationClientError: 14 | assert True 15 | 16 | def test_zero_length_api_key(): 17 | """ 18 | Tests that a zero length API key will cause the client to throw 19 | an error on instantiation 20 | """ 21 | try: 22 | client = BaseClient("", "mycitrinationsite") 23 | assert False 24 | except CitrinationClientError: 25 | assert True 26 | 27 | def test_version(): 28 | """ 29 | Tests that the version is extracted 30 | """ 31 | ver = __version__ 32 | print("Version:" + ver) 33 | assert ver[0].isdigit() 34 | 35 | -------------------------------------------------------------------------------- /citrination_client/client.py: -------------------------------------------------------------------------------- 1 | from citrination_client.views.client import DataViewsClient 2 | 3 | from citrination_client.models import ModelsClient 4 | from citrination_client.search import SearchClient 5 | from citrination_client.data import DataClient 6 | from citrination_client.util.credentials import get_preferred_credentials 7 | 8 | class CitrinationClient(object): 9 | """ 10 | The top level of the client hierarchy. Instantiating this class handles 11 | authentication information (api_key and site) and provides access to instances 12 | of each of the sub-clients, for more specific actions. 13 | 14 | Instantiation requires authentication information, but that can be provided 15 | via direct parameterization, environment variables, or a .citrination credentials 16 | file. See the tutorial on client Initialization for more information. 17 | """ 18 | 19 | def __init__(self, api_key=None, site=None, suppress_warnings=False, proxies=None): 20 | """ 21 | Constructor. 22 | 23 | :param api_key: Your API key for Citrination 24 | :type api_key: str 25 | :param site: The domain name of your Citrination deployment 26 | (the default is https://citrination.com) 27 | :type site: str 28 | :param suppress_warnings: A flag allowing you to suppress warning 29 | statements guarding against misuse printed to stdout. 30 | :type suppress_warnings: bool 31 | :param proxies: proxies to use when making HTTP requests. E.g., 32 | proxies = { 'http': 'http://10.10.1.10:3128', 'https': 'http://10.10.1.10:1080' } 33 | :type proxies: dict(string, string) 34 | """ 35 | api_key, site = get_preferred_credentials(api_key, site) 36 | self.models = ModelsClient(api_key, site, suppress_warnings=suppress_warnings, proxies=proxies) 37 | self.search = SearchClient(api_key, site, suppress_warnings=suppress_warnings, proxies=proxies) 38 | self.data = DataClient(api_key, site, suppress_warnings=suppress_warnings, proxies=proxies) 39 | self.data_views = DataViewsClient(api_key, site, suppress_warnings=suppress_warnings, proxies=proxies) 40 | 41 | def __repr__(self): 42 | return "['models', 'search', 'data', 'data_views']" 43 | -------------------------------------------------------------------------------- /citrination_client/data/__init__.py: -------------------------------------------------------------------------------- 1 | from citrination_client.data.dataset import Dataset 2 | from citrination_client.data.dataset_file import DatasetFile 3 | from citrination_client.data.upload_result import UploadResult 4 | from citrination_client.data.dataset_version import DatasetVersion 5 | from citrination_client.data.client import DataClient -------------------------------------------------------------------------------- /citrination_client/data/dataset.py: -------------------------------------------------------------------------------- 1 | class Dataset(object): 2 | """ 3 | Class representation of a dataset on Citrination. 4 | """ 5 | 6 | def __init__(self, id, name=None, description=None, 7 | created_at=None): 8 | """ 9 | Constructor. 10 | 11 | :param id: The ID of the dataset (required for instantiation) 12 | :type id: int 13 | :param name: The name of the dataset 14 | :type name: str 15 | :param description: The description of the dataset 16 | :type description: str 17 | :param created_at: The timestamp for creation of the dataset 18 | :type created_at: str 19 | """ 20 | self._name = name 21 | self._description = description 22 | self._id = id 23 | self._created_at = created_at 24 | 25 | @property 26 | def id(self): 27 | return self._id 28 | 29 | @property 30 | def name(self): 31 | return self._name 32 | 33 | @name.setter 34 | def name(self, value): 35 | self._name = value 36 | 37 | @name.deleter 38 | def name(self): 39 | self._name = None 40 | 41 | @property 42 | def description(self): 43 | return self._description 44 | 45 | @description.setter 46 | def description(self, value): 47 | self._description = value 48 | 49 | @description.deleter 50 | def description(self): 51 | self._description = None 52 | 53 | @property 54 | def created_at(self): 55 | return self._created_at 56 | 57 | @created_at.setter 58 | def created_at(self, value): 59 | self._created_at = value 60 | 61 | @created_at.deleter 62 | def created_at(self): 63 | self._created_at = None 64 | -------------------------------------------------------------------------------- /citrination_client/data/dataset_file.py: -------------------------------------------------------------------------------- 1 | class DatasetFile(object): 2 | """ 3 | Class representation of a file in a dataset on Citrination. 4 | """ 5 | 6 | def __init__(self, path, url=None): 7 | """ 8 | Constructor. 9 | 10 | :param path: The files path 11 | :type path: str 12 | :param url: If present, a download URL for the file 13 | :type url: str 14 | """ 15 | self._path = path 16 | self._url = url 17 | 18 | @property 19 | def path(self): 20 | return self._path 21 | 22 | @path.setter 23 | def path(self, value): 24 | self._path = value 25 | 26 | @path.deleter 27 | def path(self): 28 | self._path = None 29 | 30 | @property 31 | def url(self): 32 | return self._url 33 | 34 | @url.setter 35 | def url(self, value): 36 | self._url = value 37 | 38 | @url.deleter 39 | def url(self): 40 | self._url = None -------------------------------------------------------------------------------- /citrination_client/data/dataset_version.py: -------------------------------------------------------------------------------- 1 | class DatasetVersion(object): 2 | """ 3 | Class representation of a version of a dataset on Citrination. 4 | """ 5 | 6 | def __init__(self, number): 7 | """ 8 | Constructor. 9 | 10 | :param number: The number of the dataset version 11 | :type number: ints 12 | """ 13 | self._number = number 14 | 15 | @property 16 | def number(self): 17 | return self._number 18 | 19 | @number.setter 20 | def number(self, value): 21 | self._number = value 22 | 23 | @number.deleter 24 | def number(self): 25 | self._number = None -------------------------------------------------------------------------------- /citrination_client/data/ingest/__init__.py: -------------------------------------------------------------------------------- 1 | from citrination_client.data.ingest.ingester import Ingester 2 | from citrination_client.data.ingest.ingester_list import IngesterList 3 | from citrination_client.data.ingest.client import IngestClient 4 | -------------------------------------------------------------------------------- /citrination_client/data/routes.py: -------------------------------------------------------------------------------- 1 | from citrination_client.util.quote_finder import quote 2 | 3 | def update_file(file_id): 4 | return 'data_sets/update_file/{}'.format(file_id) 5 | 6 | def list_files(dataset_id): 7 | return 'datasets/{}/list_filepaths'.format(dataset_id) 8 | 9 | def matched_files(dataset_id): 10 | return 'datasets/{}/download_files'.format(dataset_id) 11 | 12 | def pif_dataset_uid(dataset_id, pif_uid, pif_version = None, with_metadata = False): 13 | url = 'datasets/{}/pif/{}'.format(dataset_id, pif_uid) 14 | return _get_pif_url_helper(url, pif_version, with_metadata) 15 | 16 | def pif_dataset_version_uid(dataset_id, version, pif_uid, pif_version = None, with_metadata = False): 17 | url = 'datasets/{}/version/{}/pif/{}'.format(dataset_id, version, pif_uid) 18 | return _get_pif_url_helper(url, pif_version, with_metadata) 19 | 20 | def _get_pif_url_helper(url, pif_version, with_metadata): 21 | if pif_version: 22 | url += '/pif-version/{}'.format(pif_version) 23 | if with_metadata: 24 | url += '?with-metadata=true' 25 | return url 26 | 27 | def create_dataset(): 28 | return 'data_sets/create_dataset' 29 | 30 | def create_dataset_version(dataset_id): 31 | return 'data_sets/{}/create_dataset_version'.format(dataset_id) 32 | 33 | def update_dataset(dataset_id): 34 | return 'data_sets/{}/update'.format(dataset_id) 35 | 36 | def upload_to_dataset(dataset_id): 37 | return 'data_sets/{}/upload'.format(dataset_id) 38 | 39 | def file_dataset_path(dataset_id, file_path): 40 | return 'datasets/{}/file/{}'.format(dataset_id, quote(file_path)) 41 | 42 | def file_dataset_version_path(dataset_id, version, file_path): 43 | return 'datasets/{}/version/{}/files/{}'.format(dataset_id, version, quote(file_path)) 44 | 45 | def get_data_view_ids_path(dataset_id): 46 | return 'v1/datasets/{}/data-views'.format(dataset_id) 47 | -------------------------------------------------------------------------------- /citrination_client/data/tests/test_dataset.py: -------------------------------------------------------------------------------- 1 | from citrination_client.data import Dataset 2 | 3 | def test_can_crud_name(): 4 | """ 5 | Tests that full get/set/delete functionality is 6 | available for the name property 7 | """ 8 | d = Dataset(1) 9 | name = "name" 10 | assert d.name is None 11 | d.name = name 12 | assert d.name is name 13 | del(d.name) 14 | assert d.name is None 15 | 16 | def test_can_crud_description(): 17 | """ 18 | Tests that full get/set/delete functionality is 19 | available for the description property 20 | """ 21 | d = Dataset(1) 22 | description = "description" 23 | assert d.description is None 24 | d.description = description 25 | assert d.description is description 26 | del(d.description) 27 | assert d.description is None 28 | 29 | def test_can_crud_created_at(): 30 | """ 31 | Tests that full get/set/delete functionality is 32 | available for the created_at property 33 | """ 34 | d = Dataset(1) 35 | created_at = "created_at" 36 | assert d.created_at is None 37 | d.created_at = created_at 38 | assert d.created_at is created_at 39 | del(d.created_at) 40 | assert d.created_at is None -------------------------------------------------------------------------------- /citrination_client/data/tests/test_dataset_file.py: -------------------------------------------------------------------------------- 1 | from citrination_client.data import DatasetFile 2 | 3 | def test_can_crud_path(): 4 | """ 5 | Tests that full get/set/delete functionality is 6 | available for the path property 7 | """ 8 | path = "path" 9 | d = DatasetFile(path) 10 | assert d.path is path 11 | d.path = path 12 | assert d.path is path 13 | del(d.path) 14 | assert d.path is None 15 | 16 | def test_can_crud_url(): 17 | """ 18 | Tests that full get/set/delete functionality is 19 | available for the url property 20 | """ 21 | path = "path" 22 | d = DatasetFile(path) 23 | url = "http://mysite.com" 24 | assert d.url is None 25 | d.url = url 26 | assert d.url is url 27 | del(d.url) 28 | assert d.url is None -------------------------------------------------------------------------------- /citrination_client/data/tests/test_dataset_version.py: -------------------------------------------------------------------------------- 1 | from citrination_client.data import DatasetVersion 2 | 3 | def test_can_crud_number(): 4 | """ 5 | Tests that full get/set/delete functionality is 6 | available for the number property 7 | """ 8 | d = DatasetVersion(1) 9 | number = 2 10 | assert d.number is 1 11 | d.number = number 12 | assert d.number is number 13 | del(d.number) 14 | assert d.number is None -------------------------------------------------------------------------------- /citrination_client/data/tests/test_files/data/data_holder/1: -------------------------------------------------------------------------------- 1 | string 2 | -------------------------------------------------------------------------------- /citrination_client/data/tests/test_files/data/data_holder/2: -------------------------------------------------------------------------------- 1 | string 2 | -------------------------------------------------------------------------------- /citrination_client/data/tests/test_files/data/data_holder/3: -------------------------------------------------------------------------------- 1 | string 2 | -------------------------------------------------------------------------------- /citrination_client/data/tests/test_files/data/keys_and_values.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /citrination_client/data/tests/test_files/data/level2/level2.json: -------------------------------------------------------------------------------- 1 | string -------------------------------------------------------------------------------- /citrination_client/data/tests/test_files/data/level2/level3/level3.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /citrination_client/data/tests/test_files/data/orienteering/flags.txt: -------------------------------------------------------------------------------- 1 | strings 2 | -------------------------------------------------------------------------------- /citrination_client/data/tests/test_files/data/revolver/A.csv: -------------------------------------------------------------------------------- 1 | string 2 | -------------------------------------------------------------------------------- /citrination_client/data/tests/test_files/data/revolver/B.json: -------------------------------------------------------------------------------- 1 | string 2 | -------------------------------------------------------------------------------- /citrination_client/data/tests/test_files/data/revolver/commas.csv: -------------------------------------------------------------------------------- 1 | string 2 | -------------------------------------------------------------------------------- /citrination_client/data/tests/test_files/data/weird_extensions.woodle: -------------------------------------------------------------------------------- 1 | string -------------------------------------------------------------------------------- /citrination_client/data/tests/test_files/empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CitrineInformatics/python-citrination-client/2cda4dd2a600224acd4e19ae2df66a922dd93342/citrination_client/data/tests/test_files/empty -------------------------------------------------------------------------------- /citrination_client/data/tests/test_upload_result.py: -------------------------------------------------------------------------------- 1 | from citrination_client.data import UploadResult 2 | 3 | def test_indicates_failure(): 4 | """ 5 | Tests that the presence of a single failure 6 | will make the result report unsuccessful 7 | """ 8 | ur = UploadResult() 9 | ur.add_failure("test.jpg", "bad filename") 10 | assert ur.successful() is False 11 | 12 | def test_default_is_success(): 13 | """ 14 | Tests that an empty result is successful 15 | """ 16 | ur = UploadResult() 17 | assert ur.successful() 18 | 19 | def test_add_success(): 20 | """ 21 | Tests that a success can be added to the upload 22 | result 23 | """ 24 | ur = UploadResult() 25 | ur.add_success("my/path.jpg", 2, "path.jpg") 26 | assert len(ur.successes) == 1 27 | 28 | def test_cant_write_lists(): 29 | """ 30 | Tests that the successes and failures properties 31 | are not settable 32 | """ 33 | ur = UploadResult() 34 | 35 | try: 36 | ur.successes = "asdf" 37 | assert False 38 | except AttributeError: 39 | assert True 40 | 41 | try: 42 | ur.failures = "asdf" 43 | assert False 44 | except AttributeError: 45 | assert True 46 | -------------------------------------------------------------------------------- /citrination_client/data/upload_result.py: -------------------------------------------------------------------------------- 1 | class UploadResult(object): 2 | """ 3 | The result of an attempted upload. Keeps track of the failures 4 | and successes if multiple files were uploaded (for instance, 5 | if a directory was uploaded). 6 | """ 7 | 8 | def __init__(self): 9 | """ 10 | Constructor. 11 | """ 12 | self._failures = [] 13 | self._successes = [] 14 | 15 | @property 16 | def failures(self): 17 | return self._failures 18 | 19 | @property 20 | def successes(self): 21 | return self._successes 22 | 23 | def successful(self): 24 | """ 25 | Indicates whether or not the entire upload was successful. 26 | 27 | :return: Whether or not the upload was successful 28 | :rtype: bool 29 | """ 30 | return len(self._failures) == 0 31 | 32 | def add_failure(self, filepath, reason): 33 | """ 34 | Registers a file as a failure to upload. 35 | 36 | :param filepath: The path to the file which was to be uploaded. 37 | :type filepath: str 38 | :param reason: The reason the file failed to upload 39 | :type reason: str 40 | """ 41 | self._failures.append({ 42 | "path": filepath, 43 | "reason": reason 44 | }) 45 | 46 | def add_success(self, filepath, id, dest_path): 47 | """ 48 | Registers a file as successfully uploaded. 49 | 50 | :param filepath: The path to the successfully uploaded file. 51 | :type filepath: str 52 | :param id: The id of the successfully uploaded file. 53 | :type id: Union[str, int] 54 | :param dest_path: The destination path to the successfully uploaded file. 55 | :type dest_path: str 56 | """ 57 | self._successes.append({ 58 | "path": filepath, "id": str(id), "dest_path": dest_path 59 | }) 60 | -------------------------------------------------------------------------------- /citrination_client/models/__init__.py: -------------------------------------------------------------------------------- 1 | from citrination_client.models.event import Event 2 | from citrination_client.models.service_status import ServiceStatus 3 | from citrination_client.models.predicted_value import PredictedValue 4 | from citrination_client.models.prediction_result import PredictionResult 5 | from citrination_client.models.projection import Projection 6 | from citrination_client.models.tsne import Tsne 7 | from citrination_client.models.data_view_status import DataViewStatus 8 | from citrination_client.models.columns import * 9 | from citrination_client.models.client import ModelsClient 10 | from citrination_client.models.design import * 11 | -------------------------------------------------------------------------------- /citrination_client/models/columns/__init__.py: -------------------------------------------------------------------------------- 1 | from .alloy_composition import AlloyCompositionColumn 2 | from .base import BaseColumn 3 | from .categorical import CategoricalColumn 4 | from .descriptor_converter import DescriptorConverter 5 | from .formulation import FormulationColumn 6 | from .inorganic_chemical_formula import InorganicChemicalFormulaColumn 7 | from .integer import IntColumn 8 | from .organic_chemical_formula import OrganicChemicalFormulaColumn 9 | from .real import RealColumn 10 | from .vector import VectorColumn 11 | -------------------------------------------------------------------------------- /citrination_client/models/columns/alloy_composition.py: -------------------------------------------------------------------------------- 1 | from citrination_client.models.columns.base import BaseColumn 2 | 3 | class AlloyCompositionColumn(BaseColumn): 4 | """ 5 | An alloy composition column configuration for a data view. Parameterized 6 | with the basic column options, plus the balance element for the column 7 | and the basis value for the composition. 8 | """ 9 | 10 | TYPE = "Alloy composition" 11 | 12 | def __init__(self, name, role, balance_element, group_by_key=False, units=None, basis=100.0): 13 | """ 14 | Constructor. 15 | 16 | :param name: The name of the column 17 | :type name: str 18 | :param role: The role the column will play in machine learning: 19 | "Input" 20 | "Output" 21 | "Latent Variable" 22 | "Ignore" 23 | :type role: str 24 | :param group_by_key: Whether or not this column should be used for 25 | grouping during cross validation 26 | :type group_by_key: bool 27 | :param units: Optionally, the units for the column 28 | :type units: str 29 | :param balance_element: The element making up the balance in the 30 | composition 31 | :type balance_element: str 32 | :param basis: The total amount of composition when deciding how to fill 33 | the balance 34 | :type basis: float 35 | 36 | """ 37 | super(AlloyCompositionColumn, self).__init__(name=name, 38 | role=role, 39 | group_by_key=group_by_key, 40 | units=units) 41 | self._balance_element = balance_element 42 | self._basis = basis 43 | 44 | def build_options(self): 45 | return { 46 | "balance_element": self.balance_element, 47 | "basis": self.basis 48 | } 49 | 50 | @property 51 | def basis(self): 52 | return self._basis 53 | 54 | @basis.setter 55 | def basis(self, value): 56 | self._basis = value 57 | 58 | @basis.deleter 59 | def basis(self): 60 | self._basis = None 61 | 62 | @property 63 | def balance_element(self): 64 | return self._balance_element 65 | 66 | @balance_element.setter 67 | def balance_element(self, value): 68 | self._balance_element = value 69 | 70 | @balance_element.deleter 71 | def balance_element(self): 72 | self._balance_element = None 73 | -------------------------------------------------------------------------------- /citrination_client/models/columns/categorical.py: -------------------------------------------------------------------------------- 1 | from citrination_client.models.columns.base import BaseColumn 2 | from citrination_client.base.errors import CitrinationClientError 3 | 4 | from six import string_types 5 | 6 | class CategoricalColumn(BaseColumn): 7 | """ 8 | A categorical column configuration for a data view. Parameterized 9 | with the basic column options and a list of valid values for the 10 | column to have. 11 | """ 12 | 13 | TYPE = "Categorical" 14 | 15 | def __init__(self, name, role, group_by_key=False, units=None, categories=[]): 16 | """ 17 | Constructor. 18 | 19 | :param name: The name of the column 20 | :type name: str 21 | :param role: The role the column will play in machine learning: 22 | "Input" 23 | "Output" 24 | "Latent Variable" 25 | "Ignore" 26 | :type role: str 27 | :param group_by_key: Whether or not this column should be used for 28 | grouping during cross validation 29 | :type group_by_key: bool 30 | :param units: Optionally, the units for the column 31 | :type units: str 32 | :param categories: An array of strings that are valid values for data 33 | in this column 34 | :type categories: list of str 35 | """ 36 | super(CategoricalColumn, self).__init__(name=name, 37 | role=role, 38 | group_by_key=group_by_key, 39 | units=units) 40 | self.categories = categories 41 | 42 | def build_options(self): 43 | return { 44 | "categories": self.categories 45 | } 46 | 47 | @property 48 | def categories(self): 49 | return self._categories 50 | 51 | @categories.setter 52 | def categories(self, value): 53 | self._validate_categories(value) 54 | self._categories = [str(v) for v in value] 55 | 56 | @categories.deleter 57 | def categories(self): 58 | self._categories = None 59 | 60 | def _validate_categories(self, categories): 61 | if type(categories) is not list: 62 | raise CitrinationClientError("CategoricalColumn requires that the categories value is a list of strings") 63 | 64 | if not all(isinstance(item, string_types) for item in categories): 65 | raise CitrinationClientError("CategoricalColumn requires that the categories value is a list of strings") 66 | -------------------------------------------------------------------------------- /citrination_client/models/columns/column_factory.py: -------------------------------------------------------------------------------- 1 | from citrination_client.models.columns import * 2 | from citrination_client.base import CitrinationClientError 3 | 4 | def _flatten_column_dict(response_dict): 5 | flat_dict = {} 6 | for k in response_dict: 7 | if k == "options" or k == "type": 8 | continue 9 | flat_dict[k] = response_dict[k] 10 | 11 | if response_dict["options"]: 12 | for option in response_dict["options"]: 13 | flat_dict[option] = response_dict["options"][option] 14 | 15 | return flat_dict 16 | 17 | def _get_column_class_from_type(type): 18 | if type == RealColumn.TYPE: 19 | return RealColumn 20 | if type == IntColumn.TYPE: 21 | return IntColumn 22 | elif type == CategoricalColumn.TYPE: 23 | return CategoricalColumn 24 | elif type == AlloyCompositionColumn.TYPE: 25 | return AlloyCompositionColumn 26 | elif type == InorganicChemicalFormulaColumn.TYPE: 27 | return InorganicChemicalFormulaColumn 28 | elif type == VectorColumn.TYPE: 29 | return VectorColumn 30 | else: 31 | raise CitrinationClientError("Unknown column type: {}".format(type)) 32 | 33 | class ColumnFactory(object): 34 | 35 | @staticmethod 36 | def from_dict(response_dict): 37 | column_class = _get_column_class_from_type(response_dict["type"]) 38 | processed_dict = _flatten_column_dict(response_dict) 39 | return column_class(**processed_dict) 40 | -------------------------------------------------------------------------------- /citrination_client/models/columns/descriptor_converter.py: -------------------------------------------------------------------------------- 1 | from .alloy_composition import AlloyCompositionColumn 2 | from .categorical import CategoricalColumn 3 | from .formulation import FormulationColumn 4 | from .inorganic_chemical_formula import InorganicChemicalFormulaColumn 5 | from .integer import IntColumn 6 | from .organic_chemical_formula import OrganicChemicalFormulaColumn 7 | from .real import RealColumn 8 | from .vector import VectorColumn 9 | 10 | 11 | class DescriptorConverter(object): 12 | 13 | @classmethod 14 | def convert(self, col_name, descriptor, role, units=None): 15 | column_type = descriptor['category'] 16 | if column_type == CategoricalColumn.TYPE: 17 | categories = [str(v) for v in descriptor["descriptorValues"]] 18 | 19 | return CategoricalColumn( 20 | name=col_name, 21 | role=role, 22 | units=units, 23 | categories=categories 24 | ) 25 | elif column_type == RealColumn.TYPE: 26 | lower_bound = descriptor["lowerBound"] 27 | upper_bound = descriptor["upperBound"] 28 | 29 | return RealColumn( 30 | name=col_name, 31 | role=role, 32 | units=units, 33 | lower_bound=lower_bound, 34 | upper_bound=upper_bound 35 | ) 36 | elif column_type == IntColumn.TYPE: 37 | lower_bound = descriptor["lowerBound"] 38 | upper_bound = descriptor["upperBound"] 39 | 40 | return IntColumn( 41 | name=col_name, 42 | role=role, 43 | units=units, 44 | lower_bound=lower_bound, 45 | upper_bound=upper_bound 46 | ) 47 | elif column_type == AlloyCompositionColumn.TYPE: 48 | balance_element = descriptor['balanceElement'] 49 | basis = descriptor['basis'] 50 | 51 | return AlloyCompositionColumn( 52 | name=col_name, 53 | role=role, 54 | units=units, 55 | balance_element=balance_element, 56 | basis=basis 57 | ) 58 | elif column_type == InorganicChemicalFormulaColumn.TYPE: 59 | return InorganicChemicalFormulaColumn( 60 | name=col_name, 61 | role=role, 62 | units=units 63 | ) 64 | elif column_type == OrganicChemicalFormulaColumn.TYPE: 65 | return OrganicChemicalFormulaColumn( 66 | name=col_name, 67 | role=role, 68 | units=units 69 | ) 70 | elif column_type == FormulationColumn.TYPE: 71 | return FormulationColumn( 72 | name=col_name, 73 | role=role, 74 | units=units 75 | ) 76 | elif column_type == VectorColumn.TYPE: 77 | length = descriptor["length"] 78 | 79 | return VectorColumn( 80 | name=col_name, 81 | role=role, 82 | units=units, 83 | length=length 84 | ) 85 | -------------------------------------------------------------------------------- /citrination_client/models/columns/formulation.py: -------------------------------------------------------------------------------- 1 | from citrination_client.models.columns.base import BaseColumn 2 | 3 | 4 | class FormulationColumn(BaseColumn): 5 | """ 6 | A formulation column configuration for a data view. 7 | Parameterized with the basic column options. 8 | """ 9 | 10 | TYPE = "Formulation" 11 | -------------------------------------------------------------------------------- /citrination_client/models/columns/inorganic_chemical_formula.py: -------------------------------------------------------------------------------- 1 | from citrination_client.models.columns.base import BaseColumn 2 | 3 | class InorganicChemicalFormulaColumn(BaseColumn): 4 | """ 5 | An inorganic chemical formula column configuration for a data view. 6 | Parameterized with the basic column options. 7 | """ 8 | 9 | TYPE = "Inorganic" 10 | -------------------------------------------------------------------------------- /citrination_client/models/columns/organic_chemical_formula.py: -------------------------------------------------------------------------------- 1 | from citrination_client.models.columns.base import BaseColumn 2 | 3 | class OrganicChemicalFormulaColumn(BaseColumn): 4 | """ 5 | An organic chemical formula column configuration for a data view. 6 | Parameterized with the basic column options. 7 | """ 8 | 9 | TYPE = "Organic" 10 | -------------------------------------------------------------------------------- /citrination_client/models/columns/tests/test_alloy_composition.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from citrination_client.models.columns import * 3 | from citrination_client.base.errors import CitrinationClientError 4 | 5 | 6 | class TestAlloyCompositionColumn(object): 7 | 8 | @classmethod 9 | def setup_class(self): 10 | self.name = "Property Band gap" 11 | self.role = "Input" 12 | self.group_by_key = False 13 | self.units = "eV" 14 | 15 | def test_alloy_composition_column_serializes_options_correctly(self): 16 | """ 17 | Tests that the AlloyCompositionColumn correctly expresses it's 18 | configuration (balance_element, basis, threshold) in its options 19 | dictionary 20 | """ 21 | 22 | basis = 200.0 23 | balance_element = "ALUMINUM" 24 | 25 | column = AlloyCompositionColumn(name=self.name, role=self.role, group_by_key=self.group_by_key, units=self.units, basis=basis, balance_element=balance_element) 26 | 27 | c_dict = column.to_dict() 28 | 29 | assert c_dict["name"] == self.name 30 | assert c_dict["role"] == self.role 31 | assert c_dict["group_by_key"] == self.group_by_key 32 | assert c_dict["units"] == self.units 33 | assert c_dict["type"] == AlloyCompositionColumn.TYPE 34 | assert c_dict["options"]["basis"] == basis 35 | assert c_dict["options"]["balance_element"] == balance_element 36 | -------------------------------------------------------------------------------- /citrination_client/models/columns/tests/test_base.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from citrination_client.models.columns import * 3 | from citrination_client.base.errors import * 4 | 5 | class TestBaseColumn(object): 6 | 7 | @classmethod 8 | def setup_class(self): 9 | self.name = "Property Band gap" 10 | self.role = "Input" 11 | self.group_by_key = False 12 | self.units = "eV" 13 | 14 | def test_base_column_validates_role(self): 15 | try: 16 | column = BaseColumn(name=self.name, role="asdf") 17 | assert False, "Base column should validate role" 18 | except CitrinationClientError: 19 | pass 20 | -------------------------------------------------------------------------------- /citrination_client/models/columns/tests/test_categorical.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from citrination_client.models.columns import * 3 | from citrination_client.base.errors import * 4 | 5 | class TestCategoricalColumn(object): 6 | 7 | @classmethod 8 | def setup_class(self): 9 | self.name = "Property Band gap" 10 | self.role = "Input" 11 | self.group_by_key = False 12 | self.units = "eV" 13 | 14 | def test_categorical_column_writes_categories_correctly(self): 15 | categories = ["Grey", "Blue"] 16 | 17 | column = CategoricalColumn(name=self.name, role=self.role, group_by_key=self.group_by_key, categories=categories) 18 | 19 | c_dict = column.to_dict() 20 | 21 | assert c_dict["name"] == self.name 22 | assert c_dict["role"] == self.role 23 | assert c_dict["group_by_key"] == self.group_by_key 24 | assert c_dict["type"] == CategoricalColumn.TYPE 25 | assert c_dict["units"] == None 26 | assert c_dict["options"]["categories"] == categories 27 | 28 | def test_categorical_column_validates_categories(self): 29 | """ 30 | Tests that the CategoricalColumn class validates that the categories 31 | value is a list of strings. 32 | """ 33 | 34 | categories = 1 35 | 36 | with pytest.raises(CitrinationClientError): 37 | CategoricalColumn(name=self.name, role=self.role, group_by_key=self.group_by_key, categories=categories) 38 | 39 | categories = ["Grey", 1] 40 | with pytest.raises(CitrinationClientError): 41 | CategoricalColumn(name=self.name, role=self.role, group_by_key=self.group_by_key, categories=categories) 42 | 43 | categories = ["Grey", "Blue"] 44 | CategoricalColumn(name=self.name, role=self.role, group_by_key=self.group_by_key, categories=categories) 45 | -------------------------------------------------------------------------------- /citrination_client/models/columns/tests/test_int.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from citrination_client.models.columns import * 3 | from citrination_client.base.errors import * 4 | 5 | class TestIntColumn(object): 6 | 7 | @classmethod 8 | def setup_class(self): 9 | self.name = "Property Ingredient count" 10 | self.role = "Input" 11 | self.group_by_key = False 12 | self.units = "ingredients" 13 | 14 | def test_int_column_validates_intable(self): 15 | """ 16 | Tests that the bounds options passed into the 17 | IntColumn constructor are validated as castable to int 18 | """ 19 | 20 | # Non intable lower 21 | lower_bound = "asdf" 22 | upper_bound = 1 23 | 24 | with pytest.raises(CitrinationClientError): 25 | IntColumn(name=self.name, role=self.role, group_by_key=self.group_by_key, lower_bound=lower_bound, upper_bound=upper_bound) 26 | 27 | # Non intable upper 28 | lower_bound = 0 29 | upper_bound = "asdf" 30 | 31 | with pytest.raises(CitrinationClientError): 32 | IntColumn(name=self.name, role=self.role, group_by_key=self.group_by_key, lower_bound=lower_bound, upper_bound=upper_bound) 33 | 34 | # Some valid values; should not raise exceptions 35 | lower_bound = 0 36 | upper_bound = 10 37 | IntColumn(name=self.name, role=self.role, group_by_key=self.group_by_key, lower_bound=lower_bound, upper_bound=upper_bound) 38 | 39 | def test_int_column_validates_bounded_range(self): 40 | """ 41 | Tests that the upper bound must be greater than the lower bound 42 | on IntColumn construction 43 | """ 44 | 45 | # Invalid range, lower is greater than upper 46 | lower_bound = 10 47 | upper_bound = 1 48 | with pytest.raises(CitrinationClientError): 49 | IntColumn(name=self.name, role=self.role, group_by_key=self.group_by_key, lower_bound=lower_bound, upper_bound=upper_bound) 50 | 51 | # Valid range, lower is equal to upper 52 | lower_bound = 10 53 | upper_bound = 10 54 | IntColumn(name=self.name, role=self.role, group_by_key=self.group_by_key, lower_bound=lower_bound, upper_bound=upper_bound) 55 | 56 | def test_int_column_serializes_correctly(self): 57 | """ 58 | Tests that the IntColumn correctly expresses its options in 59 | dictionary format; particularly, that it stringifies Infinities 60 | for the back end 61 | """ 62 | 63 | # Invalid range, lower is greater than upper 64 | lower_bound = 0 65 | upper_bound = 10 66 | column = IntColumn(name=self.name, role=self.role, group_by_key=self.group_by_key, units=self.units, lower_bound=lower_bound, upper_bound=upper_bound) 67 | 68 | c_dict = column.to_dict() 69 | 70 | assert c_dict["name"] == self.name 71 | assert c_dict["role"] == self.role 72 | assert c_dict["group_by_key"] == self.group_by_key 73 | assert c_dict["units"] == self.units 74 | assert c_dict["type"] == IntColumn.TYPE 75 | assert c_dict["options"]["lower_bound"] == lower_bound 76 | assert c_dict["options"]["upper_bound"] == upper_bound 77 | -------------------------------------------------------------------------------- /citrination_client/models/columns/tests/test_vector.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from citrination_client.models.columns import * 3 | from citrination_client.base.errors import * 4 | 5 | class TestVectorColumn(object): 6 | 7 | @classmethod 8 | def setup_class(self): 9 | self.name = "Property Band gap" 10 | self.role = "Input" 11 | self.group_by_key = False 12 | self.units = "eV" 13 | 14 | def test_vector_column_validates_length_castable_to_int(self): 15 | """ 16 | Tests that the length option passed into the RealColumn constructor 17 | is castable to int 18 | """ 19 | 20 | # Non intable length 21 | length = "asdf" 22 | 23 | with pytest.raises(CitrinationClientError): 24 | VectorColumn(name=self.name, role=self.role, group_by_key=self.group_by_key, length=length) 25 | 26 | def test_vector_column_serializes_length_correctly(self): 27 | """ 28 | Tests that the VectorColumn class expresses the length option correctly 29 | in its dictionary form. 30 | """ 31 | 32 | length = 10.0 33 | 34 | column = VectorColumn(name=self.name, role=self.role, group_by_key=self.group_by_key, units=self.units, length=length) 35 | 36 | c_dict = column.to_dict() 37 | 38 | assert c_dict["name"] == self.name 39 | assert c_dict["role"] == self.role 40 | assert c_dict["group_by_key"] == self.group_by_key 41 | assert c_dict["units"] == self.units 42 | assert c_dict["type"] == VectorColumn.TYPE 43 | assert c_dict["options"]["length"] == 10 44 | -------------------------------------------------------------------------------- /citrination_client/models/columns/vector.py: -------------------------------------------------------------------------------- 1 | from citrination_client.models.columns.base import BaseColumn 2 | from citrination_client.base.errors import CitrinationClientError 3 | 4 | class VectorColumn(BaseColumn): 5 | """ 6 | An vector column configuration for a data view. 7 | Parameterized with the basic column options and a length parameter expressing the length of vectors in this column. 8 | """ 9 | 10 | TYPE = "Vector" 11 | 12 | def __init__(self, name, role, group_by_key=False, units=None, length=None): 13 | """ 14 | Constructor. 15 | 16 | :param name: The name of the column 17 | :type name: str 18 | :param role: The role the column will play in machine learning: 19 | "Input" 20 | "Output" 21 | "Latent Variable" 22 | "Ignore" 23 | :type role: str 24 | :param group_by_key: Whether or not this column should be used for 25 | grouping during cross validation 26 | :type group_by_key: bool 27 | :param units: Optionally, the units for this column 28 | :type units: str 29 | :param length: The length of vectors in this column 30 | :type length: int 31 | """ 32 | super(VectorColumn, self).__init__(name=name, 33 | role=role, 34 | group_by_key=group_by_key, 35 | units=units) 36 | self.length = length 37 | 38 | def build_options(self): 39 | return { 40 | "length": self.length 41 | } 42 | 43 | @property 44 | def length(self): 45 | return self._length 46 | 47 | @length.setter 48 | def length(self, value): 49 | try: 50 | self._length = int(value) 51 | except ValueError: 52 | raise CitrinationClientError("When constructing a VectorColumn, parameter length must be castable as an int") 53 | 54 | @length.deleter 55 | def length(self): 56 | self._length = None 57 | -------------------------------------------------------------------------------- /citrination_client/models/data_view_status.py: -------------------------------------------------------------------------------- 1 | class DataViewStatus(object): 2 | """ 3 | A summary of each of the services a data view exposes. 4 | """ 5 | 6 | def __init__(self, predict=None, experimental_design=None, data_reports=None, model_reports=None): 7 | """ 8 | Constructor. 9 | 10 | :param predict: The status predict 11 | :type predict: ServiceStatus 12 | :param experimental_design: The status of experimental_design 13 | :type experimental_design: ServiceStatus 14 | :param data_reports: The status of data_analysis 15 | :type data_reports: ServiceStatus 16 | :param model_reports: The status of model reports 17 | :type model_reports: ServiceStatus 18 | """ 19 | self._predict = predict 20 | self._experimental_design = experimental_design 21 | self._data_reports = data_reports 22 | self._model_reports = model_reports 23 | 24 | @property 25 | def predict(self): 26 | return self._predict 27 | 28 | @predict.setter 29 | def predict(self, value): 30 | self._predict = value 31 | 32 | @predict.deleter 33 | def predict(self): 34 | self._predict = None 35 | 36 | @property 37 | def experimental_design(self): 38 | return self._experimental_design 39 | 40 | @experimental_design.setter 41 | def experimental_design(self, value): 42 | self._experimental_design = value 43 | 44 | @experimental_design.deleter 45 | def experimental_design(self): 46 | self._experimental_design = None 47 | 48 | @property 49 | def data_reports(self): 50 | return self._data_reports 51 | 52 | @data_reports.setter 53 | def data_reports(self, value): 54 | self._data_reports = value 55 | 56 | @data_reports.deleter 57 | def data_reports(self): 58 | self._data_reports = None 59 | 60 | @property 61 | def model_reports(self): 62 | return self._model_reports 63 | 64 | @model_reports.setter 65 | def model_reports(self, value): 66 | self._model_reports = value 67 | 68 | @model_reports.deleter 69 | def model_reports(self): 70 | self._model_reports = None 71 | -------------------------------------------------------------------------------- /citrination_client/models/design/__init__.py: -------------------------------------------------------------------------------- 1 | from citrination_client.models.design.design_run import DesignRun 2 | from citrination_client.models.design.process_status import ProcessStatus 3 | from citrination_client.models.design.design_results import DesignResults 4 | from citrination_client.models.design.target import Target 5 | from citrination_client.models.design.constraints import * -------------------------------------------------------------------------------- /citrination_client/models/design/constraints/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import BaseConstraint 2 | from .int_range import IntRangeConstraint 3 | from .int_value import IntValueConstraint 4 | from .real_value import RealValueConstraint 5 | from .real_range import RealRangeConstraint 6 | from .categorical import CategoricalConstraint 7 | from .elemental_inclusion import ElementalInclusionConstraint 8 | from .elemental_composition import ElementalCompositionConstraint 9 | -------------------------------------------------------------------------------- /citrination_client/models/design/constraints/base.py: -------------------------------------------------------------------------------- 1 | class BaseConstraint(object): 2 | 3 | def to_dict(self): 4 | """ 5 | Returns a dictionary representing the constraint. 6 | Assists in JSON serialization. 7 | 8 | :return: A dictionary with the name and type of the constraint 9 | along with any type-specific metadata the constraint may 10 | require 11 | """ 12 | return { 13 | "name": self._name, 14 | "type": self._type, 15 | "options": self.options() 16 | } 17 | -------------------------------------------------------------------------------- /citrination_client/models/design/constraints/categorical.py: -------------------------------------------------------------------------------- 1 | from citrination_client.models.design.constraints.base import BaseConstraint 2 | 3 | class CategoricalConstraint(BaseConstraint): 4 | """ 5 | Constrains a column to a particular set of categorical 6 | values. 7 | """ 8 | 9 | def __init__(self, name, accepted_categories): 10 | """ 11 | Constructor. 12 | 13 | :param name: The name of the column in the data 14 | view to which this constraint should be applied 15 | :type name: str 16 | :param accepted_categories: An array of categories to constrain the name to 17 | :type accepted_categories: list of str 18 | """ 19 | self._type = "categorical" 20 | self._name = name 21 | self._categories = accepted_categories 22 | 23 | def options(self): 24 | return { 25 | "categories": self._categories, 26 | } -------------------------------------------------------------------------------- /citrination_client/models/design/constraints/elemental_composition.py: -------------------------------------------------------------------------------- 1 | from citrination_client.models.design.constraints.base import BaseConstraint 2 | from citrination_client.base.errors import CitrinationClientError 3 | 4 | class ElementalCompositionConstraint(BaseConstraint): 5 | """ 6 | Constrains a composition or inorganic formula column 7 | to a particular percentage. 8 | """ 9 | 10 | def __init__(self, name, elements, minimum, maximum): 11 | """ 12 | Constructor. 13 | 14 | :param name: The name of the column in the data 15 | view to which this constraint should be applied 16 | :type name: str 17 | :param elements: An array of element abbreviations as 18 | strings, e.g. ["Mg", "C"] 19 | :type elements: list of str 20 | :param minimum: The minimum value (<= 100) as a percentage 21 | at which the specified elements should appear in 22 | candidate compositions 23 | :type minimum: float 24 | :param maximum: The maximum value (<= 100) as a percentage 25 | at which the specified elements should appear in 26 | candidate compositions 27 | :type maximum: float 28 | """ 29 | if not 0 <= minimum <= 100: 30 | raise CitrinationClientError("ElementalCompositionConstraint requires that minimum be between 0 and 100") 31 | 32 | if not 0 <= maximum <= 100: 33 | raise CitrinationClientError("ElementalCompositionConstraint requires that maximum be between 0 and 100") 34 | 35 | if not maximum >= minimum: 36 | raise CitrinationClientError("ElementalCompositionConstraint requires that maximum be greater than minimum") 37 | 38 | 39 | self._type = "elementalCompositionConstraint" 40 | self._elements = elements 41 | self._name = name 42 | self._min = minimum 43 | self._max = maximum 44 | 45 | def options(self): 46 | return { 47 | "elements": self._elements, 48 | "min": self._min, 49 | "max": self._max 50 | } -------------------------------------------------------------------------------- /citrination_client/models/design/constraints/elemental_inclusion.py: -------------------------------------------------------------------------------- 1 | from citrination_client.models.design.constraints.base import BaseConstraint 2 | from citrination_client.base.errors import CitrinationClientError 3 | 4 | class ElementalInclusionConstraint(BaseConstraint): 5 | """ 6 | Constraint class which allows the assertion that a set of 7 | elements is included in the candidate compositions 8 | """ 9 | 10 | def __init__(self, name, elements, logic): 11 | """ 12 | Constructor. 13 | 14 | :param name: The name of the column in the data 15 | view to which this constraint should be applied 16 | :type name: str 17 | :param elements: An array of element abbreviations as 18 | strings, e.g. ["Mg", "C"] 19 | :type elements: list of str 20 | :param logic: The logic to apply to the constraint; either 21 | "must", "should", or "exclude" 22 | :type logic: str 23 | """ 24 | bad_logic_msg = "ElementalInclusionConstraint must be initialized with the logic parameter equal to \"must\", \"should\", or \"exclude\"" 25 | 26 | if logic not in ["must", "should", "exclude"]: 27 | raise CitrinationClientError(bad_logic_msg) 28 | 29 | self._name = name 30 | self._type = "elementalInclusionConstraint" 31 | self._elements = elements 32 | self._logic = logic 33 | 34 | def options(self): 35 | return { 36 | "elements": self._elements, 37 | "logic": self._logic 38 | } -------------------------------------------------------------------------------- /citrination_client/models/design/constraints/int_range.py: -------------------------------------------------------------------------------- 1 | from citrination_client.models.design.constraints.base import BaseConstraint 2 | from citrination_client.base.errors import CitrinationClientError 3 | import citrination_client.util.maths as maths 4 | 5 | 6 | class IntRangeConstraint(BaseConstraint): 7 | """ 8 | Constrains an integer valued column to a range. 9 | """ 10 | 11 | def __init__(self, name, minimum, maximum): 12 | """ 13 | Constructor. 14 | 15 | :param name: The name of the column in the data 16 | view to which this constraint should be applied 17 | :type name: str 18 | :param minimum: The minimum allowed value 19 | :type minimum: int 20 | :param maximum: The maximum allowed value 21 | :type maximum: int 22 | """ 23 | 24 | try: 25 | minimum_int = int(minimum) 26 | maximum_int = int(maximum) 27 | except TypeError: 28 | raise CitrinationClientError("IntRangeConstraint requires that minimum and maximum must be able to be cast to an integer") 29 | 30 | if minimum_int > maximum_int: 31 | raise CitrinationClientError("IntRangeConstraint requires that minimum be less than maximum") 32 | 33 | self._min = minimum_int 34 | self._max = maximum_int 35 | self._type = "integer" 36 | self._name = name 37 | 38 | def options(self): 39 | minimum = maths.convert_infinity_to_string(self._min) 40 | maximum = maths.convert_infinity_to_string(self._max) 41 | 42 | return { 43 | "min": minimum, 44 | "max": maximum 45 | } -------------------------------------------------------------------------------- /citrination_client/models/design/constraints/int_value.py: -------------------------------------------------------------------------------- 1 | from citrination_client.models.design.constraints.base import BaseConstraint 2 | 3 | 4 | class IntValueConstraint(BaseConstraint): 5 | """ 6 | Constrains a integer valued column to a single value. 7 | """ 8 | 9 | def __init__(self, name, value=None): 10 | """ 11 | Constructor. 12 | 13 | :param name: The name of the column in the data 14 | view to which this constraint should be applied 15 | :type name: str 16 | :param value: The value the column should be constrained to 17 | :type value: int 18 | """ 19 | self._type = "integer" 20 | self._name = name 21 | self._value = value 22 | 23 | def options(self): 24 | return { 25 | "value": self._value 26 | } -------------------------------------------------------------------------------- /citrination_client/models/design/constraints/real_range.py: -------------------------------------------------------------------------------- 1 | from citrination_client.models.design.constraints.base import BaseConstraint 2 | from citrination_client.base.errors import CitrinationClientError 3 | import citrination_client.util.maths as maths 4 | 5 | class RealRangeConstraint(BaseConstraint): 6 | """ 7 | Constrains a real valued column to a range. 8 | """ 9 | 10 | def __init__(self, name, minimum, maximum): 11 | """ 12 | Constructor. 13 | 14 | :param name: The name of the column in the data 15 | view to which this constraint should be applied 16 | :type name: str 17 | :param minimum: The minimum allowed value 18 | :type minimum: float 19 | :param maximum: The maximum allowed value 20 | :type maximum: float 21 | """ 22 | 23 | try: 24 | minimum_f = float(minimum) 25 | maximum_f = float(maximum) 26 | except TypeError: 27 | raise CitrinationClientError("RealRangeConstraint requires that minimum and maximum must be able to be cast to float") 28 | 29 | if minimum_f > maximum_f: 30 | raise CitrinationClientError("RealRangeConstraint requires that minimum be less than maximum") 31 | 32 | self._min = minimum_f 33 | self._max = maximum_f 34 | self._type = "real" 35 | self._name = name 36 | 37 | def options(self): 38 | minimum = maths.convert_infinity_to_string(self._min) 39 | maximum = maths.convert_infinity_to_string(self._max) 40 | 41 | return { 42 | "min": minimum, 43 | "max": maximum 44 | } -------------------------------------------------------------------------------- /citrination_client/models/design/constraints/real_value.py: -------------------------------------------------------------------------------- 1 | from citrination_client.models.design.constraints.base import BaseConstraint 2 | 3 | class RealValueConstraint(BaseConstraint): 4 | """ 5 | Constrains a real valued column to a single value. 6 | """ 7 | 8 | def __init__(self, name, value=None): 9 | """ 10 | Constructor. 11 | 12 | :param name: The name of the column in the data 13 | view to which this constraint should be applied 14 | :type name: str 15 | :param value: The value the column should be constrained to 16 | :type value: float 17 | """ 18 | self._type = "real" 19 | self._name = name 20 | self._value = value 21 | 22 | def options(self): 23 | return { 24 | "value": self._value 25 | } -------------------------------------------------------------------------------- /citrination_client/models/design/design_results.py: -------------------------------------------------------------------------------- 1 | class DesignResults(object): 2 | """ 3 | The result of a design run. Consists of a set of dictionaries representing 4 | the candidates which are the best materials according to the target 5 | and the constraints and a set of dictionaries representing candidates 6 | which would be the most useful in improving the quality of the model. 7 | """ 8 | 9 | def __init__(self, best_materials, next_experiments): 10 | """ 11 | Constructor. 12 | 13 | :param best_materials: An array of candidate dictionaries 14 | :type best_materials: list of dictionaries 15 | :param next_experiments: An array of candidate dictionaries 16 | :type next_experiments: list of dictionaries 17 | """ 18 | self._best_materials = best_materials 19 | self._next_experiments = next_experiments 20 | 21 | @property 22 | def best_materials(self): 23 | return self._best_materials 24 | 25 | @best_materials.setter 26 | def best_materials(self, value): 27 | self._best_materials = value 28 | 29 | @best_materials.deleter 30 | def best_materials(self): 31 | self._best_materials = None 32 | 33 | @property 34 | def next_experiments(self): 35 | return self._next_experiments 36 | 37 | @next_experiments.setter 38 | def next_experiments(self, value): 39 | self._next_experiments = value 40 | 41 | @next_experiments.deleter 42 | def next_experiments(self): 43 | self._next_experiments = None -------------------------------------------------------------------------------- /citrination_client/models/design/design_run.py: -------------------------------------------------------------------------------- 1 | class DesignRun(object): 2 | """ 3 | An in progress design run. Contains a UUID which is the identifier 4 | for the run. 5 | """ 6 | 7 | def __init__(self, uuid): 8 | """ 9 | Constructor. 10 | 11 | :param uuid: The UUID of an in progress design run. 12 | :type uuid: str 13 | """ 14 | self._uuid = uuid 15 | 16 | @property 17 | def uuid(self): 18 | return self._uuid 19 | 20 | @uuid.setter 21 | def uuid(self, value): 22 | self._uuid = value 23 | 24 | @uuid.deleter 25 | def uuid(self): 26 | self._uuid = None -------------------------------------------------------------------------------- /citrination_client/models/design/process_status.py: -------------------------------------------------------------------------------- 1 | class ProcessStatus(object): 2 | """ 3 | The status of an in progress process executing on Citrination. 4 | """ 5 | 6 | def __init__(self, result, progress, status, messages=None): 7 | """ 8 | Constructor. 9 | 10 | :param result: The result of the process 11 | :type result: any 12 | :param progress: The progress of the process as as percentage 13 | :type progress: int 14 | :param status: The status string for the process 15 | :type status: str 16 | :param messages: A list of messages representing the steps the process 17 | has already progressed through 18 | :type messages: list of str 19 | """ 20 | self._status = status 21 | self._result = result 22 | self._progress = progress 23 | self._messages = messages 24 | 25 | @property 26 | def uuid(self): 27 | return self._uuid 28 | 29 | @uuid.setter 30 | def uuid(self, value): 31 | self._uuid = value 32 | 33 | @uuid.deleter 34 | def uuid(self): 35 | self._uuid = None 36 | 37 | @property 38 | def result(self): 39 | return self._result 40 | 41 | @result.setter 42 | def result(self, value): 43 | self._result = value 44 | 45 | @result.deleter 46 | def result(self): 47 | self._result = None 48 | 49 | @property 50 | def progress(self): 51 | return self._progress 52 | 53 | @progress.setter 54 | def progress(self, value): 55 | self._progress = value 56 | 57 | @progress.deleter 58 | def progress(self): 59 | self._progress = None 60 | 61 | @property 62 | def status(self): 63 | return self._status 64 | 65 | @status.setter 66 | def status(self, value): 67 | self._status = value 68 | 69 | @status.deleter 70 | def status(self): 71 | self._status = None 72 | 73 | @property 74 | def messages(self): 75 | return self._messages 76 | 77 | @messages.setter 78 | def messages(self, value): 79 | self._messages = value 80 | 81 | @messages.deleter 82 | def messages(self): 83 | self._messages = None 84 | 85 | def finished(self): 86 | return self.status == "Finished" 87 | 88 | def killed(self): 89 | return self.status == "Killed" 90 | 91 | def accepted(self): 92 | return self.status == "Accepted" -------------------------------------------------------------------------------- /citrination_client/models/design/target.py: -------------------------------------------------------------------------------- 1 | from citrination_client.base.errors import CitrinationClientError 2 | 3 | 4 | class Target(object): 5 | """ 6 | The optimization target for a design run. Consists of 7 | the name of the output column to optimize and the objective 8 | (either "Max" or "Min", or a scalar value (such as "5.0")) 9 | """ 10 | 11 | def __init__(self, name, objective): 12 | """ 13 | Constructor. 14 | 15 | :param name: The name of the target output column 16 | :type name: str 17 | :param objective: The optimization objective; "Min", "Max", or a scalar value (such as "5.0") 18 | :type objective: str 19 | """ 20 | 21 | try: 22 | self._objective = float(objective) 23 | except ValueError: 24 | if objective.lower() not in ["max", "min"]: 25 | raise CitrinationClientError( 26 | "Target objective must either be \"min\" or \"max\"" 27 | ) 28 | self._objective = objective 29 | 30 | self._name = name 31 | 32 | def to_dict(self): 33 | return { 34 | "descriptor": self._name, 35 | "objective": self._objective 36 | } 37 | -------------------------------------------------------------------------------- /citrination_client/models/design/tests/test_target.py: -------------------------------------------------------------------------------- 1 | from citrination_client.base.errors import * 2 | from citrination_client.models.design import Target 3 | 4 | def test_target_initialization(): 5 | """ 6 | Tests that the design target must be initialized 7 | with an objective that is either Min or Max 8 | """ 9 | 10 | try: 11 | Target(name="Band gap", objective="asdf") 12 | assert False, "Target class should require that objective be one of Min or Max" 13 | except CitrinationClientError: 14 | pass 15 | 16 | # These initializations should not throw an error 17 | Target(name="Band gap", objective="Min") 18 | Target(name="Band gap", objective="Max") 19 | Target(name="Band gap", objective="5.0") 20 | -------------------------------------------------------------------------------- /citrination_client/models/event.py: -------------------------------------------------------------------------------- 1 | class Event(object): 2 | """ 3 | The representation of an in progress process. Described by a title, subtitle and subevent. 4 | """ 5 | 6 | def __init__(self, title, normalized_progress, subtitle=None, subevent=None): 7 | """ 8 | Constructor. 9 | 10 | :param title: The title of the event 11 | :type title: str 12 | :param subtitle: More detail about the event 13 | :type subtitle: str 14 | :param subevent: An event object describing the current state of the service's 15 | progress toward readiness 16 | :type subevent: Event 17 | :param normalized_progress: The fractional representation of the status of the event 18 | :type normalized_progress: float 19 | """ 20 | self._title = title 21 | self._subtitle = subtitle 22 | self._subevent = subevent 23 | self._normalized_progress = normalized_progress 24 | 25 | @property 26 | def title(self): 27 | return self._title 28 | 29 | @title.setter 30 | def title(self, value): 31 | self._title = value 32 | 33 | @title.deleter 34 | def title(self): 35 | self._title = None 36 | 37 | @property 38 | def subtitle(self): 39 | return self._subtitle 40 | 41 | @subtitle.setter 42 | def subtitle(self, value): 43 | self._subtitle = value 44 | 45 | @subtitle.deleter 46 | def subtitle(self): 47 | self._subtitle = None 48 | 49 | @property 50 | def subevent(self): 51 | return self._subevent 52 | 53 | @subevent.setter 54 | def subevent(self, value): 55 | self._subevent = value 56 | 57 | @subevent.deleter 58 | def subevent(self): 59 | self._subevent = None 60 | 61 | @property 62 | def normalized_progress(self): 63 | return self._normalized_progress 64 | 65 | @normalized_progress.setter 66 | def normalized_progress(self, value): 67 | self._normalized_progress = value 68 | 69 | @normalized_progress.deleter 70 | def normalized_progress(self): 71 | self._normalized_progress = None 72 | -------------------------------------------------------------------------------- /citrination_client/models/predicted_value.py: -------------------------------------------------------------------------------- 1 | class PredictedValue(object): 2 | """ 3 | The value/loss output from a prediction. 4 | """ 5 | 6 | def __init__(self, key, value, loss=None, class_probabilities=None): 7 | """ 8 | Constructor. 9 | 10 | :param key: The descriptor key for the prediction 11 | :type key: str 12 | :param value: The predicted value 13 | :type value: str or float 14 | :param loss: The loss for the prediction 15 | :type loss: float 16 | :param class_probabilities: The probabilities for categoricals 17 | :type class_probabilities: dict 18 | """ 19 | self._key = key 20 | self._value = value 21 | self._loss = loss 22 | self._class_probabilities = class_probabilities 23 | 24 | @property 25 | def key(self): 26 | return self._key 27 | 28 | @key.setter 29 | def key(self, value): 30 | self._key = value 31 | 32 | @key.deleter 33 | def key(self): 34 | self._key = None 35 | 36 | @property 37 | def value(self): 38 | return self._value 39 | 40 | @value.setter 41 | def value(self, value): 42 | self._value = value 43 | 44 | @value.deleter 45 | def value(self): 46 | self._value = None 47 | 48 | @property 49 | def loss(self): 50 | return self._loss 51 | 52 | @loss.setter 53 | def loss(self, value): 54 | self._loss = value 55 | 56 | @loss.deleter 57 | def loss(self): 58 | self._loss = None 59 | 60 | @property 61 | def class_probabilities(self): 62 | return self._class_probabilities 63 | -------------------------------------------------------------------------------- /citrination_client/models/prediction_result.py: -------------------------------------------------------------------------------- 1 | class PredictionResult(object): 2 | """ 3 | The collection of predicted values resulting from a prediction. 4 | """ 5 | 6 | def __init__(self): 7 | self._values = {} 8 | 9 | def add_value(self, key, value): 10 | """ 11 | Registers a predicted value in the result. 12 | 13 | :param key: The descriptor key for the predicted value 14 | :type key: str 15 | :param value: A :class:`PredictedValue` 16 | :type value: object 17 | :return: None 18 | :rtype: NoneType 19 | """ 20 | self._values[key] = value 21 | 22 | def get_value(self, key): 23 | """ 24 | Retrieves a predicted value. 25 | 26 | :param key: A descriptor key for a registered predicted value. 27 | :type key: str 28 | :return: The value stored at the provided descriptor key. None if no key is provided. 29 | :rtype: :class:`PredictedValue` 30 | """ 31 | try: 32 | return self._values[key] 33 | except KeyError: 34 | return None 35 | 36 | def all_keys(self): 37 | """ 38 | Retrieves a list of all the values which were predicted. 39 | 40 | :return: A list of keys for which predictions have been made and can 41 | be retrieved using `get_value` 42 | :rtype: list of str 43 | """ 44 | 45 | return self._values.keys() -------------------------------------------------------------------------------- /citrination_client/models/projection.py: -------------------------------------------------------------------------------- 1 | class Projection(object): 2 | """ 3 | A projection to be included in the TSNE analysis. 4 | """ 5 | 6 | def __init__(self, xs, ys, responses, tags, uids): 7 | """ 8 | Constructor. 9 | 10 | :param xs: A list of x values of the projection. 11 | :type xs: list of floats 12 | :param ys: A list of y values of the projection. 13 | :type ys: list of floats 14 | :param responses: A list of z values of the projection. 15 | :type responses: list of floats 16 | :param tags: A list of tags for the projected points 17 | :type tags: list of strings 18 | :param uids: A list of record UIDs for the projected points 19 | :type uids: list of strings 20 | """ 21 | self._xs = xs 22 | self._ys = ys 23 | self._responses = responses 24 | self._tags = tags 25 | self._uids = uids 26 | 27 | @property 28 | def xs(self): 29 | return self._xs 30 | 31 | @property 32 | def ys(self): 33 | return self._ys 34 | 35 | @property 36 | def responses(self): 37 | return self._responses 38 | 39 | @property 40 | def tags(self): 41 | return self._tags 42 | 43 | @property 44 | def uids(self): 45 | return self._uids -------------------------------------------------------------------------------- /citrination_client/models/routes.py: -------------------------------------------------------------------------------- 1 | def data_analysis(view_id): 2 | return 'data_views/{}/data_analysis'.format(view_id) 3 | 4 | def submit_data_view_design(data_view_id): 5 | return "data_views/{}/experimental_design".format(data_view_id) 6 | 7 | def get_data_view_design_status(data_view_id, run_uuid): 8 | return "data_views/{}/experimental_design/{}/status".format(data_view_id, run_uuid) 9 | 10 | def get_data_view_design_results(data_view_id, run_uuid): 11 | return "data_views/{}/experimental_design/{}/results".format(data_view_id, run_uuid) 12 | 13 | def kill_data_view_design_run(data_view_id, run_uuid): 14 | return "data_views/{}/experimental_design/{}".format(data_view_id, run_uuid) 15 | 16 | def get_data_view_status(data_view_id): 17 | """ 18 | URL for retrieving the statuses of all services 19 | associated with a data view. 20 | 21 | :param data_view_id: The ID of the desired data views 22 | :type data_view_id: str 23 | """ 24 | return "data_views/{}/status".format(data_view_id) 25 | -------------------------------------------------------------------------------- /citrination_client/models/tests/test_event.py: -------------------------------------------------------------------------------- 1 | from citrination_client.models import Event 2 | 3 | 4 | def test_can_initialize_event_without_subevent(): 5 | title = "test" 6 | subtitle = "test2" 7 | progress = 0.5 8 | 9 | e = Event( 10 | title = title, 11 | subtitle = subtitle, 12 | normalized_progress = progress 13 | ) 14 | 15 | assert e.title == title 16 | assert e.subtitle == subtitle 17 | assert e.normalized_progress == progress 18 | -------------------------------------------------------------------------------- /citrination_client/models/tests/test_predicted_value.py: -------------------------------------------------------------------------------- 1 | from citrination_client.models import PredictedValue 2 | 3 | def test_can_crud_key(): 4 | """ 5 | Tests that full get/set/delete functionality is 6 | available for the key property 7 | """ 8 | original_key = "my_key" 9 | original_value = 2 10 | original_loss = 0.3 11 | pv = PredictedValue(original_key, original_value, loss=original_loss) 12 | key = "other_key" 13 | assert pv.key is original_key 14 | pv.key = key 15 | assert pv.key is key 16 | del(pv.key) 17 | assert pv.key is None 18 | 19 | def test_can_crud_value(): 20 | """ 21 | Tests that full get/set/delete functionality is 22 | available for the value property 23 | """ 24 | original_key = "my_key" 25 | original_value = 2 26 | original_loss = 0.3 27 | pv = PredictedValue(original_key, original_value, loss=original_loss) 28 | value = 3 29 | assert pv.value is original_value 30 | pv.value = value 31 | assert pv.value is value 32 | del(pv.value) 33 | assert pv.value is None 34 | 35 | def test_can_crud_loss(): 36 | """ 37 | Tests that full get/set/delete functionality is 38 | available for the loss property 39 | """ 40 | original_key = "my_key" 41 | original_value = 2 42 | original_loss = 0.3 43 | pv = PredictedValue(original_key, original_value, loss=original_loss) 44 | loss = 3 45 | assert pv.loss is original_loss 46 | pv.loss = loss 47 | assert pv.loss is loss 48 | del(pv.loss) 49 | assert pv.loss is None -------------------------------------------------------------------------------- /citrination_client/models/tests/test_prediction_result.py: -------------------------------------------------------------------------------- 1 | from citrination_client.models import PredictionResult 2 | from citrination_client.models import PredictedValue 3 | 4 | def test_can_list_all_stored_predicted_values(): 5 | """ 6 | Tests that for a prediction result which stores many 7 | predicted values, all_keys correctly lists the keys under 8 | which each of those values is registered 9 | """ 10 | 11 | k1 = "Property Band Gap" 12 | v1 = 1.0 13 | pv1 = PredictedValue(k1, v1) 14 | 15 | k2 = "Property Color" 16 | v2 = 1.0 17 | pv2 = PredictedValue(k2, v2) 18 | 19 | k3 = "Property Transmission" 20 | v3 = 1.0 21 | pv3 = PredictedValue(k3, v3) 22 | 23 | pr = PredictionResult() 24 | 25 | pr.add_value(k1, pv1) 26 | pr.add_value(k2, pv2) 27 | pr.add_value(k3, pv3) 28 | 29 | assert k1 in pr.all_keys() 30 | assert k2 in pr.all_keys() 31 | assert k3 in pr.all_keys() -------------------------------------------------------------------------------- /citrination_client/models/tsne.py: -------------------------------------------------------------------------------- 1 | class Tsne(object): 2 | """ 3 | A TSNE analysis which can be extracted from the data analysis 4 | for a trained model on Citrination. 5 | """ 6 | 7 | def __init__(self): 8 | """ 9 | Constructor 10 | """ 11 | self._projections = {} 12 | 13 | def add_projection(self, key, projection): 14 | """ 15 | Register a projection under a descriptor key. 16 | 17 | :param key: The descriptor key for the projection 18 | :type key: str 19 | :param projection: The projection for the provided descriptor key 20 | :type projection: :class:`Projection` 21 | """ 22 | self._projections[key] = projection 23 | 24 | def projections(self): 25 | """ 26 | List the descriptor keys with registered projections. 27 | 28 | :return: List of descriptor keys 29 | """ 30 | return self._projections.keys() 31 | 32 | def get_projection(self, key): 33 | """ 34 | Retrieves the projection registered under a particular 35 | descriptor key. 36 | 37 | :param key: A descriptor key 38 | :return: A :class:`Projection` 39 | """ 40 | return self._projections.get(key) -------------------------------------------------------------------------------- /citrination_client/search/core/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /citrination_client/search/core/query/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CitrineInformatics/python-citrination-client/2cda4dd2a600224acd4e19ae2df66a922dd93342/citrination_client/search/core/query/__init__.py -------------------------------------------------------------------------------- /citrination_client/search/core/query/boolean_filter.py: -------------------------------------------------------------------------------- 1 | from pypif.util.serializable import Serializable 2 | 3 | 4 | class BooleanFilter(Serializable): 5 | """ 6 | Boolean filter that can be applied to any field. 7 | """ 8 | 9 | def __init__(self, logic=None, weight=None, exists=None, equal=None, filter=None, **kwargs): 10 | """ 11 | Constructor. 12 | 13 | :param logic: Logic for this filter. Must be equal to one of "MUST", "MUST_NOT", "SHOULD", or "OPTIONAL". 14 | :param weight: Weight for the filter. 15 | :param exists: True/False to simply test whether the field exists and has a non-null value. 16 | :param equal: String with the phrase to match against. 17 | :param filter: List of :class:`BooleanFilter` objects with sub-filters. 18 | """ 19 | self._logic = None 20 | self.logic = logic 21 | self._weight = None 22 | self.weight = weight 23 | self._exists = None 24 | self.exists = exists 25 | self._equal = None 26 | self.equal = equal 27 | self._filter = None 28 | self.filter = filter 29 | 30 | @property 31 | def logic(self): 32 | return self._logic 33 | 34 | @logic.setter 35 | def logic(self, logic): 36 | self._logic = logic 37 | 38 | @logic.deleter 39 | def logic(self): 40 | self._logic = None 41 | 42 | @property 43 | def weight(self): 44 | return self._weight 45 | 46 | @weight.setter 47 | def weight(self, weight): 48 | self._weight = weight 49 | 50 | @weight.deleter 51 | def weight(self): 52 | self._weight = None 53 | 54 | @property 55 | def exists(self): 56 | return self._exists 57 | 58 | @exists.setter 59 | def exists(self, exists): 60 | self._exists = exists 61 | 62 | @exists.deleter 63 | def exists(self): 64 | self._exists = None 65 | 66 | @property 67 | def equal(self): 68 | return self._equal 69 | 70 | @equal.setter 71 | def equal(self, equal): 72 | self._equal = equal 73 | 74 | @equal.deleter 75 | def equal(self): 76 | self._equal = None 77 | 78 | @property 79 | def filter(self): 80 | return self._filter 81 | 82 | @filter.setter 83 | def filter(self, filter): 84 | self._filter = self._get_object(BooleanFilter, filter) 85 | 86 | @filter.deleter 87 | def filter(self): 88 | self._filter = None 89 | -------------------------------------------------------------------------------- /citrination_client/search/core/query/data_scope.py: -------------------------------------------------------------------------------- 1 | from pypif.util.serializable import Serializable 2 | 3 | from citrination_client.search.core.query.data_query import DataQuery 4 | 5 | 6 | class DataScope(Serializable): 7 | """ 8 | Query to against data. 9 | """ 10 | 11 | def __init__(self, query=None, **kwargs): 12 | """ 13 | Constructor. 14 | 15 | :param query: One or more :class:`DataQuery` objects with the queries to run. 16 | """ 17 | self._query = None 18 | self.query = query 19 | 20 | @property 21 | def query(self): 22 | return self._query 23 | 24 | @query.setter 25 | def query(self, query): 26 | self._query = self._get_object(DataQuery, query) 27 | 28 | @query.deleter 29 | def query(self): 30 | self._query = None 31 | -------------------------------------------------------------------------------- /citrination_client/search/core/query/multi_query.py: -------------------------------------------------------------------------------- 1 | from pypif.util.serializable import Serializable 2 | 3 | 4 | class MultiQuery(Serializable): 5 | """ 6 | Base class for all multi-search requests. 7 | """ 8 | 9 | def __init__(self, queries=None, **kwargs): 10 | """ 11 | Constructor. 12 | 13 | :param queries: One or more queries to run. 14 | """ 15 | self._queries = None 16 | self.queries = queries 17 | 18 | @property 19 | def queries(self): 20 | return self._queries 21 | 22 | @queries.setter 23 | def queries(self, queries): 24 | self._queries = queries 25 | 26 | @queries.deleter 27 | def queries(self): 28 | self._queries = None 29 | -------------------------------------------------------------------------------- /citrination_client/search/core/result/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CitrineInformatics/python-citrination-client/2cda4dd2a600224acd4e19ae2df66a922dd93342/citrination_client/search/core/result/__init__.py -------------------------------------------------------------------------------- /citrination_client/search/core/result/base_search_result.py: -------------------------------------------------------------------------------- 1 | from pypif.util.serializable import Serializable 2 | 3 | 4 | class BaseSearchResult(Serializable): 5 | """ 6 | Base class for all search results. 7 | """ 8 | 9 | def __init__(self, took=None, total_num_hits=None, max_score=None, hits=None, **kwargs): 10 | """ 11 | Constructor. 12 | 13 | :param took: Number of milliseconds that the query took to execute. 14 | :param total_num_hits: Total number of hits. 15 | :param max_score: The maximum score. 16 | :param hits: List of hits. 17 | """ 18 | self._took = None 19 | self.took = took 20 | self._total_num_hits = None 21 | self.total_num_hits = total_num_hits 22 | self._max_score = None 23 | self.max_score = max_score 24 | self._hits = None 25 | self.hits = hits 26 | 27 | @property 28 | def took(self): 29 | return self._took 30 | 31 | @took.setter 32 | def took(self, took): 33 | self._took = took 34 | 35 | @took.deleter 36 | def took(self): 37 | self._took = None 38 | 39 | @property 40 | def total_num_hits(self): 41 | return self._total_num_hits 42 | 43 | @total_num_hits.setter 44 | def total_num_hits(self, total_num_hits): 45 | self._total_num_hits = total_num_hits 46 | 47 | @total_num_hits.deleter 48 | def total_num_hits(self): 49 | self._total_num_hits = None 50 | 51 | @property 52 | def max_score(self): 53 | return self._max_score 54 | 55 | @max_score.setter 56 | def max_score(self, max_score): 57 | self._max_score = max_score 58 | 59 | @max_score.deleter 60 | def max_score(self): 61 | self._max_score = None 62 | 63 | @property 64 | def hits(self): 65 | return self._hits 66 | 67 | @hits.setter 68 | def hits(self, hits): 69 | self._hits = hits 70 | 71 | @hits.deleter 72 | def hits(self): 73 | self._hits = None 74 | -------------------------------------------------------------------------------- /citrination_client/search/dataset/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CitrineInformatics/python-citrination-client/2cda4dd2a600224acd4e19ae2df66a922dd93342/citrination_client/search/dataset/__init__.py -------------------------------------------------------------------------------- /citrination_client/search/dataset/query/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CitrineInformatics/python-citrination-client/2cda4dd2a600224acd4e19ae2df66a922dd93342/citrination_client/search/dataset/query/__init__.py -------------------------------------------------------------------------------- /citrination_client/search/dataset/query/dataset_returning_query.py: -------------------------------------------------------------------------------- 1 | from citrination_client.search.core.query.base_returning_query import BaseReturningQuery 2 | 3 | 4 | class DatasetReturningQuery(BaseReturningQuery): 5 | """ 6 | Query used to return information about datasets. Citrination does not support pagination past the 50,000th result. Please chose values for from_index and size that do not exceed this limit. 7 | """ 8 | 9 | def __init__(self, query=None, from_index=None, size=None, random_results=None, random_seed=None, 10 | score_relevance=None, return_max_score=None, timeout=None, count_pifs=None, **kwargs): 11 | """ 12 | Constructor. 13 | 14 | :param query: One or more :class:`DataQuery` objects with the queries to run. 15 | :param from_index: Index of the first hit that should be returned. 16 | :param size: Total number of hits the should be returned. 17 | :param random_results: Whether to return a random set of records. 18 | :param random_seed: The random seed to use. 19 | :param score_relevance: Whether to use relevance scoring. 20 | :param return_max_score: Whether to return the maximum score. 21 | :param timeout: The number of milliseconds to wait for the query to execute. 22 | :param count_pifs: Whether to return counts of PIFs for each dataset. 23 | """ 24 | super(DatasetReturningQuery, self).__init__( 25 | query=query, from_index=from_index, size=size, random_results=random_results, random_seed=random_seed, 26 | score_relevance=score_relevance, return_max_score=return_max_score, timeout=timeout, **kwargs) 27 | self._count_pifs = None 28 | self.count_pifs = count_pifs 29 | 30 | @property 31 | def count_pifs(self): 32 | return self._count_pifs 33 | 34 | @count_pifs.setter 35 | def count_pifs(self, count_pifs): 36 | self._count_pifs = count_pifs 37 | 38 | @count_pifs.deleter 39 | def count_pifs(self): 40 | self._count_pifs = None 41 | -------------------------------------------------------------------------------- /citrination_client/search/dataset/result/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CitrineInformatics/python-citrination-client/2cda4dd2a600224acd4e19ae2df66a922dd93342/citrination_client/search/dataset/result/__init__.py -------------------------------------------------------------------------------- /citrination_client/search/dataset/result/dataset_multi_search_result.py: -------------------------------------------------------------------------------- 1 | from pypif.util.serializable import Serializable 2 | 3 | from citrination_client.search.dataset.result.dataset_multi_search_result_element import DatasetMultiSearchResultElement 4 | 5 | 6 | class DatasetMultiSearchResult(Serializable): 7 | """ 8 | Class to store the results of a dataset multi-query. 9 | """ 10 | 11 | def __init__(self, took=None, results=None, **kwargs): 12 | """ 13 | Constructor. 14 | 15 | :param took: Number of milliseconds that the query took to execute. 16 | :param results: List of :class:`DatasetMultiSearchResultElement` objects. 17 | """ 18 | self._took = None 19 | self.took = took 20 | self._results = None 21 | self.results = results 22 | 23 | @property 24 | def took(self): 25 | return self._took 26 | 27 | @took.setter 28 | def took(self, took): 29 | self._took = took 30 | 31 | @took.deleter 32 | def took(self): 33 | self._took = None 34 | 35 | @property 36 | def results(self): 37 | return self._results 38 | 39 | @results.setter 40 | def results(self, results): 41 | self._results = self._get_object(DatasetMultiSearchResultElement, results) 42 | 43 | @results.deleter 44 | def results(self): 45 | self._results = None 46 | -------------------------------------------------------------------------------- /citrination_client/search/dataset/result/dataset_multi_search_result_element.py: -------------------------------------------------------------------------------- 1 | from pypif.util.serializable import Serializable 2 | 3 | from citrination_client.search.dataset.result.dataset_search_result import DatasetSearchResult 4 | 5 | 6 | class DatasetMultiSearchResultElement(Serializable): 7 | """ 8 | A single search result as a part of a dataset multi-query. 9 | """ 10 | 11 | def __init__(self, result=None, status=None, **kwargs): 12 | """ 13 | Constructor. 14 | 15 | :param result: A single :class:`DatasetSearchResult` object with the query results. 16 | :param status: 'SUCCESS', 'ERROR', or 'NOT_EXECUTED'. 17 | """ 18 | self._result = None 19 | self.result = result 20 | self._status = None 21 | self.status = status 22 | 23 | @property 24 | def result(self): 25 | return self._result 26 | 27 | @result.setter 28 | def result(self, result): 29 | self._result = self._get_object(DatasetSearchResult, result) 30 | 31 | @result.deleter 32 | def result(self): 33 | self._result = None 34 | 35 | @property 36 | def status(self): 37 | return self._status 38 | 39 | @status.setter 40 | def status(self, status): 41 | self._status = status 42 | 43 | @status.deleter 44 | def status(self): 45 | self._status = None 46 | -------------------------------------------------------------------------------- /citrination_client/search/dataset/result/dataset_search_result.py: -------------------------------------------------------------------------------- 1 | from citrination_client.search.core.result.base_search_result import BaseSearchResult 2 | from citrination_client.search.dataset.result.dataset_search_hit import DatasetSearchHit 3 | 4 | 5 | class DatasetSearchResult(BaseSearchResult): 6 | """ 7 | Class to store the results of a dataset query. 8 | """ 9 | 10 | def __init__(self, took=None, total_num_hits=None, max_score=None, hits=None, **kwargs): 11 | """ 12 | Constructor. 13 | 14 | :param took: Number of milliseconds that the query took to execute. 15 | :param total_num_hits: Total number of hits. 16 | :param max_score: The maximum score. 17 | :param hits: List of :class:`DatasetSearchHit` objects. 18 | """ 19 | super(DatasetSearchResult, self).__init__( 20 | took=took, total_num_hits=total_num_hits, max_score=max_score, 21 | hits=self._get_object(DatasetSearchHit, hits), **kwargs) 22 | -------------------------------------------------------------------------------- /citrination_client/search/dataset/tests/test_dataset_query.py: -------------------------------------------------------------------------------- 1 | from os import environ 2 | import unittest 3 | import pytest 4 | 5 | from citrination_client import * 6 | from citrination_client.search.client import MAX_QUERY_DEPTH 7 | from citrination_client.base.errors import CitrinationClientError 8 | 9 | 10 | class TestDatasetQuery(unittest.TestCase): 11 | @classmethod 12 | def setup_class(cls): 13 | cls.client = CitrinationClient(environ['CITRINATION_API_KEY'], environ['CITRINATION_SITE']).search 14 | 15 | @pytest.mark.skipif(environ['CITRINATION_SITE'] != "https://citrination.com", 16 | reason="Dataset search test only supported on public") 17 | def test_full_dataset_query(self): 18 | """Test a public dataset query with every option on""" 19 | query = DatasetReturningQuery( 20 | size=8, 21 | score_relevance=True, 22 | count_pifs=True) 23 | result = self.client.dataset_search(query) 24 | 25 | assert len(result.hits) == 8, "Number of hits didn't match query size" 26 | assert result.took > 0, "Result shouldn't have been instant" 27 | assert result.total_num_hits > 10000, "The number of results is too low" 28 | for hit in result.hits: 29 | assert len(hit.id) > 0, "Returned empty id" 30 | assert len(hit.name) > 0, "Returned empty name" 31 | assert len(hit.email) > 0, "Returned empty email" 32 | assert hit.num_pifs >= 0, "Dataset had no pifs" 33 | assert hit.score is not None, "Score is not returned" 34 | 35 | @pytest.mark.skipif(environ['CITRINATION_SITE'] != "https://citrination.com", 36 | reason="Dataset search test only supported on public") 37 | def test_dataset_search(self): 38 | """Test that a basic query with a dataset ID returns 1 hit""" 39 | response = self.client.dataset_search(DatasetReturningQuery( 40 | size=0, 41 | query=DataQuery( 42 | dataset=DatasetQuery( 43 | id=Filter(equal='150675'))))) 44 | assert 1 == response.total_num_hits 45 | 46 | def test_search_limit_enforced_dataset_search(self): 47 | """ 48 | Tests that if a user tries to access more than the max allowed results an error is thrown 49 | """ 50 | query = DatasetReturningQuery(from_index=MAX_QUERY_DEPTH, size=10) 51 | with pytest.raises(CitrinationClientError): 52 | self.client.dataset_search(query) 53 | 54 | def test_timeout(self): 55 | """Test that timeouts are sent properly. This request should fail with an exception.""" 56 | with self.assertRaises(RequestTimeoutException): 57 | self.client.dataset_search(DatasetReturningQuery(timeout=0, size=10)) 58 | -------------------------------------------------------------------------------- /citrination_client/search/file/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CitrineInformatics/python-citrination-client/2cda4dd2a600224acd4e19ae2df66a922dd93342/citrination_client/search/file/__init__.py -------------------------------------------------------------------------------- /citrination_client/search/file/query/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CitrineInformatics/python-citrination-client/2cda4dd2a600224acd4e19ae2df66a922dd93342/citrination_client/search/file/query/__init__.py -------------------------------------------------------------------------------- /citrination_client/search/file/query/file_returning_query.py: -------------------------------------------------------------------------------- 1 | from citrination_client.search.core.query.base_returning_query import BaseReturningQuery 2 | 3 | 4 | class FileReturningQuery(BaseReturningQuery): 5 | """ 6 | Query used to return information about files. 7 | """ 8 | 9 | def __init__(self, query=None, from_index=None, size=None, random_results=None, random_seed=None, 10 | score_relevance=None, return_max_score=None, timeout=None, max_content_highlights=None, 11 | highlight_pre_tag=None, highlight_post_tag=None, **kwargs): 12 | """ 13 | Constructor. 14 | 15 | :param query: One or more :class:`DataQuery` objects with the queries to run. 16 | :param from_index: Index of the first hit that should be returned. 17 | :param size: Total number of hits the should be returned. 18 | :param random_results: Whether to return a random set of records. 19 | :param random_seed: The random seed to use. 20 | :param score_relevance: Whether to use relevance scoring. 21 | :param return_max_score: Whether to return the maximum score. 22 | :param timeout: The number of milliseconds to wait for the query to execute. 23 | :param max_content_highlights: The maximum number of highlighted results to return. 24 | :param highlight_pre_tag: The tag to use at the beginning of a highlight. 25 | :param highlight_post_tag: The tag to use at the end of a highlight. 26 | """ 27 | super(FileReturningQuery, self).__init__( 28 | query=query, from_index=from_index, size=size, random_results=random_results, random_seed=random_seed, 29 | score_relevance=score_relevance, return_max_score=return_max_score, timeout=timeout, **kwargs) 30 | self._max_content_highlights = None 31 | self.max_content_highlights = max_content_highlights 32 | self._highlight_pre_tag = None 33 | self.highlight_pre_tag = highlight_pre_tag 34 | self._highlight_post_tag = None 35 | self.highlight_post_tag = highlight_post_tag 36 | 37 | @property 38 | def max_content_highlights(self): 39 | return self._max_content_highlights 40 | 41 | @max_content_highlights.setter 42 | def max_content_highlights(self, max_content_highlights): 43 | self._max_content_highlights = max_content_highlights 44 | 45 | @max_content_highlights.deleter 46 | def max_content_highlights(self): 47 | self._max_content_highlights = None 48 | 49 | @property 50 | def highlight_pre_tag(self): 51 | return self._highlight_pre_tag 52 | 53 | @highlight_pre_tag.setter 54 | def highlight_pre_tag(self, highlight_pre_tag): 55 | self._highlight_pre_tag = highlight_pre_tag 56 | 57 | @highlight_pre_tag.deleter 58 | def highlight_pre_tag(self): 59 | self._highlight_pre_tag = None 60 | 61 | @property 62 | def highlight_post_tag(self): 63 | return self._highlight_post_tag 64 | 65 | @highlight_post_tag.setter 66 | def highlight_post_tag(self, highlight_post_tag): 67 | self._highlight_post_tag = highlight_post_tag 68 | 69 | @highlight_post_tag.deleter 70 | def highlight_post_tag(self): 71 | self._highlight_post_tag = None 72 | -------------------------------------------------------------------------------- /citrination_client/search/file/result/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CitrineInformatics/python-citrination-client/2cda4dd2a600224acd4e19ae2df66a922dd93342/citrination_client/search/file/result/__init__.py -------------------------------------------------------------------------------- /citrination_client/search/file/result/file_multi_search_result.py: -------------------------------------------------------------------------------- 1 | from pypif.util.serializable import Serializable 2 | 3 | from citrination_client.search.file.result.file_multi_search_result_element import FileMultiSearchResultElement 4 | 5 | 6 | class FileMultiSearchResult(Serializable): 7 | """ 8 | Class to store the results of a file multi-query. 9 | """ 10 | 11 | def __init__(self, took=None, results=None, **kwargs): 12 | """ 13 | Constructor. 14 | 15 | :param took: Number of milliseconds that the query took to execute. 16 | :param results: List of :class:`FileMultiSearchResultElement` objects. 17 | """ 18 | self._took = None 19 | self.took = took 20 | self._results = None 21 | self.results = results 22 | 23 | @property 24 | def took(self): 25 | return self._took 26 | 27 | @took.setter 28 | def took(self, took): 29 | self._took = took 30 | 31 | @took.deleter 32 | def took(self): 33 | self._took = None 34 | 35 | @property 36 | def results(self): 37 | return self._results 38 | 39 | @results.setter 40 | def results(self, results): 41 | self._results = self._get_object(FileMultiSearchResultElement, results) 42 | 43 | @results.deleter 44 | def results(self): 45 | self._results = None 46 | -------------------------------------------------------------------------------- /citrination_client/search/file/result/file_multi_search_result_element.py: -------------------------------------------------------------------------------- 1 | from pypif.util.serializable import Serializable 2 | 3 | from citrination_client.search.file.result.file_search_result import FileSearchResult 4 | 5 | 6 | class FileMultiSearchResultElement(Serializable): 7 | """ 8 | A single search result as a part of a dataset multi-query. 9 | """ 10 | 11 | def __init__(self, result=None, status=None, **kwargs): 12 | """ 13 | Constructor. 14 | 15 | :param result: A single :class:`FileSearchResult` object with the query results. 16 | :param status: 'SUCCESS', 'ERROR', or 'NOT_EXECUTED'. 17 | """ 18 | self._result = None 19 | self.result = result 20 | self._status = None 21 | self.status = status 22 | 23 | @property 24 | def result(self): 25 | return self._result 26 | 27 | @result.setter 28 | def result(self, result): 29 | self._result = self._get_object(FileSearchResult, result) 30 | 31 | @result.deleter 32 | def result(self): 33 | self._result = None 34 | 35 | @property 36 | def status(self): 37 | return self._status 38 | 39 | @status.setter 40 | def status(self, status): 41 | self._status = status 42 | 43 | @status.deleter 44 | def status(self): 45 | self._status = None 46 | -------------------------------------------------------------------------------- /citrination_client/search/file/result/file_search_result.py: -------------------------------------------------------------------------------- 1 | from citrination_client.search.core.result.base_search_result import BaseSearchResult 2 | from citrination_client.search.file.result.file_search_hit import FileSearchHit 3 | 4 | 5 | class FileSearchResult(BaseSearchResult): 6 | """ 7 | Class to store the results of a file query. 8 | """ 9 | 10 | def __init__(self, took=None, total_num_hits=None, max_score=None, hits=None, **kwargs): 11 | """ 12 | Constructor. 13 | 14 | :param took: Number of milliseconds that the query took to execute. 15 | :param total_num_hits: Total number of hits. 16 | :param max_score: The maximum score. 17 | :param hits: List of :class:`FileSearchHit` objects. 18 | """ 19 | super(FileSearchResult, self).__init__( 20 | took=took, total_num_hits=total_num_hits, max_score=max_score, 21 | hits=self._get_object(FileSearchHit, hits), **kwargs) 22 | -------------------------------------------------------------------------------- /citrination_client/search/pif/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CitrineInformatics/python-citrination-client/2cda4dd2a600224acd4e19ae2df66a922dd93342/citrination_client/search/pif/__init__.py -------------------------------------------------------------------------------- /citrination_client/search/pif/query/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CitrineInformatics/python-citrination-client/2cda4dd2a600224acd4e19ae2df66a922dd93342/citrination_client/search/pif/query/__init__.py -------------------------------------------------------------------------------- /citrination_client/search/pif/query/chemical/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CitrineInformatics/python-citrination-client/2cda4dd2a600224acd4e19ae2df66a922dd93342/citrination_client/search/pif/query/chemical/__init__.py -------------------------------------------------------------------------------- /citrination_client/search/pif/query/chemical/chemical_field_query.py: -------------------------------------------------------------------------------- 1 | from citrination_client.search.pif.query.chemical.chemical_filter import ChemicalFilter 2 | from citrination_client.search.pif.query.core.base_field_query import BaseFieldQuery 3 | 4 | 5 | class ChemicalFieldQuery(BaseFieldQuery): 6 | """ 7 | Class for all field operations against chemical information. 8 | """ 9 | 10 | def __init__(self, sort=None, logic=None, weight=None, simple=None, simple_weight=None, extract_as=None, 11 | extract_all=None, extract_when_missing=None, length=None, offset=None, filter=None, **kwargs): 12 | """ 13 | Constructor. 14 | 15 | :param sort: ASCENDING or DESCENDING to set the sort order on this field. 16 | :param logic: Logic for this query. Must be equal to one of "MUST", "MUST_NOT", "SHOULD", or "OPTIONAL". 17 | :param weight: Weight for the query. 18 | :param simple: String with the simple search to run against all fields. 19 | :param simple_weight: Dictionary of relative paths to their weights for simple queries. 20 | :param extract_as: String with the alias to save this field under. 21 | :param extract_all: Boolean setting whether all values in an array should be extracted. 22 | :param extract_when_missing: Any valid JSON-supported object or PIF object. This value is returned when a value is missing that should be extracted (and the overall query is still satisfied). 23 | :param length: One or more :class:`FieldOperation` objects against the length field. 24 | :param offset: One or more :class:`FieldOperation` objects against the offset field. 25 | :param filter: One or more :class:`ChemicalFilter` objects against this field. 26 | """ 27 | super(ChemicalFieldQuery, self).__init__( 28 | sort=sort, weight=weight, logic=logic, simple=simple, simple_weight=simple_weight, extract_as=extract_as, 29 | extract_all=extract_all, extract_when_missing=extract_when_missing, length=length, offset=offset, **kwargs) 30 | self._filter = None 31 | self.filter = filter 32 | 33 | @property 34 | def filter(self): 35 | return self._filter 36 | 37 | @filter.setter 38 | def filter(self, filter): 39 | self._filter = self._get_object(ChemicalFilter, filter) 40 | 41 | @filter.deleter 42 | def filter(self): 43 | self._filter = None 44 | -------------------------------------------------------------------------------- /citrination_client/search/pif/query/core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CitrineInformatics/python-citrination-client/2cda4dd2a600224acd4e19ae2df66a922dd93342/citrination_client/search/pif/query/core/__init__.py -------------------------------------------------------------------------------- /citrination_client/search/pif/query/core/field_query.py: -------------------------------------------------------------------------------- 1 | from citrination_client.search.core.query.filter import Filter 2 | from citrination_client.search.pif.query.core.base_field_query import BaseFieldQuery 3 | 4 | 5 | class FieldQuery(BaseFieldQuery): 6 | """ 7 | Class for all field queries. 8 | """ 9 | 10 | def __init__(self, sort=None, weight=None, logic=None, simple=None, simple_weight=None, extract_as=None, 11 | extract_all=None, extract_when_missing=None, length=None, offset=None, filter=None, **kwargs): 12 | """ 13 | Constructor. 14 | 15 | :param sort: ASCENDING or DESCENDING to set the sort order on this field. 16 | :param weight: Weight of the query. 17 | :param logic: Logic for this query. Must be equal to one of "MUST", "MUST_NOT", "SHOULD", or "OPTIONAL". 18 | :param simple: String with the simple search to run against all fields. 19 | :param simple_weight: Dictionary of relative paths to their weights for simple queries. 20 | :param extract_as: String with the alias to save this field under. 21 | :param extract_all: Boolean setting whether all values in an array should be extracted. 22 | :param extract_when_missing: Any valid JSON-supported object or PIF object. This value is returned when a value is missing that should be extracted (and the overall query is still satisfied). 23 | :param length: One or more :class:`FieldQuery` objects against the length field. 24 | :param offset: One or more :class:`FieldQuery` objects against the offset field. 25 | :param filter: One or more :class:`Filter` objects against this field. 26 | """ 27 | super(FieldQuery, self).__init__( 28 | sort=sort, weight=weight, logic=logic, simple=simple, simple_weight=simple_weight, extract_as=extract_as, 29 | extract_all=extract_all, extract_when_missing=extract_when_missing, length=length, offset=offset, **kwargs) 30 | self._filter = None 31 | self.filter = filter 32 | 33 | @property 34 | def filter(self): 35 | return self._filter 36 | 37 | @filter.setter 38 | def filter(self, filter): 39 | self._filter = self._get_object(Filter, filter) 40 | 41 | @filter.deleter 42 | def filter(self): 43 | self._filter = None 44 | -------------------------------------------------------------------------------- /citrination_client/search/pif/query/core/id_query.py: -------------------------------------------------------------------------------- 1 | from citrination_client.search.pif.query.core.base_object_query import BaseObjectQuery 2 | from citrination_client.search.pif.query.core.field_query import FieldQuery 3 | 4 | 5 | class IdQuery(BaseObjectQuery): 6 | """ 7 | Class to query against a PIF ID object. 8 | """ 9 | 10 | def __init__(self, logic=None, weight=None, simple=None, simple_weight=None, extract_as=None, extract_all=None, 11 | extract_when_missing=None, tags=None, length=None, offset=None, name=None, value=None, 12 | query=None, **kwargs): 13 | """ 14 | Constructor. 15 | 16 | :param logic: Logic for this filter. Must be equal to one of "MUST", "MUST_NOT", "SHOULD", or "OPTIONAL". 17 | :param weight: Weight of the query. 18 | :param simple: String with the query to run over all fields. 19 | :param simple_weight: Dictionary of relative paths to their weights for simple queries. 20 | :param extract_as: String with the alias to save this field under. 21 | :param extract_all: Boolean setting whether all values in an array should be extracted. 22 | :param extract_when_missing: Any valid JSON-supported object or PIF object. This value is returned when a value is missing that should be extracted (and the overall query is still satisfied). 23 | :param tags: One or more :class:`FieldQuery` operations against the tags field. 24 | :param length: One or more :class:`FieldQuery` operations against the length field. 25 | :param offset: One or more :class:`FieldQuery` operations against the offset field. 26 | :param name: One or more :class:`FieldQuery` operations against the name field. 27 | :param value: One or more :class:`FieldQuery` operations against the value field. 28 | :param query: One or more :class:`IdQuery` objects with nested queries. 29 | """ 30 | super(IdQuery, self).__init__( 31 | logic=logic, weight=weight, simple=simple, simple_weight=simple_weight, extract_as=extract_as, 32 | extract_all=extract_all, extract_when_missing=extract_when_missing, tags=tags, length=length, 33 | offset=offset, **kwargs) 34 | self._name = None 35 | self.name = name 36 | self._value = None 37 | self.value = value 38 | self._query = None 39 | self.query = query 40 | 41 | @property 42 | def name(self): 43 | return self._name 44 | 45 | @name.setter 46 | def name(self, name): 47 | self._name = self._get_object(FieldQuery, name) 48 | 49 | @name.deleter 50 | def name(self): 51 | self._name = None 52 | 53 | @property 54 | def value(self): 55 | return self._value 56 | 57 | @value.setter 58 | def value(self, value): 59 | self._value = self._get_object(FieldQuery, value) 60 | 61 | @value.deleter 62 | def value(self): 63 | self._value = None 64 | 65 | @property 66 | def query(self): 67 | return self._query 68 | 69 | @query.setter 70 | def query(self, query): 71 | self._query = self._get_object(IdQuery, query) 72 | 73 | @query.deleter 74 | def query(self): 75 | self._query = None 76 | -------------------------------------------------------------------------------- /citrination_client/search/pif/query/core/pages_query.py: -------------------------------------------------------------------------------- 1 | from citrination_client.search.pif.query.core.base_object_query import BaseObjectQuery 2 | from citrination_client.search.pif.query.core.field_query import FieldQuery 3 | 4 | 5 | class PagesQuery(BaseObjectQuery): 6 | """ 7 | Class to query against a Pif Pages object. 8 | """ 9 | 10 | def __init__(self, logic=None, weight=None, simple=None, simple_weight=None, extract_as=None, extract_all=None, 11 | extract_when_missing=None, tags=None, length=None, offset=None, start=None, end=None, 12 | query=None, **kwargs): 13 | """ 14 | Constructor. 15 | 16 | :param logic: Logic for this filter. Must be equal to one of "MUST", "MUST_NOT", "SHOULD", or "OPTIONAL". 17 | :param weight: Weight of the query. 18 | :param simple: String with the simple query to run against all fields. 19 | :param simple_weight: Dictionary of relative paths to their weights for simple queries. 20 | :param extract_as: String with the alias to save this field under. 21 | :param extract_all: Boolean setting whether all values in an array should be extracted. 22 | :param extract_when_missing: Any valid JSON-supported object or PIF object. This value is returned when a value is missing that should be extracted (and the overall query is still satisfied). 23 | :param tags: One or more :class:`FieldQuery` operations against the tags field. 24 | :param length: One or more :class:`FieldQuery` operations against the length field. 25 | :param offset: One or more :class:`FieldQuery` operations against the offset field. 26 | :param start: One or more :class:`FieldQuery` operations against the starting page field. 27 | :param end: One or more :class:`FieldQuery` operations against the ending page field. 28 | :param query: One or more :class:`PagesQuery` objects with nested queries. 29 | """ 30 | super(PagesQuery, self).__init__( 31 | logic=logic, weight=weight, simple=simple, simple_weight=simple_weight, extract_as=extract_as, 32 | extract_all=extract_all, extract_when_missing=extract_when_missing, tags=tags, length=length, 33 | offset=offset, **kwargs) 34 | self._start = None 35 | self.start = start 36 | self._end = None 37 | self.end = end 38 | self._query = None 39 | self.query = query 40 | 41 | @property 42 | def start(self): 43 | return self._start 44 | 45 | @start.setter 46 | def start(self, start): 47 | self._start = start 48 | 49 | @start.deleter 50 | def start(self): 51 | self._start = None 52 | 53 | @property 54 | def end(self): 55 | return self._end 56 | 57 | @end.setter 58 | def end(self, end): 59 | self._end = self._get_object(FieldQuery, end) 60 | 61 | @end.deleter 62 | def end(self): 63 | self._end = None 64 | 65 | @property 66 | def query(self): 67 | return self._query 68 | 69 | @query.setter 70 | def query(self, query): 71 | self._query = self._get_object(PagesQuery, query) 72 | 73 | @query.deleter 74 | def query(self): 75 | self._query = None 76 | -------------------------------------------------------------------------------- /citrination_client/search/pif/query/core/source_query.py: -------------------------------------------------------------------------------- 1 | from citrination_client.search.pif.query.core.base_object_query import BaseObjectQuery 2 | from citrination_client.search.pif.query.core.field_query import FieldQuery 3 | 4 | 5 | class SourceQuery(BaseObjectQuery): 6 | """ 7 | Class to query against a PIF Source object. 8 | """ 9 | 10 | def __init__(self, logic=None, weight=None, simple=None, simple_weight=None, extract_as=None, extract_all=None, 11 | extract_when_missing=None, tags=None, length=None, offset=None, producer=None, url=None, 12 | query=None, **kwargs): 13 | """ 14 | Constructor. 15 | 16 | :param logic: Logic for this filter. Must be equal to one of "MUST", "MUST_NOT", "SHOULD", or "OPTIONAL". 17 | :param weight: Weight of the query. 18 | :param simple: String with the simple query to run against all fields. 19 | :param simple_weight: Dictionary of relative paths to their weights for simple queries. 20 | :param extract_as: String with the alias to save this field under. 21 | :param extract_all: Boolean setting whether all values in an array should be extracted. 22 | :param extract_when_missing: Any valid JSON-supported object or PIF object. This value is returned when a value is missing that should be extracted (and the overall query is still satisfied). 23 | :param tags: One or more :class:`FieldQuery` operations against the tags field. 24 | :param length: One or more :class:`FieldQuery` operations against the length field. 25 | :param offset: One or more :class:`FieldQuery` operations against the offset field. 26 | :param producer: One or more :class:`FieldQuery` operations against the producer field. 27 | :param url: One or more :class:`FieldQuery` operations against the url field. 28 | :param query: One or more :class:`SourceQuery` objects with nested queries. 29 | """ 30 | super(SourceQuery, self).__init__( 31 | logic=logic, weight=weight, simple=simple, simple_weight=simple_weight, extract_as=extract_as, 32 | extract_all=extract_all, extract_when_missing=extract_when_missing, tags=tags, length=length, 33 | offset=offset, **kwargs) 34 | self._producer = None 35 | self.producer = producer 36 | self._url = None 37 | self.url = url 38 | self._query = None 39 | self.query = query 40 | 41 | @property 42 | def producer(self): 43 | return self._producer 44 | 45 | @producer.setter 46 | def producer(self, producer): 47 | self._producer = self._get_object(FieldQuery, producer) 48 | 49 | @producer.deleter 50 | def producer(self): 51 | self._producer = None 52 | 53 | @property 54 | def url(self): 55 | return self._url 56 | 57 | @url.setter 58 | def url(self, url): 59 | self._url = self._get_object(FieldQuery, url) 60 | 61 | @url.deleter 62 | def url(self): 63 | self._url = None 64 | 65 | @property 66 | def query(self): 67 | return self._query 68 | 69 | @query.setter 70 | def query(self, query): 71 | self._query = self._get_object(SourceQuery, query) 72 | 73 | @query.deleter 74 | def query(self): 75 | self._query = None 76 | -------------------------------------------------------------------------------- /citrination_client/search/pif/query/extraction_sort.py: -------------------------------------------------------------------------------- 1 | from pypif.util.serializable import Serializable 2 | 3 | 4 | class ExtractionSort(Serializable): 5 | """ 6 | Class to store information about a sort on an extracted field. 7 | """ 8 | 9 | def __init__(self, key=None, order=None, **kwargs): 10 | """ 11 | Constructor. 12 | 13 | :param key: String with the key that will be sorted on. 14 | :param order: The order to use. Either ASCENDING or DESCENDING. 15 | """ 16 | self._key = None 17 | self.key = key 18 | self._order = None 19 | self.order = order 20 | 21 | @property 22 | def key(self): 23 | return self._key 24 | 25 | @key.setter 26 | def key(self, key): 27 | self._key = key 28 | 29 | @key.deleter 30 | def key(self): 31 | self._key = None 32 | 33 | @property 34 | def order(self): 35 | return self._order 36 | 37 | @order.setter 38 | def order(self, order): 39 | self._order = order 40 | 41 | @order.deleter 42 | def order(self): 43 | self._order = None 44 | -------------------------------------------------------------------------------- /citrination_client/search/pif/result/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CitrineInformatics/python-citrination-client/2cda4dd2a600224acd4e19ae2df66a922dd93342/citrination_client/search/pif/result/__init__.py -------------------------------------------------------------------------------- /citrination_client/search/pif/result/pif_multi_search_result.py: -------------------------------------------------------------------------------- 1 | from pypif.util.serializable import Serializable 2 | 3 | from citrination_client.search.pif.result.pif_multi_search_result_element import PifMultiSearchResultElement 4 | 5 | 6 | class PifMultiSearchResult(Serializable): 7 | """ 8 | Class to store the results of a PIF multi-query. 9 | """ 10 | 11 | def __init__(self, took=None, results=None, **kwargs): 12 | """ 13 | Constructor. 14 | 15 | :param took: Number of milliseconds that the query took to execute. 16 | :param results: List of :class:`PifMultiSearchResultElement` objects. 17 | """ 18 | self._took = None 19 | self.took = took 20 | self._results = None 21 | self.results = results 22 | 23 | @property 24 | def took(self): 25 | return self._took 26 | 27 | @took.setter 28 | def took(self, took): 29 | self._took = took 30 | 31 | @took.deleter 32 | def took(self): 33 | self._took = None 34 | 35 | @property 36 | def results(self): 37 | return self._results 38 | 39 | @results.setter 40 | def results(self, results): 41 | self._results = self._get_object(PifMultiSearchResultElement, results) 42 | 43 | @results.deleter 44 | def results(self): 45 | self._results = None 46 | -------------------------------------------------------------------------------- /citrination_client/search/pif/result/pif_multi_search_result_element.py: -------------------------------------------------------------------------------- 1 | from pypif.util.serializable import Serializable 2 | 3 | from citrination_client.search.pif.result.pif_search_result import PifSearchResult 4 | 5 | 6 | class PifMultiSearchResultElement(Serializable): 7 | """ 8 | A single search result as a part of a pif multi-query. 9 | """ 10 | 11 | def __init__(self, result=None, status=None, **kwargs): 12 | """ 13 | Constructor. 14 | 15 | :param result: A single :class:`PifSearchResult` object with the query results. 16 | :param status: 'SUCCESS', 'ERROR', or 'NOT_EXECUTED'. 17 | """ 18 | self._result = None 19 | self.result = result 20 | self._status = None 21 | self.status = status 22 | 23 | @property 24 | def result(self): 25 | return self._result 26 | 27 | @result.setter 28 | def result(self, result): 29 | self._result = self._get_object(PifSearchResult, result) 30 | 31 | @result.deleter 32 | def result(self): 33 | self._result = None 34 | 35 | @property 36 | def status(self): 37 | return self._status 38 | 39 | @status.setter 40 | def status(self, status): 41 | self._status = status 42 | 43 | @status.deleter 44 | def status(self): 45 | self._status = None 46 | -------------------------------------------------------------------------------- /citrination_client/search/pif/result/pif_search_result.py: -------------------------------------------------------------------------------- 1 | from citrination_client.search.core.result.base_search_result import BaseSearchResult 2 | from citrination_client.search.pif.result.pif_search_hit import PifSearchHit 3 | 4 | 5 | class PifSearchResult(BaseSearchResult): 6 | """ 7 | Class to store the results of a PIF query. 8 | """ 9 | 10 | def __init__(self, took=None, total_num_hits=None, max_score=None, hits=None, **kwargs): 11 | """ 12 | Constructor. 13 | 14 | :param took: Number of milliseconds that the query took to execute. 15 | :param total_num_hits: Total number of hits. 16 | :param max_score: The maximum score. 17 | :param hits: List of :class:`PifSearchHit` objects. 18 | """ 19 | super(PifSearchResult, self).__init__( 20 | took=took, total_num_hits=total_num_hits, max_score=max_score, 21 | hits=self._get_object(PifSearchHit, hits), **kwargs) 22 | -------------------------------------------------------------------------------- /citrination_client/search/query_encoder.py: -------------------------------------------------------------------------------- 1 | from pypif.util.case import to_camel_case 2 | from pypif.util.case import keys_to_snake_case 3 | 4 | import json 5 | 6 | class QueryEncoder(json.JSONEncoder): 7 | """ 8 | Class used to convert a query to json. 9 | """ 10 | 11 | def default(self, obj): 12 | """ 13 | Convert an object to a form ready to dump to json. 14 | 15 | :param obj: Object being serialized. The type of this object must be one of the following: None; a single object derived from the Pio class; or a list of objects, each derived from the Pio class. 16 | :return: List of dictionaries, each representing a physical information object, ready to be serialized. 17 | """ 18 | if obj is None: 19 | return [] 20 | elif isinstance(obj, list): 21 | return [i.as_dictionary() for i in obj] 22 | elif isinstance(obj, dict): 23 | return self._keys_to_camel_case(obj) 24 | else: 25 | return obj.as_dictionary() 26 | 27 | def _keys_to_camel_case(self, obj): 28 | """ 29 | Make a copy of a dictionary with all keys converted to camel case. This is just calls to_camel_case on each of the keys in the dictionary and returns a new dictionary. 30 | 31 | :param obj: Dictionary to convert keys to camel case. 32 | :return: Dictionary with the input values and all keys in camel case 33 | """ 34 | return dict((to_camel_case(key), value) for (key, value) in obj.items()) 35 | -------------------------------------------------------------------------------- /citrination_client/search/routes.py: -------------------------------------------------------------------------------- 1 | pif_search = 'search/pif_search' 2 | pif_multi_search = 'search/pif/multi_pif_search' 3 | dataset_search = 'search/dataset' -------------------------------------------------------------------------------- /citrination_client/util/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CitrineInformatics/python-citrination-client/2cda4dd2a600224acd4e19ae2df66a922dd93342/citrination_client/util/__init__.py -------------------------------------------------------------------------------- /citrination_client/util/config.py: -------------------------------------------------------------------------------- 1 | max_query_size = 10000 -------------------------------------------------------------------------------- /citrination_client/util/credentials.py: -------------------------------------------------------------------------------- 1 | import yaml 2 | import os 3 | import sys 4 | import citrination_client.util.env as citr_env_vars 5 | from citrination_client.base.errors import CitrinationClientError 6 | 7 | home = os.path.expanduser("~") 8 | 9 | DEFAULT_CITRINATION_CONFIG_DIRECTORY = ".citrination" 10 | DEFAULT_CITRINATION_CREDENTIALS_FILE = os.path.join( 11 | home, 12 | DEFAULT_CITRINATION_CONFIG_DIRECTORY, 13 | "credentials" 14 | ) 15 | 16 | DEFAULT_CITRINATION_PROFILE = "default" 17 | CREDENTIALS_API_KEY_KEY = "api_key" 18 | CREDENTIALS_SITE_KEY = "site" 19 | 20 | def load_file_as_yaml(path): 21 | """ 22 | Given a filepath, loads the file as a dictionary from YAML 23 | 24 | :param path: The path to a YAML file 25 | """ 26 | with open(path, "r") as f: 27 | raw_yaml = f.read() 28 | parsed_dict = yaml.load(raw_yaml, Loader=yaml.FullLoader) 29 | return parsed_dict 30 | 31 | def get_credentials_from_file(filepath): 32 | """ 33 | Extracts credentials from the yaml formatted credential filepath 34 | passed in. Uses the default profile if the CITRINATION_PROFILE env var 35 | is not set, otherwise looks for a profile with that name in the credentials file. 36 | 37 | :param filepath: The path of the credentials file 38 | """ 39 | try: 40 | creds = load_file_as_yaml(filepath) 41 | except Exception: 42 | creds = {} 43 | 44 | profile_name = os.environ.get(citr_env_vars.CITRINATION_PROFILE) 45 | if profile_name is None or len(profile_name) == 0: 46 | profile_name = DEFAULT_CITRINATION_PROFILE 47 | api_key = None 48 | site = None 49 | try: 50 | profile = creds[profile_name] 51 | api_key = profile[CREDENTIALS_API_KEY_KEY] 52 | site = profile[CREDENTIALS_SITE_KEY] 53 | except KeyError: 54 | pass 55 | 56 | return (api_key, site) 57 | 58 | def get_preferred_credentials(api_key, site, cred_file=DEFAULT_CITRINATION_CREDENTIALS_FILE): 59 | """ 60 | Given an API key, a site url and a credentials file path, runs through a prioritized list of credential sources to find credentials. 61 | 62 | Specifically, this method ranks credential priority as follows: 63 | 1. Those passed in as the first two parameters to this method 64 | 2. Those found in the environment as variables 65 | 3. Those found in the credentials file at the profile specified 66 | by the profile environment variable 67 | 4. Those found in the default stanza in the credentials file 68 | 69 | :param api_key: A Citrination API Key or None 70 | :param site: A Citrination site URL or None 71 | :param cred_file: The path to a credentials file 72 | """ 73 | profile_api_key, profile_site = get_credentials_from_file(cred_file) 74 | if api_key is None: 75 | api_key = os.environ.get(citr_env_vars.CITRINATION_API_KEY) 76 | if api_key is None or len(api_key) == 0: 77 | api_key = profile_api_key 78 | 79 | if site is None: 80 | site = os.environ.get(citr_env_vars.CITRINATION_SITE) 81 | if site is None or len(site) == 0: 82 | site = profile_site 83 | if site is None: 84 | site = "https://citrination.com" 85 | 86 | return api_key, site 87 | -------------------------------------------------------------------------------- /citrination_client/util/env.py: -------------------------------------------------------------------------------- 1 | CITRINATION_API_KEY = "CITRINATION_API_KEY" 2 | CITRINATION_SITE = "CITRINATION_SITE" 3 | CITRINATION_PROFILE = "CITRINATION_PROFILE" -------------------------------------------------------------------------------- /citrination_client/util/maths.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | def convert_infinity_to_string(number): 4 | if math.isinf(number): 5 | if number < 0: 6 | return "-Infinity" 7 | if number > 0: 8 | return "Infinity" 9 | return number 10 | -------------------------------------------------------------------------------- /citrination_client/util/quote_finder.py: -------------------------------------------------------------------------------- 1 | try: 2 | from urllib.parse import quote 3 | except ImportError: 4 | from urllib import quote 5 | -------------------------------------------------------------------------------- /citrination_client/util/tests/mock_credentials: -------------------------------------------------------------------------------- 1 | default: 2 | api_key: my_default_profile_key 3 | site: my_default_profile_site 4 | test: 5 | api_key: my_test_profile_key 6 | site: my_test_profile_site -------------------------------------------------------------------------------- /citrination_client/util/tests/test_maths.py: -------------------------------------------------------------------------------- 1 | from citrination_client.util.maths import * 2 | 3 | def test_negative_infinity_becomes_string(): 4 | infinity = float("-inf") 5 | assert "-Infinity" == convert_infinity_to_string(infinity) 6 | 7 | def test_positive_infinity_becomes_string(): 8 | infinity = float("+inf") 9 | assert "Infinity" == convert_infinity_to_string(infinity) 10 | 11 | def test_random_number_doesnt_change(): 12 | number = 1 13 | assert number == convert_infinity_to_string(number) 14 | -------------------------------------------------------------------------------- /citrination_client/views/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CitrineInformatics/python-citrination-client/2cda4dd2a600224acd4e19ae2df66a922dd93342/citrination_client/views/__init__.py -------------------------------------------------------------------------------- /citrination_client/views/data_view_builder.py: -------------------------------------------------------------------------------- 1 | from citrination_client.views.base_data_view_builder import BaseDataViewBuilder 2 | 3 | class DataViewBuilder(BaseDataViewBuilder): 4 | """ 5 | A low dimensional interface for building data views. Choose datasets, add 6 | descriptors and other configuration options, then call build() to return the 7 | configuration object needed by the data views api 8 | 9 | Inherits from the BaseDataViewBuilder 10 | """ 11 | 12 | def __init__(self): 13 | super(DataViewBuilder, self).__init__( 14 | { 'builder': 'simple', 'roles': dict() } 15 | ) 16 | 17 | def add_descriptor(self, descriptor, role='ignore', group_by_key=False): 18 | """ 19 | Add a descriptor column. 20 | 21 | :param descriptor: A Descriptor instance (e.g., RealDescriptor, InorganicDescriptor, etc.) 22 | :param role: Specify a role (input, output, latentVariable, or ignore) 23 | :param group_by_key: Whether or not to group by this key during cross validation 24 | """ 25 | 26 | descriptor.validate() 27 | 28 | if descriptor.key in self.configuration["roles"]: 29 | raise ValueError("Cannot add a descriptor with the same name twice") 30 | 31 | self.configuration['descriptors'].append(descriptor.as_dict()) 32 | self.configuration["roles"][descriptor.key] = role 33 | 34 | if group_by_key: 35 | self.configuration["group_by"].append(descriptor.key) 36 | 37 | def set_role(self, key, role): 38 | """ 39 | Sets the role of a descriptor 40 | 41 | :param key: A descriptor key 42 | :type key: str 43 | :param role: (input, output, latentVariable, or ignore) 44 | :type role: str 45 | """ 46 | self.configuration['roles'][key] = role 47 | 48 | def _add_role_if_required(self, key, role): 49 | """ 50 | Overridden from base class. The normal builder will set roles 51 | 52 | :param key: descriptor key 53 | :param role: ignore, input, output, or latentVariable 54 | """ 55 | self.set_role(key, role) 56 | 57 | -------------------------------------------------------------------------------- /citrination_client/views/descriptors/__init__.py: -------------------------------------------------------------------------------- 1 | from .alloy_composition_descriptor import AlloyCompositionDescriptor 2 | from .categorical_descriptor import CategoricalDescriptor 3 | from .inorganic_descriptor import InorganicDescriptor 4 | from .int_descriptor import IntDescriptor 5 | from .organic_descriptor import OrganicDescriptor 6 | from .real_descriptor import RealDescriptor 7 | from .formulation_descriptor import FormulationDescriptor 8 | -------------------------------------------------------------------------------- /citrination_client/views/descriptors/alloy_composition_descriptor.py: -------------------------------------------------------------------------------- 1 | from citrination_client.views.descriptors.descriptor import MaterialDescriptor 2 | 3 | 4 | class AlloyCompositionDescriptor(MaterialDescriptor): 5 | def __init__(self, key, balance_element, basis=100, threshold=None): 6 | self.options = dict(balance_element=balance_element, basis=basis, threshold=threshold) 7 | super(AlloyCompositionDescriptor, self).__init__(key, "Alloy composition") 8 | 9 | 10 | -------------------------------------------------------------------------------- /citrination_client/views/descriptors/categorical_descriptor.py: -------------------------------------------------------------------------------- 1 | from citrination_client.views.descriptors.descriptor import MaterialDescriptor 2 | from citrination_client.base.errors import CitrinationClientError 3 | 4 | from six import string_types 5 | 6 | 7 | class CategoricalDescriptor(MaterialDescriptor): 8 | def __init__(self, key, categories=[]): 9 | self.options = dict(descriptor_values=categories) 10 | super(CategoricalDescriptor, self).__init__(key, "Categorical") 11 | 12 | def validate(self): 13 | categories = self.options['descriptor_values'] 14 | if type(categories) is not list: 15 | raise CitrinationClientError("CategoricalColumn requires that the categories value is a list of strings") 16 | 17 | if not all(isinstance(item, string_types) for item in categories): 18 | raise CitrinationClientError("CategoricalColumn requires that the categories value is a list of strings") 19 | 20 | -------------------------------------------------------------------------------- /citrination_client/views/descriptors/descriptor.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | 4 | class MaterialDescriptor(object): 5 | def __init__(self, key, category): 6 | self.key = key 7 | self.category = category 8 | 9 | def validate(self): 10 | pass 11 | 12 | def as_dict(self): 13 | self.validate() 14 | 15 | dict_repr = { 16 | "descriptor_key": self.key, 17 | "category": self.category 18 | } 19 | 20 | dict_repr.update(self.options) 21 | 22 | return dict_repr 23 | 24 | def __repr__(self): 25 | return json.dumps(self.as_dict(), indent=2) 26 | -------------------------------------------------------------------------------- /citrination_client/views/descriptors/formulation_descriptor.py: -------------------------------------------------------------------------------- 1 | from citrination_client.views.descriptors.descriptor import MaterialDescriptor 2 | 3 | 4 | class FormulationDescriptor(MaterialDescriptor): 5 | def __init__(self, key): 6 | self.options = dict() 7 | super(FormulationDescriptor, self).__init__(key, "Formulation") 8 | 9 | 10 | -------------------------------------------------------------------------------- /citrination_client/views/descriptors/inorganic_descriptor.py: -------------------------------------------------------------------------------- 1 | from citrination_client.views.descriptors.descriptor import MaterialDescriptor 2 | 3 | 4 | class InorganicDescriptor(MaterialDescriptor): 5 | def __init__(self, key, threshold=1.0): 6 | self.options = dict(threshold=threshold) 7 | super(InorganicDescriptor, self).__init__(key, "Inorganic") 8 | 9 | 10 | -------------------------------------------------------------------------------- /citrination_client/views/descriptors/int_descriptor.py: -------------------------------------------------------------------------------- 1 | from citrination_client.views.descriptors.descriptor import MaterialDescriptor 2 | 3 | 4 | class IntDescriptor(MaterialDescriptor): 5 | def __init__(self, key, lower_bound, upper_bound, units=""): 6 | self.options = dict(lower_bound=lower_bound, upper_bound=upper_bound, units=units) 7 | super(IntDescriptor, self).__init__(key, "Integer") 8 | 9 | def validate(self): 10 | raw_lower = self.options["lower_bound"] 11 | raw_upper = self.options["upper_bound"] 12 | 13 | try: 14 | lower = int(str(raw_lower)) 15 | upper = int(str(raw_upper)) 16 | except ValueError: 17 | raise ValueError( 18 | "lower_bound and upper_bound must be integers but got {} and {} respectively".format( 19 | raw_lower, raw_upper) 20 | ) 21 | 22 | if lower > upper: 23 | raise ValueError("Lower ({}) must be smaller than upper ({})".format(lower, upper)) 24 | -------------------------------------------------------------------------------- /citrination_client/views/descriptors/organic_descriptor.py: -------------------------------------------------------------------------------- 1 | from citrination_client.views.descriptors.descriptor import MaterialDescriptor 2 | 3 | 4 | class OrganicDescriptor(MaterialDescriptor): 5 | def __init__(self, key): 6 | self.options = dict() 7 | super(OrganicDescriptor, self).__init__(key, "Organic") 8 | 9 | -------------------------------------------------------------------------------- /citrination_client/views/descriptors/real_descriptor.py: -------------------------------------------------------------------------------- 1 | from citrination_client.views.descriptors.descriptor import MaterialDescriptor 2 | 3 | 4 | class RealDescriptor(MaterialDescriptor): 5 | def __init__(self, key, lower_bound, upper_bound, units=""): 6 | self.options = dict(lower_bound=lower_bound, upper_bound=upper_bound, units=units) 7 | super(RealDescriptor, self).__init__(key, "Real") 8 | 9 | def validate(self): 10 | raw_lower = self.options["lower_bound"] 11 | raw_upper = self.options["upper_bound"] 12 | 13 | try: 14 | lower = float(str(raw_lower)) 15 | upper = float(str(raw_upper)) 16 | except ValueError: 17 | raise ValueError( 18 | "lower_bound and upper_bound must be floats but got {} and {} respectively".format( 19 | raw_lower, raw_upper) 20 | ) 21 | 22 | if lower > upper: 23 | raise ValueError("Lower ({}) must be smaller than upper ({})".format(lower, upper)) 24 | -------------------------------------------------------------------------------- /citrination_client/views/descriptors/tests/test_descriptors.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from citrination_client import CategoricalDescriptor 4 | from citrination_client.views.descriptors import RealDescriptor, IntDescriptor 5 | 6 | 7 | def test_categorical_descriptor(): 8 | d = CategoricalDescriptor("categorical", ["0", "1"]) 9 | assert d.as_dict() == dict(category="Categorical", descriptor_key="categorical", descriptor_values=["0", "1"]) 10 | 11 | 12 | def test_real_descriptor(): 13 | d = RealDescriptor("band gap", lower_bound=-5, upper_bound=3.0, units="eV") 14 | assert d.as_dict() == dict(category="Real", units="eV", descriptor_key="band gap", lower_bound=-5, upper_bound=3.0) 15 | 16 | with pytest.raises(ValueError) as err: 17 | RealDescriptor("bg", 5, -5).as_dict() 18 | 19 | 20 | def test_int_descriptor(): 21 | descriptor_key = "Ingredient count" 22 | lower_bound = 0 23 | upper_bound = 10 24 | units = "" 25 | d = IntDescriptor(descriptor_key, lower_bound=lower_bound, upper_bound=upper_bound, units=units) 26 | 27 | expected = dict( 28 | category="Integer", 29 | descriptor_key=descriptor_key, 30 | lower_bound=lower_bound, 31 | upper_bound=upper_bound, 32 | units=units 33 | ) 34 | assert d.as_dict() == expected 35 | 36 | with pytest.raises(ValueError) as err: 37 | IntDescriptor("", 5, -5).as_dict() 38 | -------------------------------------------------------------------------------- /citrination_client/views/model_report.py: -------------------------------------------------------------------------------- 1 | from copy import deepcopy 2 | 3 | class ModelReport(object): 4 | """ 5 | An abstraction of a model report that wraps access to various sections 6 | of the report. 7 | """ 8 | 9 | """ 10 | :param raw_report: the dict representation of model report JSON 11 | :type: dict 12 | """ 13 | def __init__(self, raw_report): 14 | self._raw_report = raw_report 15 | 16 | @property 17 | def model_name(self): 18 | """ 19 | :return: the model output which this report corresponds to 20 | :rtype: str 21 | """ 22 | return self._raw_report['model_name'] 23 | 24 | @property 25 | def performance(self): 26 | """ 27 | :return: dictionary of model performance metrics 28 | :rtype: dict 29 | """ 30 | return self._raw_report['error_metrics'] 31 | 32 | @property 33 | def feature_importances(self): 34 | """ 35 | :return: list of key value pairs regarding feature importances 36 | :rtype: list of dict 37 | """ 38 | return self._raw_report['feature_importances'] 39 | 40 | @property 41 | def model_settings(self): 42 | """ 43 | :return: dictionary of model settings 44 | :rtype: dict 45 | """ 46 | return self._raw_report['model_settings'] 47 | 48 | """ 49 | WARNING - the dict format returned is unstable and may change over time. 50 | 51 | :return: a copy of the raw report that backs the instance. 52 | :rtype: dict 53 | """ 54 | def as_json(self): 55 | return deepcopy(self._raw_report) 56 | 57 | def __str__(self): 58 | return "".format( 59 | self.model_name 60 | ) 61 | -------------------------------------------------------------------------------- /citrination_client/views/model_template/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CitrineInformatics/python-citrination-client/2cda4dd2a600224acd4e19ae2df66a922dd93342/citrination_client/views/model_template/__init__.py -------------------------------------------------------------------------------- /citrination_client/views/model_template/client.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from citrination_client.base import BaseClient 4 | 5 | 6 | class ModelTemplateClient(BaseClient): 7 | """ 8 | Model template client. 9 | """ 10 | 11 | def __init__(self, api_key, webserver_host="https://citrination.com", suppress_warnings=False, proxies=None): 12 | members = ["validate"] 13 | super(ModelTemplateClient, self).__init__(api_key, webserver_host, members, suppress_warnings, proxies) 14 | 15 | def validate(self, ml_template): 16 | """ 17 | Runs the template against the validation endpoint, returns a message indicating status of the templte 18 | 19 | :param ml_template: Template to validate 20 | :return: OK or error message if validation failed 21 | """ 22 | 23 | data = { 24 | "ml_template": 25 | ml_template 26 | } 27 | 28 | failure_message = "ML template validation invoke failed" 29 | 30 | res = self._get_success_json(self._post_json( 31 | 'ml_templates/validate', data, failure_message=failure_message))['data'] 32 | if res['valid']: 33 | return 'OK' 34 | return res['reason'] 35 | -------------------------------------------------------------------------------- /citrination_client/views/search_template/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CitrineInformatics/python-citrination-client/2cda4dd2a600224acd4e19ae2df66a922dd93342/citrination_client/views/search_template/__init__.py -------------------------------------------------------------------------------- /citrination_client/views/tests/test_advanced_data_view_builder.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from citrination_client.views.advanced_data_view_builder import AdvancedDataViewBuilder 4 | from citrination_client.views.descriptors.real_descriptor import RealDescriptor 5 | 6 | 7 | def check_exception(func): 8 | try: 9 | func() 10 | return True 11 | except ValueError as e: 12 | return False 13 | 14 | 15 | def test_add_relations(): 16 | builder = AdvancedDataViewBuilder() 17 | 18 | builder.add_descriptor(RealDescriptor("a", lower_bound=-1, upper_bound=1), True) 19 | builder.add_descriptor(RealDescriptor("b", lower_bound=-1, upper_bound=1), True) 20 | builder.add_descriptor(RealDescriptor("c", lower_bound=-1, upper_bound=1), True) 21 | 22 | assert not check_exception(lambda: builder.add_relation(123, 123)) 23 | assert check_exception(lambda: builder.add_relation('a', 'b')) 24 | assert check_exception(lambda: builder.add_relation('a', 'c')) 25 | assert not check_exception(lambda: builder.add_relation('c', ['d'])) 26 | assert not check_exception(lambda: builder.add_relation([], 'b')) 27 | assert not check_exception(lambda: builder.add_relation(['a'], [])) 28 | 29 | # duplicate 30 | assert not check_exception(lambda: builder.add_relation('a', 'b')) 31 | assert check_exception(lambda: builder.add_relation(['a', 'c'], 'b')) 32 | assert not check_exception(lambda: builder.add_relation(['c', 'a'], 'b')) 33 | assert not check_exception(lambda: builder.add_relation(['c', 'c'], 'b')) 34 | assert not check_exception(lambda: builder.add_relation('c', 'c')) 35 | 36 | # key not found 37 | assert not check_exception(lambda: builder.add_relation('z', 'b')) 38 | assert not check_exception(lambda: builder.add_relation('a', 'z')) 39 | 40 | # limit exceeded 41 | limit_hit = False 42 | for x in range(0, 50): 43 | try: 44 | builder.add_descriptor(RealDescriptor("a" + str(x), lower_bound=-1, upper_bound=1), True) 45 | builder.add_descriptor(RealDescriptor("b" + str(x), lower_bound=-1, upper_bound=1), True) 46 | builder.add_relation('a' + str(x), 'b' + str(x)) 47 | except ValueError: 48 | if x > 0: 49 | limit_hit = True 50 | assert limit_hit 51 | -------------------------------------------------------------------------------- /citrination_client/views/tests/test_data_view_builder.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from citrination_client.views.data_view_builder import DataViewBuilder 4 | from citrination_client.views.descriptors.real_descriptor import RealDescriptor 5 | from citrination_client.client import CitrinationClient 6 | from citrination_client.views.descriptors import FormulationDescriptor 7 | from os import environ 8 | 9 | 10 | def test_add_descriptor(): 11 | builder = DataViewBuilder() 12 | 13 | bandgap = RealDescriptor("band gap", lower_bound=-1, upper_bound=1) 14 | 15 | builder.add_descriptor(bandgap, "output", True) 16 | config = builder.build() 17 | 18 | assert config["group_by"] == ["band gap"] 19 | assert config["descriptors"] == [bandgap.as_dict()] 20 | assert config["roles"] == {"band gap": "output"} 21 | 22 | # Make sure duplicates raise an error 23 | with pytest.raises(ValueError) as err: 24 | builder.add_descriptor(RealDescriptor("band gap", lower_bound=-1, upper_bound=1), "input") 25 | 26 | @pytest.mark.skipif(environ['CITRINATION_SITE'] != "https://citrination.com", 27 | reason="Formulation test only supported on open citrination") 28 | def test_formulation_descriptor(): 29 | client = CitrinationClient(environ["CITRINATION_API_KEY"]) 30 | 31 | builder = DataViewBuilder() 32 | builder.dataset_ids([187195]) 33 | 34 | builder.add_formulation_descriptor(FormulationDescriptor("Formulation (idealMassPercent)"), client.data_views) 35 | 36 | config = builder.build() 37 | 38 | assert config["roles"]["Formulation (idealMassPercent)"] == "input" 39 | assert config["roles"]["component type"] == "ignore" 40 | assert config["roles"]["name"] == "ignore" 41 | assert config["roles"]["% Y2O3 (volume, ideal)"] == "ignore" 42 | 43 | y203_share_present = False 44 | name_present = False 45 | component_type_present = False 46 | 47 | for desc in config["descriptors"]: 48 | if desc["descriptor_key"] == "% Y2O3 (volume, ideal)": 49 | y203_share_present = True 50 | if desc["descriptor_key"] == "name": 51 | name_present = True 52 | if desc["descriptor_key"] == "component type": 53 | component_type_present = True 54 | 55 | assert y203_share_present 56 | assert name_present 57 | assert component_type_present 58 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SPHINXPROJ = PythonCitrinationClient 8 | SOURCEDIR = tmpsrc 9 | BUILDDIR = build 10 | STATICDIR = source 11 | 12 | # Put it first so that "make" without argument is like "make help". 13 | help: 14 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 15 | 16 | .PHONY: help Makefile 17 | 18 | html: prepare 19 | $(SPHINXBUILD) -b html $(SOURCEDIR) $(BUILDDIR)/html 20 | @echo 21 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 22 | 23 | clean: 24 | rm -rf $(BUILDDIR)/* 25 | rm -rf $(SOURCEDIR) 26 | 27 | prepare: clean 28 | mkdir $(SOURCEDIR) 29 | cp -a $(STATICDIR)/ $(SOURCEDIR) 30 | 31 | apidoc: prepare 32 | sphinx-apidoc -f ../citrination_client -o $(SOURCEDIR) 33 | 34 | gh-pages: html 35 | cd build/html && \ 36 | git init && \ 37 | git checkout -b gh-pages && \ 38 | git add . && \ 39 | git commit -m "updating sphinx" && \ 40 | git remote add upstream git@github.com:CitrineInformatics/python-citrination-client.git && \ 41 | git push upstream gh-pages --force -------------------------------------------------------------------------------- /docs/source/code_samples/data/custom_ingest_with_arguments.py: -------------------------------------------------------------------------------- 1 | # ... client initialization left out 2 | data_client = client.data 3 | 4 | file_path = "experiments/data.xrdml" 5 | dataset_id = 1 6 | 7 | ingester_list = data_client.list_ingesters() 8 | xrdml_ingester = ingester_list.find_by_id("citrine/ingest xrdml_xrd_converter") 9 | 10 | # Printing the ingester's arguments, we can see it requires an argument with the 11 | # name `sample_id`, and another with the name `chemical_formula`, both of which 12 | # should be strings. 13 | print(ingester.arguments) 14 | # [{ 'name': 'sample_id', 15 | # 'desc': 'An ID to uniquely identify the material referenced in the file.', 16 | # 'type': 'String', 17 | # 'required': True }, 18 | # { 'name': 'chemical_formula', 19 | # 'desc': 'The chemical formula of the material referenced in the file.', 20 | # 'type': 'String', 21 | # 'required': True }] 22 | 23 | ingester_arguments = [ 24 | { "name": "sample_id", "value": "1212" }, 25 | { "name": "chemical_formula", "value": "NaCl" }, 26 | ] 27 | 28 | # To ingest the file using the file_path as the destination path 29 | data_client.upload_with_ingester( 30 | dataset_id, file_path, xrdml_ingester, ingester_arguments 31 | ) 32 | # To ingest the file using a different destination path 33 | data_client.upload_with_ingester( 34 | dataset_id, file_path, xrdml_ingester, ingester_arguments, 'data.xrdml' 35 | ) 36 | -------------------------------------------------------------------------------- /docs/source/code_samples/data/custom_ingest_without_arguments.py: -------------------------------------------------------------------------------- 1 | # ... client initialization left out 2 | data_client = client.data 3 | 4 | file_path = "data/formulation.csv" 5 | dataset_id = 1 6 | 7 | ingester_list = data_client.list_ingesters() 8 | formulation_ingester = ingester_list.find_by_id("citrine/ingest formulation_csv_converter") 9 | 10 | # Printing the formulation_ingester's arguments, we can see that it takes one 11 | # argument that is optional - so we can elect to omit it 12 | print(formulation_ingester.arguments) 13 | # [{ 'name': 'check_ingredient_names', 14 | # 'desc': 'Whether to check that the names of the ingredients in the formulations are present in this upload', 15 | # 'type': 'Boolean', 16 | # 'required': False }] 17 | 18 | # To ingest the file using the file_path as the destination path 19 | data_client.upload_with_ingester( 20 | dataset_id, file_path, formulation_ingester 21 | ) 22 | # To ingest the file using a different destination path 23 | data_client.upload_with_ingester( 24 | dataset_id, file_path, formulation_ingester, dest_path='formulation.csv' 25 | ) 26 | -------------------------------------------------------------------------------- /docs/source/code_samples/data/file_urls.py: -------------------------------------------------------------------------------- 1 | # ... client initialization left out 2 | data_client = client.data 3 | dataset_id = 1 4 | 5 | # Gets a single file named exactly my_file.json 6 | 7 | dataset_file = data_client.get_dataset_file(dataset_id, "my_file.json") 8 | 9 | dataset_file.url # url that can be used to download the file 10 | dataset_file.path # the filepath as it appears in Citrination 11 | 12 | # Gets all the files in a dataset, organized by version, 13 | # represented as a list of DatasetFile objects 14 | 15 | dataset_files = data_client.get_dataset_files(dataset_id) 16 | -------------------------------------------------------------------------------- /docs/source/code_samples/data/find_ingester_by_id.py: -------------------------------------------------------------------------------- 1 | # ... client initialization left out 2 | data_client = client.data 3 | 4 | ingester_list = data_client.list_ingesters() 5 | csv_ingester = ingester_list.find_by_id("citrine/ingest template_csv_converter") 6 | -------------------------------------------------------------------------------- /docs/source/code_samples/data/find_ingester_using_where.py: -------------------------------------------------------------------------------- 1 | # ... client initialization left out 2 | data_client = client.data 3 | 4 | # All ingesters available on your Citrination deployment 5 | ingester_list = data_client.list_ingesters() 6 | # Find all ingesters whose `name` attributes contain the phrase "xrd" 7 | # Note that the `where` method returns a new IngesterList 8 | xrd_ingesters = ingester_list.where({ "name": "xrd" }) 9 | 10 | # How many ingesters had names that contained "xrd"? 11 | print(xrd_ingesters.ingester_count) 12 | # 2 13 | 14 | # Quick summary of what those ingesters are 15 | print(xrd_ingesters) 16 | # ", 21 | # "" 25 | # ]> 26 | 27 | # Supposing we want to go with the one whose display_name is `Citrine: XRD .xrdml`, 28 | # there are several ways to do this: 29 | # 1. Indexing into the xrd_ingesters' `ingesters` list: 30 | xrdml_ingester = xrd_ingesters.ingesters[1] 31 | 32 | # 2. Alternatively, using a where clause with the matching `display_name`, `id`, 33 | # or other searchable attribute: 34 | xrdml_ingester = xrd_ingesters.where({ "display_name": "Citrine: XRD .xrdml" }).ingesters[0] 35 | 36 | # 3. Since xrd_ingesters is an `IngesterList`, we could also use `find_by_id` to 37 | # avoid having to index into the `ingesters` list: 38 | xrdml_ingester = xrd_ingesters.find_by_id("citrine/ingest xrdml_xrd_converter") 39 | -------------------------------------------------------------------------------- /docs/source/code_samples/data/get_ingest_status.py: -------------------------------------------------------------------------------- 1 | # ... client initialization left out 2 | data_client = client.data 3 | 4 | dataset = client.data.create_dataset() 5 | file = 'test_data/template_example.csv' 6 | client.data.upload_with_template_csv_ingester(dataset.id, file) 7 | 8 | # After uploading, the status will initially be `Processing` 9 | print(client.data.get_ingest_status(dataset.id)) 10 | # Processing 11 | 12 | # After data has finished processing, the status will be `Finished` 13 | print(client.data.get_ingest_status(dataset.id)) 14 | # Finished 15 | -------------------------------------------------------------------------------- /docs/source/code_samples/data/get_pif.py: -------------------------------------------------------------------------------- 1 | # ... client initialization left out 2 | data_client = client.data 3 | dataset_id = 1 4 | pif_uid = "abc123" 5 | 6 | # Retrieves the latest version of the PIF with uid is "abc123" from the latest 7 | # version of dataset 1 8 | data_client.get_pif(dataset_id, pif_uid) 9 | 10 | # Retrieves the latest version of the PIF with uid is "abc123" from version 3 11 | # of dataset 1 12 | data_client.get_pif(dataset_id, pif_uid, dataset_version = 3) 13 | 14 | # Retrieves the version 2 of the PIF with uid is "abc123" from the latest version 15 | # of dataset 1 16 | data_client.get_pif(dataset_id, pif_uid, pif_version = 2) 17 | 18 | # Retrieves the version 2 of the PIF with uid is "abc123" from version 3 of 19 | # dataset 1 20 | data_client.get_pif(dataset_id, pif_uid, dataset_version = 3, pif_version = 2) 21 | -------------------------------------------------------------------------------- /docs/source/code_samples/data/get_pif_with_metadata.py: -------------------------------------------------------------------------------- 1 | # ... client initialization left out 2 | data_client = client.data 3 | dataset_id = 105924 4 | pif_uid = "1DF1C8EB706363E40546253D5D025D90" 5 | 6 | get_pif_with_metadata = data_client.get_pif_with_metadata(dataset_id, pif_uid) 7 | 8 | print(get_pif_with_metadata) 9 | # {'metadata': { 10 | # 'uid': '1DF1C8EB706363E40546253D5D025D90', 11 | # 'version': 1, 12 | # 'dataset_id': '105924', 13 | # 'dataset_version': 1, 14 | # 'updated_at': '2017-07-04T19:41:40.139Z'}, 15 | # 'pif': } 16 | -------------------------------------------------------------------------------- /docs/source/code_samples/data/ingester_arguments.py: -------------------------------------------------------------------------------- 1 | # ... client initialization left out 2 | data_client = client.data 3 | 4 | ingester_list = data_client.list_ingesters() 5 | csv_ingester = ingester_list.find_by_id("citrine/ingest template_csv_converter") 6 | formulation_ingester = ingester_list.find_by_id("citrine/ingest formulation_csv_converter") 7 | xrdml_ingester = ingester_list.find_by_id("citrine/ingest xrdml_xrd_converter") 8 | 9 | # Here we can see that the Template CSV ingester accepts no arguments 10 | print(csv_ingester.arguments) 11 | # [] 12 | 13 | # Here we can see that the Formulation CSV ingester accepts one optional argument 14 | print(formulation_ingester.arguments) 15 | # [{ 'name': 'check_ingredient_names', 16 | # 'desc': 'Whether to check that the names of the ingredients in the formulations are present in this upload', 17 | # 'type': 'Boolean', 18 | # 'required': False }] 19 | 20 | # Here we can see that the Citrine: XRD .xrdml ingester requires 2 arguments, 21 | # one named `sample_id` and the other named `chemical_formula`, both of which 22 | # should be strings. 23 | print(xrdml_ingester.arguments) 24 | # [{ 'name': 'sample_id', 25 | # 'desc': 'An ID to uniquely identify the material referenced in the file.', 26 | # 'type': 'String', 27 | # 'required': True }, 28 | # { 'name': 'chemical_formula', 29 | # 'desc': 'The chemical formula of the material referenced in the file.', 30 | # 'type': 'String', 31 | # 'required': True }] 32 | -------------------------------------------------------------------------------- /docs/source/code_samples/data/instantiation.py: -------------------------------------------------------------------------------- 1 | from citrination_client import CitrinationClient 2 | from os import environ 3 | 4 | client = CitrinationClient(environ["CITRINATION_API_KEY"], environ["CITRINATION_SITE"]) 5 | data_client = client.data -------------------------------------------------------------------------------- /docs/source/code_samples/data/list_files.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CitrineInformatics/python-citrination-client/2cda4dd2a600224acd4e19ae2df66a922dd93342/docs/source/code_samples/data/list_files.py -------------------------------------------------------------------------------- /docs/source/code_samples/data/permissions.py: -------------------------------------------------------------------------------- 1 | # ... client initialization left out 2 | data_client = client.data 3 | 4 | # Creates a new dataset (permissions default to private) 5 | dataset = data_client.create_dataset("My New Dataset") 6 | dataset_id = dataset.id 7 | 8 | # Make the dataset public 9 | data_client.update_dataset(dataset_id, public=True) 10 | 11 | # Make the dataset private again 12 | data_client.update_dataset(dataset_id, public=False) 13 | -------------------------------------------------------------------------------- /docs/source/code_samples/data/upload_dir_no_dest.py: -------------------------------------------------------------------------------- 1 | # ... client initialization left out 2 | data_client = client.data 3 | 4 | directory_path = "characterizations/" 5 | dataset_id = 1 6 | data_client.upload(dataset_id, directory_path) -------------------------------------------------------------------------------- /docs/source/code_samples/data/upload_dir_with_dest.py: -------------------------------------------------------------------------------- 1 | # ... client initialization left out 2 | data_client = client.data 3 | 4 | directory_path = "characterizations/" 5 | dataset_id = 1 6 | data_client.upload(dataset_id, directory_path, "january_characterizations/") -------------------------------------------------------------------------------- /docs/source/code_samples/data/upload_file_with_dest.py: -------------------------------------------------------------------------------- 1 | # ... client initialization left out 2 | data_client = client.data 3 | 4 | file_path = "characterizations/CdTe1.json" 5 | dataset_id = 1 6 | # Pass in the third parameter to upload() 7 | data_client.upload(dataset_id, file_path, "CadTel1.json") -------------------------------------------------------------------------------- /docs/source/code_samples/data/upload_no_dest.py: -------------------------------------------------------------------------------- 1 | # ... client initialization left out 2 | data_client = client.data 3 | 4 | file_path = "characterizations/CdTe1.json" 5 | dataset_id = 1 6 | data_client.upload(dataset_id, file_path) -------------------------------------------------------------------------------- /docs/source/code_samples/data/upload_with_template_csv_ingester.py: -------------------------------------------------------------------------------- 1 | # ... client initialization left out 2 | data_client = client.data 3 | 4 | file_path = "experiments/data.csv" 5 | dataset_id = 1 6 | 7 | # To ingest the file using the file_path as the destination path 8 | data_client.upload_with_template_csv_ingester( 9 | dataset_id, file_path 10 | ) 11 | # To ingest the file using a different destination path 12 | data_client.upload_with_template_csv_ingester( 13 | dataset_id, file_path, dest_path='data.csv' 14 | ) 15 | -------------------------------------------------------------------------------- /docs/source/code_samples/data/version.py: -------------------------------------------------------------------------------- 1 | # ... client initialization left out 2 | data_client = client.data 3 | 4 | # Creates a new dataset (permissions default to private) 5 | dataset = data_client.create_dataset("My New Dataset") 6 | dataset_id = dataset.id 7 | 8 | # Uploads a file to it 9 | data_client.upload(dataset_id, "my_file.json") 10 | 11 | print(data_client.matched_file_count(dataset_id)) 12 | # -> 1 13 | 14 | # Create a new dataset version 15 | data_client.create_dataset_version(dataset_id) 16 | 17 | # No files in the new version 18 | print(data_client.matched_file_count(dataset_id)) 19 | # -> 0 -------------------------------------------------------------------------------- /docs/source/code_samples/general/client_serialization.py: -------------------------------------------------------------------------------- 1 | client = CitrinationClient(environ["CITRINATION_API_KEY"], environ["CITRINATION_SITE"]) 2 | 3 | print(client) 4 | # ['models', 'search', 'data'] 5 | 6 | models_client = client.models 7 | type(models_client) 8 | # -------------------------------------------------------------------------------- /docs/source/code_samples/general/initialization.py: -------------------------------------------------------------------------------- 1 | from citrination_client import CitrinationClient 2 | from os import environ 3 | 4 | client = CitrinationClient("my_api_key") -------------------------------------------------------------------------------- /docs/source/code_samples/migrating/activate_venv: -------------------------------------------------------------------------------- 1 | $ cd my_project_folder 2 | $ virtualenv my_project 3 | $ source my_project/bin/activate -------------------------------------------------------------------------------- /docs/source/code_samples/migrating/deactivate: -------------------------------------------------------------------------------- 1 | $ deactivate -------------------------------------------------------------------------------- /docs/source/code_samples/migrating/pip_install: -------------------------------------------------------------------------------- 1 | $ pip install citrination_client==4.0.0 -------------------------------------------------------------------------------- /docs/source/code_samples/models/design.py: -------------------------------------------------------------------------------- 1 | # ... client initialization left out 2 | 3 | linear_design = models_client.submit_design_run(data_view_id=data_view_id, num_candidates=10, 4 | effort=3, 5 | target=Target('Property Bulk modulus', .1), constraints=[], 6 | sampler='Default') 7 | # Wait for the design to finish 8 | print("DesignUUID: " + linear_design.uuid) 9 | while True: 10 | design_status = models_client.get_design_run_status(data_view_id, linear_design.uuid) 11 | if design_status.status == 'Finished': 12 | break 13 | time.sleep(5) 14 | 15 | results = models_client.get_design_run_results(data_view_id, linear_design.uuid) 16 | print(results.best_materials) 17 | # [{'descriptor_values': {'formula': 'Ba', 'Property Bulk modulus': '9.0', ... -------------------------------------------------------------------------------- /docs/source/code_samples/models/predict.py: -------------------------------------------------------------------------------- 1 | # ... client initialization left out 2 | 3 | models_client = client.models 4 | 5 | # The input to this data view is a SMILES string 6 | inputs = [ 7 | {"formula": "NaCl", "Property Crystallinity": "Amorphous"}, 8 | {"formula": "MgO2", "Property Crystallinity": "Polycrystalline"} 9 | ] 10 | 11 | data_view_id = "4106" 12 | 13 | # This prediction will return a list of two PredictionResult objects since 14 | # there were two candidates passed in as inputs. 15 | prediction_results = models_client.predict(data_view_id, inputs, method="scalar") 16 | 17 | # Retrieve the prediction value and loss for the "Property Band gap" output 18 | # for the NaCl candidate 19 | nacl_result = prediction_results[0] 20 | nacl_value = nacl_result.get_value("Property Band gap").value 21 | nacl_loss = nacl_result.get_value("Property Band gap").loss -------------------------------------------------------------------------------- /docs/source/code_samples/models/tsne.py: -------------------------------------------------------------------------------- 1 | # ... client initialization left out 2 | 3 | models_client = client.models 4 | 5 | data_view_id = "4106" 6 | 7 | resp = models_client.tsne(data_view_id) 8 | 9 | band_gap_projection = resp.get_projection('Property Band gap') 10 | band_gap_projection.xs # returns the x component of the TSNE projection 11 | band_gap_projection.ys # returns the y component of the TSNE projection 12 | band_gap_projection.responses # returns the responses for the points in the projection 13 | band_gap_projection.uids # returns the record UIDs for the projection 14 | band_gap_projection.tags # returns the tags on the points in the projection -------------------------------------------------------------------------------- /docs/source/code_samples/search/dataset_search.py: -------------------------------------------------------------------------------- 1 | # ... client initialization left out 2 | 3 | search_client = client.search 4 | 5 | # Construct a query which returns datasets 6 | # which contain records with chemical formulas 7 | # matching MgO2 8 | query = DatasetReturningQuery( 9 | query=DataQuery( 10 | system=PifSystemQuery( 11 | chemical_formula=ChemicalFieldQuery( 12 | filter=ChemicalFilter( 13 | equal='MgO2'))))) 14 | 15 | # Execute the search; we use dataset_search because 16 | # we have a DatasetReturningQuery 17 | results = search_client.dataset_search(query) 18 | 19 | # The resulting hits represent datasets which 20 | # contain records that matched our criteria 21 | print(results.hits[0].name) 22 | # => Wikipedia 23 | 24 | print(results.hits[0].id) 25 | # => 114201 -------------------------------------------------------------------------------- /docs/source/code_samples/search/generate_simple_query.py: -------------------------------------------------------------------------------- 1 | # ... client initialization left out 2 | 3 | search_client = client.search 4 | 5 | # Construct a query which returns records in dataset 1160 6 | # which have GaN as a chemical formula and have band gap 7 | # property values between 1.5 and 1.6 8 | query = search_client.generate_simple_chemical_query(chemical_formula="GaN", 9 | property_name="Band gap", 10 | property_min=1.5, 11 | property_max=1.6) 12 | 13 | results = search_client.pif_search(query) 14 | 15 | # Get the ID of the first hit 16 | record_id = results.hits[0].id 17 | print(results.hits[0].extracted) 18 | # => { 19 | # u'property_units': u'eV', 20 | # u'property_value': u'1.57', 21 | # u'chemical_formula': u'GaN', 22 | # u'property_name': u'Band gap', 23 | # u'reference_doi': u'10.1038/s41524-017-0013-3' 24 | # } -------------------------------------------------------------------------------- /docs/source/code_samples/search/pif_search.py: -------------------------------------------------------------------------------- 1 | # ... client initialization left out 2 | 3 | search_client = client.search 4 | 5 | # Construct a query which returns records in dataset 1160 6 | # and have chemical formulas matching CoSi 7 | query = PifSystemReturningQuery( 8 | query=DataQuery( 9 | dataset=DatasetQuery( 10 | id=Filter(equal='1160') 11 | ), 12 | system=PifSystemQuery( 13 | chemical_formula=ChemicalFieldQuery( 14 | filter=ChemicalFilter( 15 | equal='CoSi'))))) 16 | 17 | # Execute the search (we use pif_search because we have a PifSystemReturningQuery) 18 | results = search_client.pif_search(query) 19 | 20 | # Get the ID of the first hit 21 | record_id = results.hits[0].id -------------------------------------------------------------------------------- /docs/source/code_samples/views/advanced_data_view_builder_1.py: -------------------------------------------------------------------------------- 1 | from citrination_client.views.advanced_data_view_builder import AdvancedDataViewBuilder 2 | from citrination_client.views.client import DataViewsClient 3 | from os import environ 4 | 5 | # Note: for the purposes of this example, environ["CITRINATION_SITE"] is 6 | # https://citrination.com 7 | client = DataViewsClient(environ["CITRINATION_API_KEY"], environ["CITRINATION_SITE"]) 8 | 9 | dataset_ids = ['1160'] 10 | # Get the ml config defaults for all descriptor keys in the datasets 11 | data_view_config = client.create_ml_configuration_from_datasets(dataset_ids) 12 | 13 | descriptor_keys = [ 14 | 'formula', 15 | 'Property Band gap', 16 | 'Property Color', 17 | 'Temperature (Property Band gap)', 18 | 'Temperature (Property Color)' 19 | ] 20 | descriptors = list( 21 | filter( 22 | lambda d: d['descriptor_key'] in descriptor_keys, 23 | data_view_config['descriptors'] 24 | ) 25 | ) 26 | -------------------------------------------------------------------------------- /docs/source/code_samples/views/advanced_data_view_builder_2.py: -------------------------------------------------------------------------------- 1 | advanced_builder = AdvancedDataViewBuilder() 2 | advanced_builder.dataset_ids(dataset_ids) 3 | 4 | for descriptor in descriptors: 5 | advanced_builder.add_raw_descriptor(descriptor) 6 | 7 | advanced_builder.add_relation(['formula'], 'Property Band gap') 8 | advanced_builder.add_relation(['formula'], 'Property Color') 9 | advanced_builder.add_relation(['formula', 'Temperature (Property Band gap)'], 'Property Band gap') 10 | advanced_builder.add_relation(['formula', 'Property Band gap'], 'Property Color') 11 | -------------------------------------------------------------------------------- /docs/source/code_samples/views/advanced_data_view_builder_3.py: -------------------------------------------------------------------------------- 1 | advanced_config = advanced_builder.build() 2 | 3 | # Create a data view 4 | data_view_id = client.data_views.create( 5 | advanced_config, 'My dataview name', 'The data view description' 6 | ) 7 | 8 | # Or update an existing data view 9 | client.update(data_view_id, advanced_config) 10 | -------------------------------------------------------------------------------- /docs/source/code_samples/views/advanced_data_view_builder_4.py: -------------------------------------------------------------------------------- 1 | #Add ingredient level property relations 2 | advanced_builder.add_relation(['Property pigment red', 3 | 'Property pigment green', 4 | 'Property pigment blue'], 5 | 'Formulation (idealMassPercent)') 6 | -------------------------------------------------------------------------------- /docs/source/code_samples/views/advanced_data_view_builder_relation_graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CitrineInformatics/python-citrination-client/2cda4dd2a600224acd4e19ae2df66a922dd93342/docs/source/code_samples/views/advanced_data_view_builder_relation_graph.png -------------------------------------------------------------------------------- /docs/source/code_samples/views/advanced_data_view_builder_relations.py: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | 'inputs': ['formula'], 4 | 'output': 'Property Band gap' 5 | }, { 6 | 'inputs': ['formula', 'Temperature (Property Band gap)'], 7 | 'output': 'Property Band gap' 8 | }, { 9 | 'inputs': ['formula'], 10 | 'output': 'Property Color' 11 | },{ 12 | 'inputs': ['formula', 'Property Band gap'], 13 | 'output': 'Property Color' 14 | }, 15 | ] 16 | -------------------------------------------------------------------------------- /docs/source/code_samples/views/data_view_builder.py: -------------------------------------------------------------------------------- 1 | from citrination_client import CategoricalDescriptor, InorganicDescriptor, RealDescriptor 2 | from citrination_client.views.data_view_builder import DataViewBuilder 3 | from citrination_client.views.client import DataViewsClient 4 | from os import environ 5 | 6 | # Note: for the purposes of this example, environ["CITRINATION_SITE"] is 7 | # https://citrination.com 8 | client = DataViewsClient(environ["CITRINATION_API_KEY"], environ["CITRINATION_SITE"]) 9 | 10 | dv_builder = DataViewBuilder() 11 | dv_builder.dataset_ids([1160]) 12 | 13 | colors = [ 14 | 'Yellow', 'Pale Yellow', 'Violet', 'Gray', 'Amber', 'Orange-Red', 'Dark Brown', 15 | 'Red', 'Blue', 'White', 'Red-Yellow', 'Brown', 'Black', 'Ocher', 'Bluish', 16 | 'Bronze', 'Light Gray', 'Dark Green', 'Yellow-White', 'Copper-Red', 17 | 'Brown-Black', 'Yellow-Orange', 'Orange', 'Dark Gray', 'Dark Red' 18 | ] 19 | 20 | dv_builder.add_descriptor( 21 | InorganicDescriptor('formula'), 'input' 22 | ) 23 | dv_builder.add_descriptor( 24 | RealDescriptor('Temperature (Property Band gap)', '0.0', '1946.0'), 'input' 25 | ) 26 | dv_builder.add_descriptor( 27 | RealDescriptor('Temperature (Property Color)', '0.0', '1946.0'), 'input' 28 | ) 29 | dv_builder.add_descriptor( 30 | RealDescriptor(u'Property Band gap', '0.0', '29.0'), 'latentVariable' 31 | ) 32 | dv_builder.add_descriptor( 33 | CategoricalDescriptor(u'Property Color', colors), 'output' 34 | ) 35 | 36 | # Converts the DataViewBuilder instance to a dictionary "config" object 37 | data_view_config = dv_builder.build() 38 | 39 | # Create a data view 40 | data_view_id = client.create( 41 | data_view_config, 'My dataview name', 'The data view description' 42 | ) 43 | 44 | # Or update an existing data view 45 | client.update(data_view_id, data_view_config) 46 | -------------------------------------------------------------------------------- /docs/source/code_samples/views/data_view_builder_formulation_desc.py: -------------------------------------------------------------------------------- 1 | client = CitrinationClient(environ["CITRINATION_API_KEY"]) 2 | 3 | builder = DataViewBuilder() 4 | builder.dataset_ids([187195]) 5 | 6 | formulation_desc = FormulationDescriptor("Formulation (idealMassPercent)") 7 | builder.add_formulation_descriptor(formulation_desc, client.data_views) 8 | -------------------------------------------------------------------------------- /docs/source/code_samples/views/data_view_builder_relation_graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CitrineInformatics/python-citrination-client/2cda4dd2a600224acd4e19ae2df66a922dd93342/docs/source/code_samples/views/data_view_builder_relation_graph.png -------------------------------------------------------------------------------- /docs/source/code_samples/views/data_view_builder_relations.py: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | 'inputs': [ 4 | 'formula', 5 | 'Temperature (Property Band gap)' 6 | ], 7 | 'output': 'Property Band gap' 8 | }, { 9 | 'inputs': [ 10 | 'formula', 11 | 'Temperature (Property Color)' 12 | ], 13 | 'output': 'Property Color' 14 | },{ 15 | 'inputs': [ 16 | 'formula', 17 | 'Temperature (Property Band gap)', 18 | 'Property Band gap' 19 | ], 20 | 'output': 'Property Color' 21 | }, 22 | ] 23 | -------------------------------------------------------------------------------- /docs/source/code_samples/views/descriptor_keys.py: -------------------------------------------------------------------------------- 1 | from citrination_client.views.client import DataViewsClient 2 | from os import environ 3 | 4 | # Note: for the purposes of this example, environ["CITRINATION_SITE"] is 5 | # https://citrination.com 6 | client = DataViewsClient(environ["CITRINATION_API_KEY"], environ["CITRINATION_SITE"]) 7 | 8 | # Get an array of descriptor keys for dataset id 1160 9 | dataset_ids = ['1160'] 10 | descriptor_keys = client.search_template.get_available_columns(dataset_ids) 11 | print(descriptor_keys) 12 | 13 | # ['formula', 14 | # 'Property Lasing', 15 | # 'Temperature (Property Lasing)', 16 | # 'Property Electroluminescence', 17 | # 'Temperature (Property Electroluminescence)', 18 | # 'Property Temperature derivative of band gap', 19 | # 'Temperature (Property Temperature derivative of band gap)', 20 | # 'Transition (Property Temperature derivative of band gap)', 21 | # 'Electric field polarization (Property Temperature derivative of band gap)', 22 | # 'Property Phase', 23 | # 'Property Photoluminescence', 24 | # 'Temperature (Property Photoluminescence)', 25 | # 'Property Thermoluminescence', 26 | # 'Temperature (Property Thermoluminescence)', 27 | # 'Property Morphology', 28 | # 'Property Mechanical luminescence', 29 | # 'Temperature (Property Mechanical luminescence)', 30 | # 'Property Cathodoluminescence', 31 | # 'Temperature (Property Cathodoluminescence)', 32 | # 'Property Band gap', 33 | # 'Temperature (Property Band gap)', 34 | # 'Transition (Property Band gap)', 35 | # 'Electric field polarization (Property Band gap)', 36 | # 'Property Crystallinity', 37 | # 'Property Color', 38 | # 'Temperature (Property Color)'] 39 | -------------------------------------------------------------------------------- /docs/source/code_samples/views/fetching_ml_config_defaults_1.py: -------------------------------------------------------------------------------- 1 | from citrination_client.views.client import DataViewsClient 2 | from os import environ 3 | 4 | # Note: for the purposes of this example, environ["CITRINATION_SITE"] is 5 | # https://citrination.com 6 | client = DataViewsClient(environ["CITRINATION_API_KEY"], environ["CITRINATION_SITE"]) 7 | 8 | dataset_ids = ['1160'] 9 | # Get the ml config defaults for all descriptor keys in the datasets 10 | data_view_config = client.create_ml_configuration_from_datasets(dataset_ids) 11 | 12 | print(data_view_config) 13 | # { 14 | # "dataset_ids": [ 15 | # 1160 16 | # ], 17 | # "group_by": [], 18 | # "model_type": "default", 19 | # "descriptors": [ 20 | # { 21 | # "category": "Real", 22 | # "descriptor_key": "Temperature (Property Thermoluminescence)", 23 | # "units": "", 24 | # "lower_bound": 0, 25 | # "upper_bound": 1746 26 | # }, 27 | # { 28 | # "category": "Categorical", 29 | # "descriptor_key": "Property Crystallinity", 30 | # "descriptor_values": [ 31 | # "Single crystalline", 32 | # "Polycrystalline", 33 | # "Amorphous" 34 | # ], 35 | # "finite_set": True 36 | # }, 37 | # ... 38 | # (truncated for documentation) 39 | # ... 40 | # ], 41 | # "builder": "simple", 42 | # "roles": { 43 | # "Temperature (Property Thermoluminescence)": "input", 44 | # "Property Temperature derivative of band gap": "output", 45 | # "Property Crystallinity": "output", 46 | # "Electric field polarization (Property Band gap)": "input", 47 | # ... 48 | # (truncated for documentation) 49 | # ... 50 | # } 51 | # } 52 | -------------------------------------------------------------------------------- /docs/source/code_samples/views/fetching_ml_config_defaults_2.py: -------------------------------------------------------------------------------- 1 | descriptor_keys = [ 2 | 'formula', 3 | 'Property Band gap', 4 | 'Property Color', 5 | 'Temperature (Property Band gap)', 6 | 'Temperature (Property Color)' 7 | ] 8 | data_view_config['descriptors'] = list( 9 | filter( 10 | lambda d: d['descriptor_key'] in descriptor_keys, 11 | data_view_config['descriptors'] 12 | ) 13 | ) 14 | data_view_config['roles'] = { 15 | key: data_view_config['roles'][key] for key in descriptor_keys 16 | } 17 | -------------------------------------------------------------------------------- /docs/source/code_samples/views/fetching_ml_config_defaults_3.py: -------------------------------------------------------------------------------- 1 | print(data_view_config) 2 | # { 'dataset_ids': ['1160'], 3 | # 'group_by': [], 4 | # 'model_type': 'default', 5 | # 'descriptors': [ 6 | # { 7 | # 'category': 'Real', 8 | # 'descriptor_key': 'Temperature (Property Band gap)', 9 | # 'units': '', 10 | # 'lower_bound': 0.0, 11 | # 'upper_bound': 1946.0 12 | # }, { 13 | # 'category': 'Categorical', 14 | # 'descriptor_key': 'Property Color', 15 | # 'descriptor_values': [ 16 | # 'Yellow', 'Pale Yellow', 'Violet', 'Gray', 'Amber', 'Orange-Red', 17 | # 'Dark Brown', 'Red', 'Blue', 'White', 'Red-Yellow', 'Brown', 18 | # 'Black', 'Ocher', 'Bluish', 'Bronze', 'Light Gray', 'Dark Green', 19 | # 'Yellow-White', 'Copper-Red', 'Brown-Black', 'Yellow-Orange', 20 | # 'Orange', 'Dark Gray', 'Dark Red' 21 | # ], 22 | # 'finite_set': True 23 | # }, { 24 | # 'category': 'Real', 25 | # 'descriptor_key': 'Temperature (Property Color)', 26 | # 'units': '', 27 | # 'lower_bound': 0.0, 28 | # 'upper_bound': 1746.0 29 | # }, { 30 | # 'category': 'Real', 31 | # 'descriptor_key': 'Property Band gap', 32 | # 'units': '', 33 | # 'lower_bound': 0.0, 34 | # 'upper_bound': 29.0 35 | # }, { 36 | # 'category': 'Inorganic', 37 | # 'descriptor_key': 'formula', 38 | # 'threshold': 1.0 39 | # } 40 | # ], 41 | # 'builder': 'simple', 42 | # 'roles': { 43 | # 'formula': 'input', 44 | # 'Property Band gap': 'output', 45 | # 'Property Color': 'output', 46 | # 'Temperature (Property Band gap)': 'input', 47 | # 'Temperature (Property Color)': 'input' 48 | # } 49 | # } 50 | 51 | # Create a data view 52 | data_view_id = client.create( 53 | data_view_config, 'My dataview name', 'The data view description' 54 | ) 55 | 56 | # Or update an existing data view 57 | client.update(data_view_id, data_view_config) 58 | -------------------------------------------------------------------------------- /docs/source/code_samples/views/instantiation.py: -------------------------------------------------------------------------------- 1 | from citrination_client import CitrinationClient 2 | from os import environ 3 | 4 | client = CitrinationClient(environ["CITRINATION_API_KEY"], environ["CITRINATION_SITE"]) 5 | data_views_client = client.data_views 6 | 7 | # Alternatively, import and instantiate the data views client directly 8 | from citrination_client.views.client import DataViewsClient 9 | data_views_client = DataViewsClient( 10 | environ["CITRINATION_API_KEY"], environ["CITRINATION_SITE"] 11 | ) 12 | -------------------------------------------------------------------------------- /docs/source/code_samples/views/model_reports.py: -------------------------------------------------------------------------------- 1 | from citrination_client.views.client import DataViewsClient 2 | from os import environ 3 | 4 | # Note: for the purposes of this example, environ["CITRINATION_SITE"] is 5 | # https://citrination.com 6 | client = DataViewsClient(environ["CITRINATION_API_KEY"], environ["CITRINATION_SITE"]) 7 | reports = client.get_model_reports(524) 8 | 9 | print([str(report) for report in reports]) 10 | # ["", 11 | # "", 12 | # ""] 13 | 14 | # Crystallinity is a Categorical output 15 | print(reports[0].performance) 16 | # {'ndme_f1': 0.7513840971831497} 17 | 18 | print(reports[0].model_settings) 19 | # {'Algorithm': 'Ensemble of non-linear estimators', 20 | # 'Maximum tree depth': 30, 21 | # 'Minimum samples per leaf': 1, 22 | # 'Number of cross-validation folds': 3, 23 | # 'Number of estimators': 194} 24 | 25 | print(reports[0].feature_importances) 26 | # [{'name': 'mean of Non-dimensional work function for Chemical formula', 27 | # 'value': 0.016694057309452427}, 28 | # {'name': 'mean of DFT volume ratio for Chemical formula', 29 | # 'value': 0.02425121569638139}, 30 | # {'name': 'mean of Non-dimensional liquid range for Chemical formula', 31 | # 'value': 0.024088896494558795}, 32 | # {'name': 'mean of Radius of d orbitals for Chemical formula', 33 | # 'value': 0.018493567944483043}, 34 | # ... 35 | # ] 36 | 37 | # Band Gap is a Real output, hence why it may have different keys 38 | print(reports[2].performance) 39 | # {'ndme': 0.35374524040321054, 40 | # 'ndme_stderr': 0.008224021869691423, 41 | # 'rmse': 0.8023624551013153, 42 | # 'rmse_stderr': 0.018653668302790923, 43 | # 'uc_fraction': 0.5793304221251819, 44 | # 'uc_rmsse': 1.9191506842755264} 45 | 46 | print(reports[2].model_settings) 47 | # {'Algorithm': 'Ensemble of non-linear estimators', 48 | # 'Leaf model': 'Mean', 49 | # 'Maximum tree depth': 30, 50 | # 'Minimum samples per leaf': 1, 51 | # 'Number of cross-validation folds': 3, 52 | # 'Number of estimators': 148, 53 | # 'Uses jackknife method of uncertainty estimation': True} 54 | 55 | print(reports[2].feature_importances) 56 | # [{'name': 'mean of Non-dimensional work function for Chemical formula', 57 | # 'value': 0.04867717383587202}, 58 | # {'name': 'mean of DFT volume ratio for Chemical formula', 59 | # 'value': 0.008961689534579438}, 60 | # {'name': 'mean of Non-dimensional liquid range for Chemical formula', 61 | # 'value': 0.006946688158984557}, 62 | # ... 63 | # ] 64 | -------------------------------------------------------------------------------- /docs/source/code_samples/views/relation_graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CitrineInformatics/python-citrination-client/2cda4dd2a600224acd4e19ae2df66a922dd93342/docs/source/code_samples/views/relation_graph.png -------------------------------------------------------------------------------- /docs/source/code_samples/views/relation_graphs.py: -------------------------------------------------------------------------------- 1 | from citrination_client.views.client import DataViewsClient 2 | from dagre_py.core import plot 3 | from os import environ 4 | 5 | # Note: for the purposes of this example, environ["CITRINATION_SITE"] is 6 | # https://citrination.com 7 | client = DataViewsClient(environ["CITRINATION_API_KEY"], environ["CITRINATION_SITE"]) 8 | 9 | # Example using https://citrination.com/data_views/12329/data_summary 10 | # Returns a dict containing `nodes` and `edges` 11 | relation_graph = client.get_relation_graph(12329) 12 | 13 | print(relation_graph) 14 | # {'edges': [{'source': 'formula', 'target': 'ML Model: 1'}, 15 | # {'source': 'ML Model: 1', 'target': 'Property Bulk modulus'}], 16 | # 'nodes': [{'attributes': {'style': {'fill': '#ff8200', 'stroke': '#453536'}}, 17 | # 'description': 'Featurized to: \n' 18 | # '-- mean of Packing density\n' 19 | # '-- mean of Liquid range\n' 20 | # '...', (truncated for documentation purposes) 21 | # 'label': 'formula'}, 22 | # {'attributes': {'style': {'fill': '#ff8200', 'stroke': '#453536'}}, 23 | # 'description': 'Error Metrics\n' 24 | # '- Root mean squared error (GPa) (0.0 for a ' 25 | # 'perfect model) = 50.427349\n' 26 | # '- Uncertainty calibration: root mean square of ' 27 | # 'standardized errors (1.0 is perfectly calibrated) ' 28 | # '= 2.069455\n' 29 | # '...', (truncated for documentation purposes) 30 | # 'label': 'Property Bulk modulus'}, 31 | # {'attributes': {'style': {'fill': '#78be20', 'stroke': '#453536'}}, 32 | # 'description': 'Model Type: Lolo\nSample Count: 30\n', 33 | # 'label': 'ML Model: 1'}]} 34 | 35 | # Uses ``dagre_py`` to create a visualization of the relation graph in jupyter. 36 | # See screenshot below. 37 | plot(relation_graph) 38 | -------------------------------------------------------------------------------- /docs/source/modules/citrination_client.rst: -------------------------------------------------------------------------------- 1 | Citrination Client 2 | --------------------------------- 3 | 4 | .. automodule:: citrination_client.client 5 | :members: 6 | :undoc-members: 7 | -------------------------------------------------------------------------------- /docs/source/modules/data/data_client.rst: -------------------------------------------------------------------------------- 1 | Data Client 2 | =========== 3 | 4 | .. automodule:: citrination_client.data.client 5 | :members: 6 | 7 | .. automodule:: citrination_client.data.upload_result 8 | :members: -------------------------------------------------------------------------------- /docs/source/modules/data/datasets.rst: -------------------------------------------------------------------------------- 1 | Datasets 2 | ======== 3 | 4 | .. automodule:: citrination_client.data.dataset 5 | :members: 6 | 7 | .. automodule:: citrination_client.data.dataset_file 8 | :members: 9 | 10 | .. automodule:: citrination_client.data.dataset_version 11 | :members: -------------------------------------------------------------------------------- /docs/source/modules/data/ingest.rst: -------------------------------------------------------------------------------- 1 | Ingest 2 | ======== 3 | 4 | .. automodule:: citrination_client.data.ingest.ingester 5 | :members: 6 | 7 | .. automodule:: citrination_client.data.ingest.ingester_list 8 | :members: 9 | -------------------------------------------------------------------------------- /docs/source/modules/data_management.rst: -------------------------------------------------------------------------------- 1 | Data Management 2 | =============== 3 | 4 | .. toctree:: 5 | :glob: 6 | 7 | data/* 8 | -------------------------------------------------------------------------------- /docs/source/modules/models.rst: -------------------------------------------------------------------------------- 1 | Models 2 | ====== 3 | 4 | .. toctree:: 5 | :glob: 6 | 7 | models/* -------------------------------------------------------------------------------- /docs/source/modules/models/design.rst: -------------------------------------------------------------------------------- 1 | Design 2 | ====== 3 | 4 | .. automodule:: citrination_client.models.design.process_status 5 | :members: 6 | :undoc-members: 7 | 8 | .. automodule:: citrination_client.models.design.design_results 9 | :members: 10 | :undoc-members: 11 | -------------------------------------------------------------------------------- /docs/source/modules/models/models_client.rst: -------------------------------------------------------------------------------- 1 | Model Client 2 | ============ 3 | 4 | .. automodule:: citrination_client.models.client 5 | :members: -------------------------------------------------------------------------------- /docs/source/modules/models/predictions.rst: -------------------------------------------------------------------------------- 1 | Predictions 2 | =========== 3 | 4 | .. automodule:: citrination_client.models.predicted_value 5 | :members: 6 | 7 | .. automodule:: citrination_client.models.prediction_result 8 | :members: -------------------------------------------------------------------------------- /docs/source/modules/models/tsne.rst: -------------------------------------------------------------------------------- 1 | TSNE 2 | ==== 3 | 4 | .. automodule:: citrination_client.models.tsne 5 | :members: 6 | 7 | .. automodule:: citrination_client.models.projection 8 | :members: -------------------------------------------------------------------------------- /docs/source/modules/modules.rst: -------------------------------------------------------------------------------- 1 | Module Documentation 2 | ==================== 3 | 4 | .. toctree:: 5 | 6 | citrination_client 7 | data_management 8 | models 9 | search 10 | views -------------------------------------------------------------------------------- /docs/source/modules/search.rst: -------------------------------------------------------------------------------- 1 | Search 2 | ====== 3 | 4 | .. toctree:: 5 | :glob: 6 | 7 | search/* -------------------------------------------------------------------------------- /docs/source/modules/search/citrination_client.search.rst: -------------------------------------------------------------------------------- 1 | Search Module 2 | ================================== 3 | 4 | Search Client 5 | ---------------------------------------- 6 | 7 | .. automodule:: citrination_client.search.client 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | 12 | Query Encoder 13 | ------------------------------------------------ 14 | 15 | .. automodule:: citrination_client.search.query_encoder 16 | :members: 17 | :undoc-members: 18 | :show-inheritance: 19 | -------------------------------------------------------------------------------- /docs/source/modules/search/core_query.rst: -------------------------------------------------------------------------------- 1 | Search - Core - Query package 2 | ============================================= 3 | 4 | Boolean Filter 5 | ------------------------------------------------------------ 6 | 7 | .. automodule:: citrination_client.search.core.query.boolean_filter 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | 12 | Data Query module 13 | -------------------------------------------------------- 14 | 15 | .. automodule:: citrination_client.search.core.query.data_query 16 | :members: 17 | :undoc-members: 18 | :show-inheritance: 19 | 20 | Data Scope module 21 | -------------------------------------------------------- 22 | 23 | .. automodule:: citrination_client.search.core.query.data_scope 24 | :members: 25 | :undoc-members: 26 | :show-inheritance: 27 | 28 | Filter module 29 | --------------------------------------------------- 30 | 31 | .. automodule:: citrination_client.search.core.query.filter 32 | :members: 33 | :undoc-members: 34 | :show-inheritance: 35 | 36 | Multi Query module 37 | --------------------------------------------------------- 38 | 39 | .. automodule:: citrination_client.search.core.query.multi_query 40 | :members: 41 | :undoc-members: 42 | :show-inheritance: 43 | -------------------------------------------------------------------------------- /docs/source/modules/search/core_result.rst: -------------------------------------------------------------------------------- 1 | Search - Core - Result package 2 | ============================================== 3 | 4 | Base Search Result 5 | ------------------------------------------------------------------ 6 | 7 | .. automodule:: citrination_client.search.core.result.base_search_result 8 | :members: 9 | :show-inheritance: 10 | -------------------------------------------------------------------------------- /docs/source/modules/search/dataset_query.rst: -------------------------------------------------------------------------------- 1 | Dataset Queries 2 | ================================================ 3 | 4 | Dataset Query 5 | -------------------------------------------------------------- 6 | 7 | .. automodule:: citrination_client.search.dataset.query.dataset_query 8 | :members: 9 | :show-inheritance: 10 | 11 | Dataset Returning Query 12 | ------------------------------------------------------------------------- 13 | 14 | .. automodule:: citrination_client.search.dataset.query.dataset_returning_query 15 | :members: 16 | :show-inheritance: -------------------------------------------------------------------------------- /docs/source/modules/search/dataset_result.rst: -------------------------------------------------------------------------------- 1 | Dataset Results 2 | ================================================= 3 | 4 | Dataset Multi Search Result 5 | ------------------------------------------------------------------------------- 6 | 7 | .. automodule:: citrination_client.search.dataset.result.dataset_multi_search_result 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | 12 | Dataset Multi Search Result Element 13 | ---------------------------------------------------------------------------------------- 14 | 15 | .. automodule:: citrination_client.search.dataset.result.dataset_multi_search_result_element 16 | :members: 17 | :undoc-members: 18 | :show-inheritance: 19 | 20 | Dataset Search Hit 21 | --------------------------------------------------------------------- 22 | 23 | .. automodule:: citrination_client.search.dataset.result.dataset_search_hit 24 | :members: 25 | :undoc-members: 26 | :show-inheritance: 27 | 28 | Dataset Search Result 29 | ------------------------------------------------------------------------ 30 | 31 | .. automodule:: citrination_client.search.dataset.result.dataset_search_result 32 | :members: 33 | :undoc-members: 34 | :show-inheritance: 35 | 36 | -------------------------------------------------------------------------------- /docs/source/modules/search/file_query.rst: -------------------------------------------------------------------------------- 1 | File Queries 2 | ============================================= 3 | 4 | File Query 5 | -------------------------------------------------------- 6 | 7 | .. automodule:: citrination_client.search.file.query.file_query 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | 12 | File Returning Query 13 | ------------------------------------------------------------------- 14 | 15 | .. automodule:: citrination_client.search.file.query.file_returning_query 16 | :members: 17 | :undoc-members: 18 | :show-inheritance: 19 | -------------------------------------------------------------------------------- /docs/source/modules/search/file_result.rst: -------------------------------------------------------------------------------- 1 | File Results 2 | ============================================== 3 | 4 | File Multi Search Result 5 | ------------------------------------------------------------------------- 6 | 7 | .. automodule:: citrination_client.search.file.result.file_multi_search_result 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | 12 | File Multi Search Result Element 13 | ---------------------------------------------------------------------------------- 14 | 15 | .. automodule:: citrination_client.search.file.result.file_multi_search_result_element 16 | :members: 17 | :undoc-members: 18 | :show-inheritance: 19 | 20 | File Search Hit 21 | --------------------------------------------------------------- 22 | 23 | .. automodule:: citrination_client.search.file.result.file_search_hit 24 | :members: 25 | :undoc-members: 26 | :show-inheritance: 27 | 28 | File Search Result 29 | ------------------------------------------------------------------ 30 | 31 | .. automodule:: citrination_client.search.file.result.file_search_result 32 | :members: 33 | :undoc-members: 34 | :show-inheritance: 35 | -------------------------------------------------------------------------------- /docs/source/modules/search/pif_chemical_query.rst: -------------------------------------------------------------------------------- 1 | PIF Query - Chemical 2 | ===================================================== 3 | 4 | Chemical Field Query 5 | --------------------------------------------------------------------------- 6 | 7 | .. automodule:: citrination_client.search.pif.query.chemical.chemical_field_query 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | 12 | Chemical Filter 13 | --------------------------------------------------------------------- 14 | 15 | .. automodule:: citrination_client.search.pif.query.chemical.chemical_filter 16 | :members: 17 | :undoc-members: 18 | :show-inheritance: 19 | 20 | Composition Query 21 | ----------------------------------------------------------------------- 22 | 23 | .. automodule:: citrination_client.search.pif.query.chemical.composition_query 24 | :members: 25 | :undoc-members: 26 | :show-inheritance: 27 | -------------------------------------------------------------------------------- /docs/source/modules/search/pif_query.rst: -------------------------------------------------------------------------------- 1 | PIF Query 2 | ============================================ 3 | 4 | Extraction Sort 5 | ------------------------------------------------------------ 6 | 7 | .. automodule:: citrination_client.search.pif.query.extraction_sort 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | 12 | PIF System Query 13 | -------------------------------------------------------------- 14 | 15 | .. automodule:: citrination_client.search.pif.query.pif_system_query 16 | :members: 17 | :undoc-members: 18 | :show-inheritance: 19 | 20 | PIF System Returning Query 21 | ------------------------------------------------------------------------- 22 | 23 | .. automodule:: citrination_client.search.pif.query.pif_system_returning_query 24 | :members: 25 | :undoc-members: 26 | :show-inheritance: 27 | -------------------------------------------------------------------------------- /docs/source/modules/search/pif_result.rst: -------------------------------------------------------------------------------- 1 | PIF Result 2 | ============================================= 3 | 4 | PIF Multi Search Result 5 | ----------------------------------------------------------------------- 6 | 7 | .. automodule:: citrination_client.search.pif.result.pif_multi_search_result 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | 12 | PIF Multi Search Result Element 13 | -------------------------------------------------------------------------------- 14 | 15 | .. automodule:: citrination_client.search.pif.result.pif_multi_search_result_element 16 | :members: 17 | :undoc-members: 18 | :show-inheritance: 19 | 20 | PIF Search Hit 21 | ------------------------------------------------------------- 22 | 23 | .. automodule:: citrination_client.search.pif.result.pif_search_hit 24 | :members: 25 | :undoc-members: 26 | :show-inheritance: 27 | 28 | PIF Search Result 29 | ---------------------------------------------------------------- 30 | 31 | .. automodule:: citrination_client.search.pif.result.pif_search_result 32 | :members: 33 | :undoc-members: 34 | :show-inheritance: 35 | -------------------------------------------------------------------------------- /docs/source/modules/views.rst: -------------------------------------------------------------------------------- 1 | Data Views 2 | ========== 3 | 4 | .. toctree:: 5 | :glob: 6 | 7 | views/* -------------------------------------------------------------------------------- /docs/source/modules/views/data_views_client.rst: -------------------------------------------------------------------------------- 1 | Data Views Client 2 | ================= 3 | 4 | .. automodule:: citrination_client.views.client 5 | :members: 6 | 7 | -------------------------------------------------------------------------------- /docs/source/modules/views/ml_config_builder.rst: -------------------------------------------------------------------------------- 1 | Data Views Config Builders 2 | ========================== 3 | 4 | .. automodule:: citrination_client.views.base_data_view_builder 5 | :members: 6 | 7 | .. automodule:: citrination_client.views.data_view_builder 8 | :members: 9 | 10 | .. automodule:: citrination_client.views.advanced_data_view_builder 11 | :members: 12 | 13 | .. automodule:: citrination_client.views.descriptors.descriptor 14 | :members: 15 | :undoc-members: 16 | 17 | .. automodule:: citrination_client.views.descriptors.alloy_composition_descriptor 18 | :members: 19 | :undoc-members: 20 | :show-inheritance: 21 | 22 | .. automodule:: citrination_client.views.descriptors.categorical_descriptor 23 | :members: 24 | :undoc-members: 25 | :show-inheritance: 26 | 27 | .. automodule:: citrination_client.views.descriptors.formulation_descriptor 28 | :members: 29 | :undoc-members: 30 | :show-inheritance: 31 | 32 | .. automodule:: citrination_client.views.descriptors.inorganic_descriptor 33 | :members: 34 | :undoc-members: 35 | :show-inheritance: 36 | 37 | .. automodule:: citrination_client.views.descriptors.organic_descriptor 38 | :members: 39 | :undoc-members: 40 | :show-inheritance: 41 | 42 | .. automodule:: citrination_client.views.descriptors.real_descriptor 43 | :members: 44 | :undoc-members: 45 | :show-inheritance: 46 | -------------------------------------------------------------------------------- /docs/source/modules/views/model_report.rst: -------------------------------------------------------------------------------- 1 | Model Report 2 | ================= 3 | 4 | .. automodule:: citrination_client.views.model_report 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/source/tutorial/initialization.rst: -------------------------------------------------------------------------------- 1 | Initialization 2 | ============== 3 | 4 | To instantiate ``CitrinationClient``, you will need to provide an 5 | API key and the host URL of the Citrination site you are trying 6 | to interact with. 7 | 8 | API Key 9 | ------- 10 | 11 | You can find your API key by navigation to ``https://citrination.com``, 12 | or your private Citrination deployment, logging in, and using the menu 13 | in the top right to visit your ``Account Settings`` page. 14 | 15 | .. attention:: 16 | Your API key allows you (and anyone who knows it) to access your account and your data. Use caution when storing it on your filesystem. 17 | 18 | Host URL 19 | -------- 20 | 21 | The second parameter to ``CitrinationClient`` instantiation is the URL of the Citrination site you are trying to interact with. By default it is ``https://citrination.com``, but you can pass in any Citrination site. 22 | 23 | API Key From Environment 24 | ------------------------ 25 | 26 | Set the following values and ``CitrinationClient`` will pull in your authentication values from the environment: 27 | 28 | * ``CITRINATION_API_KEY`` 29 | * ``CITRINATION_SITE`` 30 | 31 | API Key From .citrination Folder 32 | -------------------------------- 33 | 34 | If you use PyCC to interact with multiple Citrination sites, or simply don't want to use your environment to specify authentication information for ``CitrinationClient``, you may use a credentials file to keep track of your Citrination API key. 35 | 36 | #. Create a directory in your home folder called ``.citrination`` 37 | #. Create a file in the ``.citrination`` folder called ``credentials`` 38 | 39 | Use the following format to store credential information:: 40 | 41 | default: 42 | api_key: my_default_profile_key 43 | site: my_default_profile_site 44 | my_site: 45 | api_key: my_test_profile_key 46 | site: https://mysite.citrination.com 47 | 48 | 49 | In the absence of any other configuration, when you initialize ``CitrinationClient`` with no parameters, the credentials from the ``default`` stanza will be used. 50 | 51 | To specify which credentials set will be used, set the environment variable ``CITRINATION_PROFILE`` to be equal to the name of the desired credentials stanza. 52 | 53 | 54 | API Key In Direct Initialization 55 | -------------------------------- 56 | 57 | You may also pass your API key in directly as a constructor argument to the client class (along with the Citrination site you prefer). 58 | 59 | .. literalinclude:: /code_samples/general/initialization.py 60 | 61 | .. attention:: 62 | Remember to be careful not to share your API key accidentally by including it in a shared script! 63 | 64 | Inititialization Mode Priority 65 | ------------------------------ 66 | 67 | The three methods of initialization outlined above are prioritized in the following order: 68 | 69 | #. API Key In Direct Initialization 70 | #. API Key From Environment 71 | #. API Key From .citrination Folder 72 | 73 | In other words, if you pass in an API key directly on instantiation, but also have it defined in the `.citrination/credentials` file, the API key you passed in directly will be used. -------------------------------------------------------------------------------- /docs/source/tutorial/introduction.rst: -------------------------------------------------------------------------------- 1 | Introduction 2 | ============ 3 | 4 | This guide will cover some common PyCC functions their basic use cases. 5 | 6 | The biggest structural difference between older versions of PyCC and v4.0.0 and 7 | later is that the client is divided into sub-clients which each handle a specific 8 | set of functionality. Specifically, an instance of ``CitrinationClient`` will 9 | have as members ``data``, ``data_views``, ``models``, and ``search``, which 10 | each expose subsets of the functionality. 11 | -------------------------------------------------------------------------------- /docs/source/tutorial/models_examples.rst: -------------------------------------------------------------------------------- 1 | Models Client Examples 2 | ====================== 3 | 4 | The ``ModelsClient`` class encapsulates interactions with 5 | trained models on Citrination. 6 | 7 | Predict 8 | ------- 9 | 10 | The ``.predict()`` method allows you to make a prediction with 11 | a model by referencing the ID of a Data View. 12 | 13 | The predict method takes a list of candidates, each of which is a dictionary of the inputs required for that data view. You may also specify the prediction method (either ``from_distribution`` or ``scalar``). 14 | 15 | .. literalinclude:: /code_samples/models/predict.py 16 | 17 | t-SNE 18 | ----- 19 | 20 | You can retrieve the t-SNE analysis for a model by calling the 21 | ``.tsne()`` method with the ID of a data view with trained models. 22 | 23 | The result of this call will be a ``Tsne`` instance which contains 24 | projections for each of the the outputs for the models trained on the data view. 25 | 26 | .. literalinclude:: /code_samples/models/tsne.py 27 | 28 | 29 | Design 30 | ------ 31 | 32 | The ``.submit_design_run()`` method allows you to start a design run. 33 | 34 | .. literalinclude:: /code_samples/models/design.py 35 | -------------------------------------------------------------------------------- /docs/source/tutorial/search_examples.rst: -------------------------------------------------------------------------------- 1 | .. attention:: 2 | The search interface for Citrination is powerful but complex. This guide is still a work in progress. 3 | 4 | Search Client Examples 5 | ====================== 6 | 7 | The ``SearchClient`` class allows you to run search queries against 8 | Citrination. The search client utilizes a deeply recursive query structure 9 | that enables complex sets of criteria to be applied. 10 | 11 | For more detailed information about the structure of search queries, consult the documentation found here: (https://citrineinformatics.github.io/api-documentation/#tag/search 12 | 13 | Basic Usage 14 | ----------- 15 | 16 | You may execute search queries which return individual records (equivalent to running search from the landing page on Citrination), or datasets by using the ``pif_search`` or ``dataset_search`` methods on the ``SearchClient`` respectively. 17 | 18 | .. literalinclude:: /code_samples/search/pif_search.py 19 | 20 | .. literalinclude:: /code_samples/search/dataset_search.py 21 | 22 | Note that ``dataset_search`` does not return the contents of the datasets, but instead returns the metadata for datasets whose contents match the criteria applied by your search. In other words, if you pass a query to ``dataset_search`` which applies the criteria that matching records must contain a property called "Property Band gap", the resulting hits will be datasets which contain records satisfying that criteria. 23 | 24 | Simple Query Generation 25 | ----------------------- 26 | 27 | The search client provides a method called ``generate_simple_chemical_query`` which simplifies the interface for creating a new query. If you only need to apply a simple set of constraints, this can be a quick way to generate a query. 28 | 29 | .. literalinclude:: /code_samples/search/generate_simple_query.py 30 | 31 | -------------------------------------------------------------------------------- /docs/source/tutorial/tutorial.rst: -------------------------------------------------------------------------------- 1 | Tutorial 2 | ======== 3 | 4 | .. toctree:: 5 | 6 | introduction 7 | initialization 8 | data_examples 9 | models_examples 10 | search_examples 11 | view_examples 12 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pyyaml==5.1.2 2 | pypif==1.1.6 3 | requests==2.20.0 4 | six==1.10.0 5 | requests_mock 6 | pytest 7 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.md 3 | [bdist_wheel] 4 | universal=1 -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | setup(name='citrination-client', 4 | version='6.5.1', 5 | url='http://github.com/CitrineInformatics/python-citrination-client', 6 | description='Python client for accessing the Citrination api', 7 | packages=find_packages(exclude=('docs')), 8 | install_requires=[ 9 | 'requests>=2.20.0,<3', 10 | 'pypif', 11 | 'six<2', 12 | 'pyyaml>=5.1.2' 13 | ], 14 | extras_require={ 15 | "dev": [ 16 | 'sphinx_rtd_theme', 17 | 'sphinx', 18 | ], 19 | "test": [ 20 | 'requests_mock', 21 | 'pytest', 22 | ] 23 | }) 24 | --------------------------------------------------------------------------------