From eecc878b5953366944fb7e7e461756b2fc73f3ba Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Thu, 6 Jul 2023 17:12:09 +0200 Subject: [PATCH 01/86] Addded meta field to entry model --- openapi/index_openapi.json | 24 ++- openapi/openapi.json | 146 ++++++++++-------- optimade/models/entries.py | 17 +- optimade/server/data/test_structures.json | 7 + optimade/server/mappers/entries.py | 8 +- .../test_data/test_good_structures.json | 7 + tests/server/query_params/conftest.py | 5 +- 7 files changed, 138 insertions(+), 76 deletions(-) diff --git a/openapi/index_openapi.json b/openapi/index_openapi.json index 9f5692009..e247ae069 100644 --- a/openapi/index_openapi.json +++ b/openapi/index_openapi.json @@ -467,6 +467,18 @@ }, "description": "Minimum requirements to represent a relationship resource" }, + "EntryMetadata": { + "title": "EntryMetadata", + "type": "object", + "properties": { + "property_metadata": { + "title": "Property Metadata", + "type": "object", + "description": "A dictionary, where the keys are the names of the properties in the attributes field and the value is a dictionary containing the meta data for that property.\nDatabase-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes)." + } + }, + "description": "Contains the metadata for the attributes of an entry" + }, "EntryRelationships": { "title": "EntryRelationships", "type": "object", @@ -528,10 +540,10 @@ "title": "Meta", "allOf": [ { - "$ref": "#/components/schemas/Meta" + "$ref": "#/components/schemas/EntryMetadata" } ], - "description": "a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship." + "description": "A dictionary, containing entry and property-specific metadata for a given entry." }, "attributes": { "title": "Attributes", @@ -573,8 +585,8 @@ "type": "string", "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "format": "date-time", - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" } }, "description": "Contains key-value pairs representing the entry's properties." @@ -1137,10 +1149,10 @@ "title": "Meta", "allOf": [ { - "$ref": "#/components/schemas/Meta" + "$ref": "#/components/schemas/EntryMetadata" } ], - "description": "a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship." + "description": "A dictionary, containing entry and property-specific metadata for a given entry." }, "attributes": { "title": "Attributes", diff --git a/openapi/openapi.json b/openapi/openapi.json index 19c37f664..d23f8c4d0 100644 --- a/openapi/openapi.json +++ b/openapi/openapi.json @@ -1341,8 +1341,8 @@ } }, "description": "Index of the sites (0-based) that belong to each group for each assembly.\n\n- **Examples**:\n - `[[1], [2]]`: two groups, one with the second site, one with the third.\n - `[[1,2], [3]]`: one group with the second and third site, one with the fourth.", - "x-optimade-support": "must", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "must" }, "group_probabilities": { "title": "Group Probabilities", @@ -1351,8 +1351,8 @@ "type": "number" }, "description": "Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\nIt SHOULD sum to one.\nSee below for examples of how to specify the probability of the occurrence of a vacancy.\nThe possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.", - "x-optimade-support": "must", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "must" } }, "description": "A description of groups of sites that are statistically correlated.\n\n- **Examples** (for each entry of the assemblies list):\n - `{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}`: the first site and the second site never occur at the same time in the unit cell.\n Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.\n - `{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}`: the second and third site are either present together or not present; they form the first group of atoms for this assembly.\n The second group is formed by the fourth site. Sites of the first group (the second and the third) are never present at the same time as the fourth site.\n 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent)." @@ -1717,6 +1717,18 @@ }, "description": "errors are not allowed" }, + "EntryMetadata": { + "title": "EntryMetadata", + "type": "object", + "properties": { + "property_metadata": { + "title": "Property Metadata", + "type": "object", + "description": "A dictionary, where the keys are the names of the properties in the attributes field and the value is a dictionary containing the meta data for that property.\nDatabase-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes)." + } + }, + "description": "Contains the metadata for the attributes of an entry" + }, "EntryRelationships": { "title": "EntryRelationships", "type": "object", @@ -1778,10 +1790,10 @@ "title": "Meta", "allOf": [ { - "$ref": "#/components/schemas/Meta" + "$ref": "#/components/schemas/EntryMetadata" } ], - "description": "a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship." + "description": "A dictionary, containing entry and property-specific metadata for a given entry." }, "attributes": { "title": "Attributes", @@ -1823,8 +1835,8 @@ "type": "string", "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "format": "date-time", - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" } }, "description": "Contains key-value pairs representing the entry's properties." @@ -2246,10 +2258,10 @@ "title": "Meta", "allOf": [ { - "$ref": "#/components/schemas/Meta" + "$ref": "#/components/schemas/EntryMetadata" } ], - "description": "a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship." + "description": "A dictionary, containing entry and property-specific metadata for a given entry." }, "attributes": { "title": "Attributes", @@ -2648,8 +2660,8 @@ "type": "string", "description": "The name of the type of an entry.\n- **Type**: string.\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type and ID MUST be returned in response to a request for `//` under the versioned base URL.\n- **Example**: `\"structures\"`", "default": "references", - "x-optimade-support": "must", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "must" }, "links": { "title": "Links", @@ -2664,10 +2676,10 @@ "title": "Meta", "allOf": [ { - "$ref": "#/components/schemas/Meta" + "$ref": "#/components/schemas/EntryMetadata" } ], - "description": "a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship." + "description": "A dictionary, containing entry and property-specific metadata for a given entry." }, "attributes": { "$ref": "#/components/schemas/ReferenceResourceAttributes" @@ -2703,8 +2715,8 @@ "type": "string", "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "format": "date-time", - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" }, "authors": { "title": "Authors", @@ -2713,8 +2725,8 @@ "$ref": "#/components/schemas/Person" }, "description": "List of person objects containing the authors of the reference.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "editors": { "title": "Editors", @@ -2723,8 +2735,8 @@ "$ref": "#/components/schemas/Person" }, "description": "List of person objects containing the editors of the reference.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "doi": { "title": "Doi", @@ -2740,8 +2752,8 @@ "type": "string", "description": "The URL of the reference.", "format": "uri", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "address": { "title": "Address", @@ -3327,8 +3339,8 @@ "type": "string" }, "description": "MUST be a list of strings of all chemical elements composing this species. Each item of the list MUST be one of the following:\n\n- a valid chemical-element symbol, or\n- the special value `\"X\"` to represent a non-chemical element, or\n- the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\n\nIf any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.", - "x-optimade-support": "must", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "must" }, "concentration": { "title": "Concentration", @@ -3337,8 +3349,8 @@ "type": "number" }, "description": "MUST be a list of floats, with same length as `chemical_symbols`. The numbers represent the relative concentration of the corresponding chemical symbol in this species. The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\n\n- Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\n- Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\n\nNote that concentrations are uncorrelated between different site (even of the same species).", - "x-optimade-support": "must", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "must" }, "mass": { "title": "Mass", @@ -3347,8 +3359,8 @@ "type": "number" }, "description": "If present MUST be a list of floats expressed in a.m.u.\nElements denoting vacancies MUST have masses equal to 0.", - "x-optimade-support": "optional", "x-optimade-queryable": "optional", + "x-optimade-support": "optional", "x-optimade-unit": "a.m.u." }, "original_name": { @@ -3365,8 +3377,8 @@ "type": "string" }, "description": "If provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "nattached": { "title": "Nattached", @@ -3375,8 +3387,8 @@ "type": "integer" }, "description": "If provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the :field:`attached` key.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" } }, "description": "A list describing the species of the sites of this structure.\n\nSpecies can represent pure chemical elements, virtual-crystal atoms representing a\nstatistical occupation of a given site by multiple chemical elements, and/or a\nlocation to which there are attached atoms, i.e., atoms whose precise location are\nunknown beyond that they are attached to that position (frequently used to indicate\nhydrogen atoms attached to another element, e.g., a carbon with three attached\nhydrogens might represent a methyl group, -CH3).\n\n- **Examples**:\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]`: any site with this species is occupied by a Ti atom.\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]`: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.\n - `[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]`: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.\n - `[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]`: any site with this species is occupied by a carbon isotope with mass 12.\n - `[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]`: any site with this species is occupied by a carbon isotope with mass 13.\n - `[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]`: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms." @@ -3454,8 +3466,8 @@ "type": "string", "description": "The name of the type of an entry.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type `` and ID `` MUST be returned in response to a request for `//` under the versioned base URL.\n\n- **Examples**:\n - `\"structures\"`", "default": "structures", - "x-optimade-support": "must", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "must" }, "links": { "title": "Links", @@ -3470,10 +3482,10 @@ "title": "Meta", "allOf": [ { - "$ref": "#/components/schemas/Meta" + "$ref": "#/components/schemas/EntryMetadata" } ], - "description": "a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship." + "description": "A dictionary, containing entry and property-specific metadata for a given entry." }, "attributes": { "$ref": "#/components/schemas/StructureResourceAttributes" @@ -3524,8 +3536,8 @@ "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "format": "date-time", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" }, "elements": { "title": "Elements", @@ -3535,16 +3547,16 @@ }, "description": "The chemical symbols of the different elements present in the structure.\n\n- **Type**: list of strings.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The strings are the chemical symbols, i.e., either a single uppercase letter or an uppercase letter followed by a number of lowercase letters.\n - The order MUST be alphabetical.\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements_ratios`, if the latter is provided.\n - Note: This property SHOULD NOT contain the string \"X\" to indicate non-chemical elements or \"vacancy\" to indicate vacancies (in contrast to the field `chemical_symbols` for the `species` property).\n\n- **Examples**:\n - `[\"Si\"]`\n - `[\"Al\",\"O\",\"Si\"]`\n\n- **Query examples**:\n - A filter that matches all records of structures that contain Si, Al **and** O, and possibly other elements: `elements HAS ALL \"Si\", \"Al\", \"O\"`.\n - To match structures with exactly these three elements, use `elements HAS ALL \"Si\", \"Al\", \"O\" AND elements LENGTH 3`.\n - Note: length queries on this property can be equivalently formulated by filtering on the `nelements`_ property directly.", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" }, "nelements": { "title": "Nelements", "type": "integer", "description": "Number of different elements in the structure as an integer.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - MUST be equal to the lengths of the list properties `elements` and `elements_ratios`, if they are provided.\n\n- **Examples**:\n - `3`\n\n- **Querying**:\n - Note: queries on this property can equivalently be formulated using `elements LENGTH`.\n - A filter that matches structures that have exactly 4 elements: `nelements=4`.\n - A filter that matches structures that have between 2 and 7 elements: `nelements>=2 AND nelements<=7`.", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" }, "elements_ratios": { "title": "Elements Ratios", @@ -3554,16 +3566,16 @@ }, "description": "Relative proportions of different elements in the structure.\n\n- **Type**: list of floats\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - Composed by the proportions of elements in the structure as a list of floating point numbers.\n - The sum of the numbers MUST be 1.0 (within floating point accuracy)\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements`, if the latter is provided.\n\n- **Examples**:\n - `[1.0]`\n - `[0.3333333333333333, 0.2222222222222222, 0.4444444444444444]`\n\n- **Query examples**:\n - Note: Useful filters can be formulated using the set operator syntax for correlated values.\n However, since the values are floating point values, the use of equality comparisons is generally inadvisable.\n - OPTIONAL: a filter that matches structures where approximately 1/3 of the atoms in the structure are the element Al is: `elements:elements_ratios HAS ALL \"Al\":>0.3333, \"Al\":<0.3334`.", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" }, "chemical_formula_descriptive": { "title": "Chemical Formula Descriptive", "type": "string", "description": "The chemical formula for a structure as a string in a form chosen by the API implementation.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The chemical formula is given as a string consisting of properly capitalized element symbols followed by integers or decimal numbers, balanced parentheses, square, and curly brackets `(`,`)`, `[`,`]`, `{`, `}`, commas, the `+`, `-`, `:` and `=` symbols. The parentheses are allowed to be followed by a number. Spaces are allowed anywhere except within chemical symbols. The order of elements and any groupings indicated by parentheses or brackets are chosen freely by the API implementation.\n - The string SHOULD be arithmetically consistent with the element ratios in the `chemical_formula_reduced` property.\n - It is RECOMMENDED, but not mandatory, that symbols, parentheses and brackets, if used, are used with the meanings prescribed by [IUPAC's Nomenclature of Organic Chemistry](https://www.qmul.ac.uk/sbcs/iupac/bibliog/blue.html).\n\n- **Examples**:\n - `\"(H2O)2 Na\"`\n - `\"NaCl\"`\n - `\"CaCO3\"`\n - `\"CCaO3\"`\n - `\"(CH3)3N+ - [CH2]2-OH = Me3N+ - CH2 - CH2OH\"`\n\n- **Query examples**:\n - Note: the free-form nature of this property is likely to make queries on it across different databases inconsistent.\n - A filter that matches an exactly given formula: `chemical_formula_descriptive=\"(H2O)2 Na\"`.\n - A filter that does a partial match: `chemical_formula_descriptive CONTAINS \"H2O\"`.", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" }, "chemical_formula_reduced": { "title": "Chemical Formula Reduced", @@ -3571,16 +3583,16 @@ "type": "string", "description": "The reduced chemical formula for a structure as a string with element symbols and integer chemical proportion numbers.\nThe proportion number MUST be omitted if it is 1.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property.\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\n Intricate queries on formula components are instead suggested to be formulated using set-type filter operators on the multi valued `elements` and `elements_ratios` properties.\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\n - Elements MUST be placed in alphabetical order, followed by their integer chemical proportion number.\n - For structures with no partial occupation, the chemical proportion numbers are the smallest integers for which the chemical proportion is exactly correct.\n - For structures with partial occupation, the chemical proportion numbers are integers that within reasonable approximation indicate the correct chemical proportions. The precise details of how to perform the rounding is chosen by the API implementation.\n - No spaces or separators are allowed.\n\n- **Examples**:\n - `\"H2NaO\"`\n - `\"ClNa\"`\n - `\"CCaO3\"`\n\n- **Query examples**:\n - A filter that matches an exactly given formula is `chemical_formula_reduced=\"H2NaO\"`.", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" }, "chemical_formula_hill": { "title": "Chemical Formula Hill", "pattern": "(^$)|^([A-Z][a-z]?([2-9]|[1-9]\\d+)?)+$", "type": "string", "description": "The chemical formula for a structure in [Hill form](https://dx.doi.org/10.1021/ja02046a005) with element symbols followed by integer chemical proportion numbers. The proportion number MUST be omitted if it is 1.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, only a subset of the filter features MAY be supported.\n - The overall scale factor of the chemical proportions is chosen such that the resulting values are integers that indicate the most chemically relevant unit of which the system is composed.\n For example, if the structure is a repeating unit cell with four hydrogens and four oxygens that represents two hydroperoxide molecules, `chemical_formula_hill` is `\"H2O2\"` (i.e., not `\"HO\"`, nor `\"H4O4\"`).\n - If the chemical insight needed to ascribe a Hill formula to the system is not present, the property MUST be handled as unset.\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\n - Elements MUST be placed in [Hill order](https://dx.doi.org/10.1021/ja02046a005), followed by their integer chemical proportion number.\n Hill order means: if carbon is present, it is placed first, and if also present, hydrogen is placed second.\n After that, all other elements are ordered alphabetically.\n If carbon is not present, all elements are ordered alphabetically.\n - If the system has sites with partial occupation and the total occupations of each element do not all sum up to integers, then the Hill formula SHOULD be handled as unset.\n - No spaces or separators are allowed.\n\n- **Examples**:\n - `\"H2O2\"`\n\n- **Query examples**:\n - A filter that matches an exactly given formula is `chemical_formula_hill=\"H2O2\"`.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "chemical_formula_anonymous": { "title": "Chemical Formula Anonymous", @@ -3588,8 +3600,8 @@ "type": "string", "description": "The anonymous formula is the `chemical_formula_reduced`, but where the elements are instead first ordered by their chemical proportion number, and then, in order left to right, replaced by anonymous symbols A, B, C, ..., Z, Aa, Ba, ..., Za, Ab, Bb, ... and so on.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property.\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\n\n- **Examples**:\n - `\"A2B\"`\n - `\"A42B42C16D12E10F9G5\"`\n\n- **Querying**:\n - A filter that matches an exactly given formula is `chemical_formula_anonymous=\"A2B\"`.", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" }, "dimension_types": { "title": "Dimension Types", @@ -3601,16 +3613,16 @@ }, "description": "List of three integers.\nFor each of the three directions indicated by the three lattice vectors (see property `lattice_vectors`), this list indicates if the direction is periodic (value `1`) or non-periodic (value `0`).\nNote: the elements in this list each refer to the direction of the corresponding entry in `lattice_vectors` and *not* the Cartesian x, y, z directions.\n\n- **Type**: list of integers.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n - MUST be a list of length 3.\n - Each integer element MUST assume only the value 0 or 1.\n\n- **Examples**:\n - For a molecule: `[0, 0, 0]`\n - For a wire along the direction specified by the third lattice vector: `[0, 0, 1]`\n - For a 2D surface/slab, periodic on the plane defined by the first and third lattice vectors: `[1, 0, 1]`\n - For a bulk 3D system: `[1, 1, 1]`", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "should" }, "nperiodic_dimensions": { "title": "Nperiodic Dimensions", "type": "integer", "description": "An integer specifying the number of periodic dimensions in the structure, equivalent to the number of non-zero entries in `dimension_types`.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The integer value MUST be between 0 and 3 inclusive and MUST be equal to the sum of the items in the `dimension_types` property.\n - This property only reflects the treatment of the lattice vectors provided for the structure, and not any physical interpretation of the dimensionality of its contents.\n\n- **Examples**:\n - `2` should be indicated in cases where `dimension_types` is any of `[1, 1, 0]`, `[1, 0, 1]`, `[0, 1, 1]`.\n\n- **Query examples**:\n - Match only structures with exactly 3 periodic dimensions: `nperiodic_dimensions=3`\n - Match all structures with 2 or fewer periodic dimensions: `nperiodic_dimensions<=2`", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" }, "lattice_vectors": { "title": "Lattice Vectors", @@ -3627,8 +3639,8 @@ }, "description": "The three lattice vectors in Cartesian coordinates, in \u00e5ngstr\u00f6m (\u00c5).\n\n- **Type**: list of list of floats or unknown values.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - MUST be a list of three vectors *a*, *b*, and *c*, where each of the vectors MUST BE a list of the vector's coordinates along the x, y, and z Cartesian coordinates.\n (Therefore, the first index runs over the three lattice vectors and the second index runs over the x, y, z Cartesian coordinates).\n - For databases that do not define an absolute Cartesian system (e.g., only defining the length and angles between vectors), the first lattice vector SHOULD be set along *x* and the second on the *xy*-plane.\n - MUST always contain three vectors of three coordinates each, independently of the elements of property `dimension_types`.\n The vectors SHOULD by convention be chosen so the determinant of the `lattice_vectors` matrix is different from zero.\n The vectors in the non-periodic directions have no significance beyond fulfilling these requirements.\n - The coordinates of the lattice vectors of non-periodic dimensions (i.e., those dimensions for which `dimension_types` is `0`) MAY be given as a list of all `null` values.\n If a lattice vector contains the value `null`, all coordinates of that lattice vector MUST be `null`.\n\n- **Examples**:\n - `[[4.0,0.0,0.0],[0.0,4.0,0.0],[0.0,1.0,4.0]]` represents a cell, where the first vector is `(4, 0, 0)`, i.e., a vector aligned along the `x` axis of length 4 \u00c5; the second vector is `(0, 4, 0)`; and the third vector is `(0, 1, 4)`.", "nullable": true, - "x-optimade-support": "should", "x-optimade-queryable": "optional", + "x-optimade-support": "should", "x-optimade-unit": "\u00c5" }, "cartesian_site_positions": { @@ -3644,8 +3656,8 @@ }, "description": "Cartesian positions of each site in the structure.\nA site is usually used to describe positions of atoms; what atoms can be encountered at a given site is conveyed by the `species_at_sites` property, and the species themselves are described in the `species` property.\n\n- **Type**: list of list of floats\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - It MUST be a list of length equal to the number of sites in the structure, where every element is a list of the three Cartesian coordinates of a site expressed as float values in the unit angstrom (\u00c5).\n - An entry MAY have multiple sites at the same Cartesian position (for a relevant use of this, see e.g., the property `assemblies`).\n\n- **Examples**:\n - `[[0,0,0],[0,0,2]]` indicates a structure with two sites, one sitting at the origin and one along the (positive) *z*-axis, 2 \u00c5 away from the origin.", "nullable": true, - "x-optimade-support": "should", "x-optimade-queryable": "optional", + "x-optimade-support": "should", "x-optimade-unit": "\u00c5" }, "nsites": { @@ -3653,8 +3665,8 @@ "type": "integer", "description": "An integer specifying the length of the `cartesian_site_positions` property.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `42`\n\n- **Query examples**:\n - Match only structures with exactly 4 sites: `nsites=4`\n - Match structures that have between 2 and 7 sites: `nsites>=2 AND nsites<=7`", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" }, "species": { "title": "Species", @@ -3664,8 +3676,8 @@ }, "description": "A list describing the species of the sites of this structure.\nSpecies can represent pure chemical elements, virtual-crystal atoms representing a statistical occupation of a given site by multiple chemical elements, and/or a location to which there are attached atoms, i.e., atoms whose precise location are unknown beyond that they are attached to that position (frequently used to indicate hydrogen atoms attached to another element, e.g., a carbon with three attached hydrogens might represent a methyl group, -CH3).\n\n- **Type**: list of dictionary with keys:\n - `name`: string (REQUIRED)\n - `chemical_symbols`: list of strings (REQUIRED)\n - `concentration`: list of float (REQUIRED)\n - `attached`: list of strings (REQUIRED)\n - `nattached`: list of integers (OPTIONAL)\n - `mass`: list of floats (OPTIONAL)\n - `original_name`: string (OPTIONAL).\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - Each list member MUST be a dictionary with the following keys:\n - **name**: REQUIRED; gives the name of the species; the **name** value MUST be unique in the `species` list;\n - **chemical_symbols**: REQUIRED; MUST be a list of strings of all chemical elements composing this species.\n Each item of the list MUST be one of the following:\n - a valid chemical-element symbol, or\n - the special value `\"X\"` to represent a non-chemical element, or\n - the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\n\n If any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.\n\n - **concentration**: REQUIRED; MUST be a list of floats, with same length as `chemical_symbols`.\n The numbers represent the relative concentration of the corresponding chemical symbol in this species.\n The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\n\n - Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\n - Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\n\n Note that concentrations are uncorrelated between different sites (even of the same species).\n\n - **attached**: OPTIONAL; if provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.\n\n - **nattached**: OPTIONAL; if provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the `attached` key.\n\n The implementation MUST include either both or none of the `attached` and `nattached` keys, and if they are provided, they MUST be of the same length.\n Furthermore, if they are provided, the `structure_features` property MUST include the string `site_attachments`.\n\n - **mass**: OPTIONAL. If present MUST be a list of floats, with the same length as `chemical_symbols`, providing element masses expressed in a.m.u.\n Elements denoting vacancies MUST have masses equal to 0.\n\n - **original_name**: OPTIONAL. Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\n\n Note: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.\n\n The main use of this field is for source databases that use species names, containing characters that are not allowed (see description of the list property `species_at_sites`).\n\n - For systems that have only species formed by a single chemical symbol, and that have at most one species per chemical symbol, SHOULD use the chemical symbol as species name (e.g., `\"Ti\"` for titanium, `\"O\"` for oxygen, etc.)\n However, note that this is OPTIONAL, and client implementations MUST NOT assume that the key corresponds to a chemical symbol, nor assume that if the species name is a valid chemical symbol, that it represents a species with that chemical symbol.\n This means that a species `{\"name\": \"C\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]}` is valid and represents a titanium species (and *not* a carbon species).\n - It is NOT RECOMMENDED that a structure includes species that do not have at least one corresponding site.\n\n- **Examples**:\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]`: any site with this species is occupied by a Ti atom.\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]`: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.\n - `[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]`: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.\n - `[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]`: any site with this species is occupied by a carbon isotope with mass 12.\n - `[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]`: any site with this species is occupied by a carbon isotope with mass 13.\n - `[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]`: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms.", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "should" }, "species_at_sites": { "title": "Species At Sites", @@ -3675,8 +3687,8 @@ }, "description": "Name of the species at each site (where values for sites are specified with the same order of the property `cartesian_site_positions`).\nThe properties of the species are found in the property `species`.\n\n- **Type**: list of strings.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - MUST have length equal to the number of sites in the structure (first dimension of the list property `cartesian_site_positions`).\n - Each species name mentioned in the `species_at_sites` list MUST be described in the list property `species` (i.e. for each value in the `species_at_sites` list there MUST exist exactly one dictionary in the `species` list with the `name` attribute equal to the corresponding `species_at_sites` value).\n - Each site MUST be associated only to a single species.\n **Note**: However, species can represent mixtures of atoms, and multiple species MAY be defined for the same chemical element.\n This latter case is useful when different atoms of the same type need to be grouped or distinguished, for instance in simulation codes to assign different initial spin states.\n\n- **Examples**:\n - `[\"Ti\",\"O2\"]` indicates that the first site is hosting a species labeled `\"Ti\"` and the second a species labeled `\"O2\"`.\n - `[\"Ac\", \"Ac\", \"Ag\", \"Ir\"]` indicating the first two sites contains the `\"Ac\"` species, while the third and fourth sites contain the `\"Ag\"` and `\"Ir\"` species, respectively.", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "should" }, "assemblies": { "title": "Assemblies", @@ -3685,8 +3697,8 @@ "$ref": "#/components/schemas/Assembly" }, "description": "A description of groups of sites that are statistically correlated.\n\n- **Type**: list of dictionary with keys:\n - `sites_in_groups`: list of list of integers (REQUIRED)\n - `group_probabilities`: list of floats (REQUIRED)\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - The property SHOULD be `null` for entries that have no partial occupancies.\n - If present, the correct flag MUST be set in the list `structure_features`.\n - Client implementations MUST check its presence (as its presence changes the interpretation of the structure).\n - If present, it MUST be a list of dictionaries, each of which represents an assembly and MUST have the following two keys:\n - **sites_in_groups**: Index of the sites (0-based) that belong to each group for each assembly.\n\n Example: `[[1], [2]]`: two groups, one with the second site, one with the third.\n Example: `[[1,2], [3]]`: one group with the second and third site, one with the fourth.\n\n - **group_probabilities**: Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\n It SHOULD sum to one.\n See below for examples of how to specify the probability of the occurrence of a vacancy.\n The possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.\n\n - If a site is not present in any group, it means that it is present with 100 % probability (as if no assembly was specified).\n - A site MUST NOT appear in more than one group.\n\n- **Examples** (for each entry of the assemblies list):\n - `{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}`: the first site and the second site never occur at the same time in the unit cell.\n Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.\n - `{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}`: the second and third site are either present together or not present; they form the first group of atoms for this assembly.\n The second group is formed by the fourth site.\n Sites of the first group (the second and the third) are never present at the same time as the fourth site.\n 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent).\n\n- **Notes**:\n - Assemblies are essential to represent, for instance, the situation where an atom can statistically occupy two different positions (sites).\n\n - By defining groups, it is possible to represent, e.g., the case where a functional molecule (and not just one atom) is either present or absent (or the case where it it is present in two conformations)\n\n - Considerations on virtual alloys and on vacancies: In the special case of a virtual alloy, these specifications allow two different, equivalent ways of specifying them.\n For instance, for a site at the origin with 30 % probability of being occupied by Si, 50 % probability of being occupied by Ge, and 20 % of being a vacancy, the following two representations are possible:\n\n - Using a single species:\n ```json\n {\n \"cartesian_site_positions\": [[0,0,0]],\n \"species_at_sites\": [\"SiGe-vac\"],\n \"species\": [\n {\n \"name\": \"SiGe-vac\",\n \"chemical_symbols\": [\"Si\", \"Ge\", \"vacancy\"],\n \"concentration\": [0.3, 0.5, 0.2]\n }\n ]\n // ...\n }\n ```\n\n - Using multiple species and the assemblies:\n ```json\n {\n \"cartesian_site_positions\": [ [0,0,0], [0,0,0], [0,0,0] ],\n \"species_at_sites\": [\"Si\", \"Ge\", \"vac\"],\n \"species\": [\n { \"name\": \"Si\", \"chemical_symbols\": [\"Si\"], \"concentration\": [1.0] },\n { \"name\": \"Ge\", \"chemical_symbols\": [\"Ge\"], \"concentration\": [1.0] },\n { \"name\": \"vac\", \"chemical_symbols\": [\"vacancy\"], \"concentration\": [1.0] }\n ],\n \"assemblies\": [\n {\n \"sites_in_groups\": [ [0], [1], [2] ],\n \"group_probabilities\": [0.3, 0.5, 0.2]\n }\n ]\n // ...\n }\n ```\n\n - It is up to the database provider to decide which representation to use, typically depending on the internal format in which the structure is stored.\n However, given a structure identified by a unique ID, the API implementation MUST always provide the same representation for it.\n\n - The probabilities of occurrence of different assemblies are uncorrelated.\n So, for instance in the following case with two assemblies:\n ```json\n {\n \"assemblies\": [\n {\n \"sites_in_groups\": [ [0], [1] ],\n \"group_probabilities\": [0.2, 0.8],\n },\n {\n \"sites_in_groups\": [ [2], [3] ],\n \"group_probabilities\": [0.3, 0.7]\n }\n ]\n }\n ```\n\n Site 0 is present with a probability of 20 % and site 1 with a probability of 80 %. These two sites are correlated (either site 0 or 1 is present). Similarly, site 2 is present with a probability of 30 % and site 3 with a probability of 70 %.\n These two sites are correlated (either site 2 or 3 is present).\n However, the presence or absence of sites 0 and 1 is not correlated with the presence or absence of sites 2 and 3 (in the specific example, the pair of sites (0, 2) can occur with 0.2*0.3 = 6 % probability; the pair (0, 3) with 0.2*0.7 = 14 % probability; the pair (1, 2) with 0.8*0.3 = 24 % probability; and the pair (1, 3) with 0.8*0.7 = 56 % probability).", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "structure_features": { "title": "Structure Features", @@ -3695,8 +3707,8 @@ "$ref": "#/components/schemas/StructureFeatures" }, "description": "A list of strings that flag which special features are used by the structure.\n\n- **Type**: list of strings\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property.\n Filters on the list MUST support all mandatory HAS-type queries.\n Filter operators for comparisons on the string components MUST support equality, support for other comparison operators are OPTIONAL.\n - MUST be an empty list if no special features are used.\n - MUST be sorted alphabetically.\n - If a special feature listed below is used, the list MUST contain the corresponding string.\n - If a special feature listed below is not used, the list MUST NOT contain the corresponding string.\n - **List of strings used to indicate special structure features**:\n - `disorder`: this flag MUST be present if any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element.\n - `implicit_atoms`: this flag MUST be present if the structure contains atoms that are not assigned to sites via the property `species_at_sites` (e.g., because their positions are unknown).\n When this flag is present, the properties related to the chemical formula will likely not match the type and count of atoms represented by the `species_at_sites`, `species` and `assemblies` properties.\n - `site_attachments`: this flag MUST be present if any one entry in the `species` list includes `attached` and `nattached`.\n - `assemblies`: this flag MUST be present if the property `assemblies` is present.\n\n- **Examples**: A structure having implicit atoms and using assemblies: `[\"assemblies\", \"implicit_atoms\"]`", - "x-optimade-support": "must", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "must" } }, "description": "This class contains the Field for the attributes used to represent a structure, e.g. unit cell, atoms, positions." diff --git a/optimade/models/entries.py b/optimade/models/entries.py index 7850565ea..b92c241c4 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -4,7 +4,7 @@ from pydantic import BaseModel, validator # pylint: disable=no-name-in-module -from optimade.models.jsonapi import Attributes, Relationships, Resource +from optimade.models.jsonapi import Attributes, Meta, Relationships, Resource from optimade.models.optimade_json import DataType, Relationship from optimade.models.utils import OptimadeField, StrictField, SupportLevel @@ -100,6 +100,16 @@ def cast_immutable_id_to_str(cls, value): return value +class EntryMetadata(Meta): + """Contains the metadata for the attributes of an entry""" + + property_metadata: Dict = StrictField( + None, + description="""A dictionary, where the keys are the names of the properties in the attributes field and the value is a dictionary containing the meta data for that property. +Database-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes).""", + ) + + class EntryResource(Resource): """The base model for an entry resource.""" @@ -147,6 +157,11 @@ class EntryResource(Resource): Database-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes).""", ) + meta: Optional[EntryMetadata] = StrictField( + None, + description="""A dictionary, containing entry and property-specific metadata for a given entry.""", + ) + relationships: Optional[EntryRelationships] = StrictField( None, description="""A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships). diff --git a/optimade/server/data/test_structures.json b/optimade/server/data/test_structures.json index 5ea0305b6..701810319 100644 --- a/optimade/server/data/test_structures.json +++ b/optimade/server/data/test_structures.json @@ -180,6 +180,13 @@ ], "structure_features": [], "task_id": "mpf_2", + "meta": { + "property_metadata": { + "elements_ratios": { + "_exmpl_originates_from_project":"piezoelectic_perovskites" + } + } + }, "relationships": { "references": { "data": [ diff --git a/optimade/server/mappers/entries.py b/optimade/server/mappers/entries.py index eb58002a0..3e29bbe4b 100644 --- a/optimade/server/mappers/entries.py +++ b/optimade/server/mappers/entries.py @@ -73,7 +73,13 @@ class BaseResourceMapper: PROVIDER_FIELDS: Tuple[str, ...] = () ENTRY_RESOURCE_CLASS: Type[EntryResource] = EntryResource RELATIONSHIP_ENTRY_TYPES: Set[str] = {"references", "structures"} - TOP_LEVEL_NON_ATTRIBUTES_FIELDS: Set[str] = {"id", "type", "relationships", "links"} + TOP_LEVEL_NON_ATTRIBUTES_FIELDS: Set[str] = { + "id", + "type", + "relationships", + "links", + "meta", + } @classmethod @lru_cache(maxsize=NUM_ENTRY_TYPES) diff --git a/tests/models/test_data/test_good_structures.json b/tests/models/test_data/test_good_structures.json index b84605832..1af63cfc6 100644 --- a/tests/models/test_data/test_good_structures.json +++ b/tests/models/test_data/test_good_structures.json @@ -179,6 +179,13 @@ "nperiodic_dimensions": 3, "lattice_vectors": [[4.0,0.0,0.0],[0.0, 4.0, 0.0],[0.0,1.0,4.0]], "cartesian_site_positions": [ [0,0,0], [0,0,0], [0,0,0], [0,0,0], [0,0,0], [0,0,0], [0,0,0] ], + "meta": { + "property_metadata": { + "element_ratios": { + "_exmpl_originates_from_project": "piezoelectic_perovskites" + } + } + }, "species_at_sites": ["Cl", "O", "N", "met", "Os", "Na", "P"], "species": [ {"name": "Cl", "chemical_symbols": ["Cl"], "concentration": [1.0] }, diff --git a/tests/server/query_params/conftest.py b/tests/server/query_params/conftest.py index ce11819be..f6c7ee9f3 100644 --- a/tests/server/query_params/conftest.py +++ b/tests/server/query_params/conftest.py @@ -78,11 +78,14 @@ def inner( response = get_good_response(request, server) expected_fields.add("attributes") - + expected_fields.discard("meta") response_fields = set() for entry in response["data"]: response_fields.update(set(entry.keys())) response_fields.update(set(entry["attributes"].keys())) + response_fields.discard( + "meta" + ) # As "meta" is an optional field the response may or may not have it, so we remove it here to prevent problems in the assert below. assert sorted(expected_fields) == sorted(response_fields) return inner From 6ed4a5dd3c7d8c9fa90c089dc999f37be95f2e0b Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Wed, 19 Jul 2023 17:02:05 +0200 Subject: [PATCH 02/86] Added validator for meta field. --- optimade/models/entries.py | 53 ++++++++++++++++++- .../test_data/test_good_structures.json | 2 +- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/optimade/models/entries.py b/optimade/models/entries.py index b92c241c4..d992b5e9b 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -2,8 +2,13 @@ from datetime import datetime from typing import Dict, List, Optional -from pydantic import BaseModel, validator # pylint: disable=no-name-in-module +from pydantic import ( + BaseModel, + root_validator, + validator, +) +# pylint: disable=no-name-in-module from optimade.models.jsonapi import Attributes, Meta, Relationships, Resource from optimade.models.optimade_json import DataType, Relationship from optimade.models.utils import OptimadeField, StrictField, SupportLevel @@ -168,6 +173,52 @@ class EntryResource(Resource): The OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object.""", ) + def check_field_supported_prefix(field): + from optimade.server.mappers import BaseResourceMapper + + prefix = field.split("_")[1] + if prefix not in BaseResourceMapper.SUPPORTED_PREFIXES: + raise ValueError( + f"The prefix {prefix} of the field {field} is not supported by this server." + ) + + @root_validator + def check_meta(cls, values): + """Validator to check whether meta field has been formatted correctly.""" + + meta = values.get("meta") + if meta is not None: + for field in meta.__dict__: + if field.startswith("_"): + cls.check_field_supported_prefix(field) + elif field == "property_metadata": + # check that all the fields under property metadata are in attributes + attributes = ( + values.get("attributes").__dict__ + if values.get("attributes") + else {} + ) + for subfield in meta.__dict__.get( + field + ): ## ToDo The names of the fields in atributes only need to be read once so this code can still be sped up. + if subfield not in attributes: + raise ValueError( + f"The keys under the field `property_metadata` need to match with the field names in attributes. The field {subfield} is however not in attributes." + ) + # check that the fields under subfield are starting with prefix + for subsubfield in meta.__dict__.get(field).get(subfield): + if subsubfield.startswith("_"): + cls.check_field_supported_prefix(subsubfield) + else: + raise ValueError( + f"The Provider/Domain specific field {subsubfield} must be prefixed with a prefix that is supported by this database." + ) + else: + raise ValueError( + "The fields under meta either need to be database specific fields or the field `property_metadata'" + ) + return values + class EntryInfoProperty(BaseModel): description: str = StrictField( diff --git a/tests/models/test_data/test_good_structures.json b/tests/models/test_data/test_good_structures.json index 1af63cfc6..15c3f6992 100644 --- a/tests/models/test_data/test_good_structures.json +++ b/tests/models/test_data/test_good_structures.json @@ -181,7 +181,7 @@ "cartesian_site_positions": [ [0,0,0], [0,0,0], [0,0,0], [0,0,0], [0,0,0], [0,0,0], [0,0,0] ], "meta": { "property_metadata": { - "element_ratios": { + "elements_ratios": { "_exmpl_originates_from_project": "piezoelectic_perovskites" } } From ef1ca3c3c94796d5371cd58125942ab6f11ef172 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Thu, 20 Jul 2023 12:13:24 +0200 Subject: [PATCH 03/86] Update pyyaml version in requirements.txt --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 8bee4a089..5c5488835 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ email_validator==2.0.0.post2 lark==1.1.5 pydantic==1.10.9 -pyyaml==6.0 +pyyaml==6.0.1 requests==2.31.0 uvicorn==0.22.0 From 89ac0fef24a0c441a465a2aa8a60b65be26083ef Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Thu, 20 Jul 2023 12:25:33 +0200 Subject: [PATCH 04/86] Update requirements.txt --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 5c5488835..59bf8d8bb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ email_validator==2.0.0.post2 lark==1.1.5 pydantic==1.10.9 -pyyaml==6.0.1 +pyyaml==5.4.1 requests==2.31.0 uvicorn==0.22.0 From f229be156b387848627b292238a9171864e09ca4 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Thu, 20 Jul 2023 12:30:36 +0200 Subject: [PATCH 05/86] Update requirements.txt --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 59bf8d8bb..5c5488835 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ email_validator==2.0.0.post2 lark==1.1.5 pydantic==1.10.9 -pyyaml==5.4.1 +pyyaml==6.0.1 requests==2.31.0 uvicorn==0.22.0 From a195fb506985639d7635502443c1e01368338d14 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Thu, 20 Jul 2023 15:48:49 +0200 Subject: [PATCH 06/86] Remove test structures to get an idea of what triggers yaml import error on github. --- optimade/server/data/test_structures.json | 7 ------- tests/models/test_data/test_good_structures.json | 7 ------- 2 files changed, 14 deletions(-) diff --git a/optimade/server/data/test_structures.json b/optimade/server/data/test_structures.json index 701810319..5ea0305b6 100644 --- a/optimade/server/data/test_structures.json +++ b/optimade/server/data/test_structures.json @@ -180,13 +180,6 @@ ], "structure_features": [], "task_id": "mpf_2", - "meta": { - "property_metadata": { - "elements_ratios": { - "_exmpl_originates_from_project":"piezoelectic_perovskites" - } - } - }, "relationships": { "references": { "data": [ diff --git a/tests/models/test_data/test_good_structures.json b/tests/models/test_data/test_good_structures.json index 15c3f6992..b84605832 100644 --- a/tests/models/test_data/test_good_structures.json +++ b/tests/models/test_data/test_good_structures.json @@ -179,13 +179,6 @@ "nperiodic_dimensions": 3, "lattice_vectors": [[4.0,0.0,0.0],[0.0, 4.0, 0.0],[0.0,1.0,4.0]], "cartesian_site_positions": [ [0,0,0], [0,0,0], [0,0,0], [0,0,0], [0,0,0], [0,0,0], [0,0,0] ], - "meta": { - "property_metadata": { - "elements_ratios": { - "_exmpl_originates_from_project": "piezoelectic_perovskites" - } - } - }, "species_at_sites": ["Cl", "O", "N", "met", "Os", "Na", "P"], "species": [ {"name": "Cl", "chemical_symbols": ["Cl"], "concentration": [1.0] }, From 07d77feadda15784e434d338a53b9d7b97bc1316 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Thu, 20 Jul 2023 15:58:11 +0200 Subject: [PATCH 07/86] Readding meta field to test_good_structures. --- tests/models/test_data/test_good_structures.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/models/test_data/test_good_structures.json b/tests/models/test_data/test_good_structures.json index b84605832..63c7c6044 100644 --- a/tests/models/test_data/test_good_structures.json +++ b/tests/models/test_data/test_good_structures.json @@ -165,6 +165,13 @@ "last_modified": { "$date": "2019-06-08T05:13:37.331Z" }, + "meta": { + "property_metadata": { + "elements_ratios": { + "_exmpl_originates_from_project":"piezoelectic_perovskites" + } + } + }, "band_gap": 1.23456, "chemsys": "C-H-Cl-N-Na-O-Os-P", "elements": ["C", "Cl", "H", "N", "Na", "O", "Os", "P"], From 2848dd3e2fd314b028fa025d735ec70b85dd801d Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Thu, 20 Jul 2023 16:07:21 +0200 Subject: [PATCH 08/86] Readding meta field to test_structures.json. --- optimade/server/data/test_structures.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/optimade/server/data/test_structures.json b/optimade/server/data/test_structures.json index 5ea0305b6..9b753de98 100644 --- a/optimade/server/data/test_structures.json +++ b/optimade/server/data/test_structures.json @@ -3,6 +3,13 @@ "_id": { "$oid": "5cfb441f053b174410700d02" }, + "meta": { + "property_metadata": { + "elements_ratios": { + "_exmpl_originates_from_project":"piezoelectic_perovskites" + } + } + }, "assemblies": null, "chemsys": "Ac", "cartesian_site_positions": [ From 80c4dc498402f2ba495e7cad0381ea960d2f38a8 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Fri, 21 Jul 2023 11:39:21 +0200 Subject: [PATCH 09/86] Added handling None for property_metadata to validator + small correctionds. --- optimade/models/entries.py | 30 +++++++++++++---------- optimade/server/data/test_structures.json | 2 +- tests/server/query_params/conftest.py | 5 ++-- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/optimade/models/entries.py b/optimade/models/entries.py index d992b5e9b..88f6459e7 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -198,21 +198,25 @@ def check_meta(cls, values): if values.get("attributes") else {} ) - for subfield in meta.__dict__.get( - field - ): ## ToDo The names of the fields in atributes only need to be read once so this code can still be sped up. - if subfield not in attributes: - raise ValueError( - f"The keys under the field `property_metadata` need to match with the field names in attributes. The field {subfield} is however not in attributes." - ) - # check that the fields under subfield are starting with prefix - for subsubfield in meta.__dict__.get(field).get(subfield): - if subsubfield.startswith("_"): - cls.check_field_supported_prefix(subsubfield) - else: + property_metadata = meta.__dict__.get(field) + if property_metadata is not None: + for ( + subfield + ) in ( + property_metadata + ): ## ToDo The names of the fields in attributes only need to be read once so this code can still be sped up. + if subfield not in attributes: raise ValueError( - f"The Provider/Domain specific field {subsubfield} must be prefixed with a prefix that is supported by this database." + f"The keys under the field `property_metadata` need to match with the field names in attributes. The field {subfield} is however not in attributes." ) + # check that the fields under subfield are starting with prefix + for subsubfield in meta.__dict__.get(field).get(subfield): + if subsubfield.startswith("_"): + cls.check_field_supported_prefix(subsubfield) + else: + raise ValueError( + f"The Provider/Domain specific field {subsubfield} must be prefixed with a prefix that is supported by this database." + ) else: raise ValueError( "The fields under meta either need to be database specific fields or the field `property_metadata'" diff --git a/optimade/server/data/test_structures.json b/optimade/server/data/test_structures.json index 9b753de98..0adb5aa7f 100644 --- a/optimade/server/data/test_structures.json +++ b/optimade/server/data/test_structures.json @@ -6,7 +6,7 @@ "meta": { "property_metadata": { "elements_ratios": { - "_exmpl_originates_from_project":"piezoelectic_perovskites" + "_exmpl_originates_from_project":"Actinides" } } }, diff --git a/tests/server/query_params/conftest.py b/tests/server/query_params/conftest.py index f6c7ee9f3..fb80dfb9d 100644 --- a/tests/server/query_params/conftest.py +++ b/tests/server/query_params/conftest.py @@ -83,9 +83,8 @@ def inner( for entry in response["data"]: response_fields.update(set(entry.keys())) response_fields.update(set(entry["attributes"].keys())) - response_fields.discard( - "meta" - ) # As "meta" is an optional field the response may or may not have it, so we remove it here to prevent problems in the assert below. + # As "meta" is an optional field the response may or may not have it, so we remove it here to prevent problems in the assert below. + response_fields.discard("meta") assert sorted(expected_fields) == sorted(response_fields) return inner From d55abec1fed0334dbe8c67fb57500e39b0ad32d0 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Fri, 21 Jul 2023 14:32:37 +0200 Subject: [PATCH 10/86] Added test for validator per entry meta field. --- optimade/models/entries.py | 19 +++++++------- tests/models/test_entries.py | 48 +++++++++++++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 11 deletions(-) diff --git a/optimade/models/entries.py b/optimade/models/entries.py index 88f6459e7..bbf8a785f 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -182,23 +182,19 @@ def check_field_supported_prefix(field): f"The prefix {prefix} of the field {field} is not supported by this server." ) - @root_validator + @root_validator(pre=True) def check_meta(cls, values): """Validator to check whether meta field has been formatted correctly.""" meta = values.get("meta") if meta is not None: - for field in meta.__dict__: - if field.startswith("_"): - cls.check_field_supported_prefix(field) - elif field == "property_metadata": + for field in meta: + if field == "property_metadata": # check that all the fields under property metadata are in attributes attributes = ( - values.get("attributes").__dict__ - if values.get("attributes") - else {} + values.get("attributes") if values.get("attributes") else {} ) - property_metadata = meta.__dict__.get(field) + property_metadata = meta.get(field) if property_metadata is not None: for ( subfield @@ -210,13 +206,16 @@ def check_meta(cls, values): f"The keys under the field `property_metadata` need to match with the field names in attributes. The field {subfield} is however not in attributes." ) # check that the fields under subfield are starting with prefix - for subsubfield in meta.__dict__.get(field).get(subfield): + for subsubfield in meta.get(field).get(subfield): if subsubfield.startswith("_"): cls.check_field_supported_prefix(subsubfield) else: raise ValueError( f"The Provider/Domain specific field {subsubfield} must be prefixed with a prefix that is supported by this database." ) + # At this point I am getting ahead of the specification. There is the intention to allow database specific fields(with the database specific prefixes) here in line with the JSON API specification, but it has not been decided yet how this case should be handled in the property definitions. + elif field.startswith("_"): + cls.check_field_supported_prefix(field) else: raise ValueError( "The fields under meta either need to be database specific fields or the field `property_metadata'" diff --git a/tests/models/test_entries.py b/tests/models/test_entries.py index a3ab7e318..c69816667 100644 --- a/tests/models/test_entries.py +++ b/tests/models/test_entries.py @@ -1,7 +1,7 @@ import pytest from pydantic import ValidationError -from optimade.models.entries import EntryRelationships +from optimade.models.entries import EntryRelationships, EntryResource def test_simple_relationships(): @@ -48,3 +48,49 @@ def test_advanced_relationships(): } with pytest.raises(ValidationError): EntryRelationships(**relationship) + + +def test_meta(): + import copy + + good_entry_resource = { + "id": "goodstruct123", + "type": "structure", + "attributes": { + "last_modified": "2023-07-21T05:13:37.331Z", + "elements": ["Ac"], + "_exmpl_database_specific_property": "value1", + "elements_ratios": [1.0], + }, + "meta": { + "property_metadata": { + "elements_ratios": { + "_exmpl_mearsurement_method": "ICP-OES", + }, + "_exmpl_database_specific_property": { + "_exmpl_metadata_property": "metadata_value" + }, + } + }, + } + + EntryResource(**good_entry_resource) + + bad_entry_resources = [ + good_entry_resource, + copy.deepcopy(good_entry_resource), + copy.deepcopy(good_entry_resource), + ] + bad_entry_resources[0]["meta"]["property_metadata"][ + "_exmpl_database_specific_property" + ] = {"metadata_property": "metadata_value"} + bad_entry_resources[1]["meta"]["property_metadata"][ + "database_specific_property" + ] = {"_exmpl_metadata_property": "metadata_value"} + bad_entry_resources[2]["meta"]["database_specific_property"] = { + "_exmpl_metadata_property": "metadata_value" + } + + for bad_entry in bad_entry_resources: + with pytest.raises(ValueError): + EntryResource(**bad_entry) From ff545bb41f744af42167957c3e1862198fb93fc3 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Fri, 21 Jul 2023 18:02:30 +0200 Subject: [PATCH 11/86] Added test for presence metadata field in test_structures.py. --- tests/server/routers/test_structures.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/server/routers/test_structures.py b/tests/server/routers/test_structures.py index a0e089f2d..bf49797f2 100644 --- a/tests/server/routers/test_structures.py +++ b/tests/server/routers/test_structures.py @@ -69,6 +69,12 @@ def test_structures_endpoint_data(self): assert self.json_response["data"]["type"] == "structures" assert "attributes" in self.json_response["data"] assert "_exmpl_chemsys" in self.json_response["data"]["attributes"] + assert ( + self.json_response["data"]["meta"]["property_metadata"]["elements_ratios"][ + "_exmpl_originates_from_project" + ] + == "Actinides" + ) def test_check_response_single_structure(check_response): From e0d6ff3bb3b0f70cbdc56f1de06a808dc2ab53f3 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Fri, 21 Jul 2023 18:37:15 +0200 Subject: [PATCH 12/86] Remove metadata fields when the fields that they belong to are not returned. --- optimade/server/routers/utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/optimade/server/routers/utils.py b/optimade/server/routers/utils.py index e79983547..5b5cf7281 100644 --- a/optimade/server/routers/utils.py +++ b/optimade/server/routers/utils.py @@ -125,6 +125,8 @@ def handle_response_fields( for field in exclude_fields: if field in new_entry["attributes"]: del new_entry["attributes"][field] + if field in new_entry.get("meta", {}).get("property_metadata", {}): + del new_entry["meta"]["property_metadata"][field] # Include missing fields that were requested in `response_fields` for field in include_fields: From 191fbba266f7f36d441a3be3c0cdead094f5d4b4 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Fri, 21 Jul 2023 18:50:50 +0200 Subject: [PATCH 13/86] add extra test for bad prefix. --- tests/models/test_entries.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/models/test_entries.py b/tests/models/test_entries.py index c69816667..e1a8a492e 100644 --- a/tests/models/test_entries.py +++ b/tests/models/test_entries.py @@ -80,6 +80,7 @@ def test_meta(): good_entry_resource, copy.deepcopy(good_entry_resource), copy.deepcopy(good_entry_resource), + copy.deepcopy(good_entry_resource), ] bad_entry_resources[0]["meta"]["property_metadata"][ "_exmpl_database_specific_property" @@ -90,6 +91,9 @@ def test_meta(): bad_entry_resources[2]["meta"]["database_specific_property"] = { "_exmpl_metadata_property": "metadata_value" } + bad_entry_resources[3]["meta"]["_other_database_specific_property"] = { + "_exmpl_metadata_property": "metadata_value" + } for bad_entry in bad_entry_resources: with pytest.raises(ValueError): From 6251e48f0efa741fdc3156b90cf2cdc598541658 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Tue, 25 Jul 2023 18:57:28 +0200 Subject: [PATCH 14/86] Test if dependancy conflict causes error. --- requirements-http-client.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-http-client.txt b/requirements-http-client.txt index 412427a58..abdb938cb 100644 --- a/requirements-http-client.txt +++ b/requirements-http-client.txt @@ -1,3 +1,3 @@ click==8.1.5 -httpx==0.24.1 +httpx==0.23.3 rich==13.4.2 From bf9692a8ffdfa584a915227520990d7120c69b50 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Tue, 25 Jul 2023 19:40:55 +0200 Subject: [PATCH 15/86] Revert "Test if dependancy conflict causes error." This reverts commit 6251e48f0efa741fdc3156b90cf2cdc598541658. --- requirements-http-client.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-http-client.txt b/requirements-http-client.txt index abdb938cb..412427a58 100644 --- a/requirements-http-client.txt +++ b/requirements-http-client.txt @@ -1,3 +1,3 @@ click==8.1.5 -httpx==0.23.3 +httpx==0.24.1 rich==13.4.2 From 08649112d9d1f47e126dfd284a00e7b112776238 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Tue, 25 Jul 2023 19:40:59 +0200 Subject: [PATCH 16/86] Revert "Revert "Test if dependancy conflict causes error."" This reverts commit bf9692a8ffdfa584a915227520990d7120c69b50. --- requirements-http-client.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-http-client.txt b/requirements-http-client.txt index 412427a58..abdb938cb 100644 --- a/requirements-http-client.txt +++ b/requirements-http-client.txt @@ -1,3 +1,3 @@ click==8.1.5 -httpx==0.24.1 +httpx==0.23.3 rich==13.4.2 From eb953ad2d009d698ed3f85e33336b72dc8e72318 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Wed, 26 Jul 2023 12:13:32 +0200 Subject: [PATCH 17/86] correct version httpx. --- requirements-http-client.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-http-client.txt b/requirements-http-client.txt index abdb938cb..412427a58 100644 --- a/requirements-http-client.txt +++ b/requirements-http-client.txt @@ -1,3 +1,3 @@ click==8.1.5 -httpx==0.23.3 +httpx==0.24.1 rich==13.4.2 From 2879fac9f53fec381ca2614c48010ca3a3c402f6 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Wed, 26 Jul 2023 14:04:06 +0200 Subject: [PATCH 18/86] commenting out validator to see if this resolves issue github. --- optimade/models/entries.py | 79 +++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 40 deletions(-) diff --git a/optimade/models/entries.py b/optimade/models/entries.py index bbf8a785f..d7550393c 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -4,7 +4,6 @@ from pydantic import ( BaseModel, - root_validator, validator, ) @@ -182,45 +181,45 @@ def check_field_supported_prefix(field): f"The prefix {prefix} of the field {field} is not supported by this server." ) - @root_validator(pre=True) - def check_meta(cls, values): - """Validator to check whether meta field has been formatted correctly.""" - - meta = values.get("meta") - if meta is not None: - for field in meta: - if field == "property_metadata": - # check that all the fields under property metadata are in attributes - attributes = ( - values.get("attributes") if values.get("attributes") else {} - ) - property_metadata = meta.get(field) - if property_metadata is not None: - for ( - subfield - ) in ( - property_metadata - ): ## ToDo The names of the fields in attributes only need to be read once so this code can still be sped up. - if subfield not in attributes: - raise ValueError( - f"The keys under the field `property_metadata` need to match with the field names in attributes. The field {subfield} is however not in attributes." - ) - # check that the fields under subfield are starting with prefix - for subsubfield in meta.get(field).get(subfield): - if subsubfield.startswith("_"): - cls.check_field_supported_prefix(subsubfield) - else: - raise ValueError( - f"The Provider/Domain specific field {subsubfield} must be prefixed with a prefix that is supported by this database." - ) - # At this point I am getting ahead of the specification. There is the intention to allow database specific fields(with the database specific prefixes) here in line with the JSON API specification, but it has not been decided yet how this case should be handled in the property definitions. - elif field.startswith("_"): - cls.check_field_supported_prefix(field) - else: - raise ValueError( - "The fields under meta either need to be database specific fields or the field `property_metadata'" - ) - return values + # @root_validator(pre=True) + # def check_meta(cls, values): + # """Validator to check whether meta field has been formatted correctly.""" + # + # meta = values.get("meta") + # if meta is not None: + # for field in meta: + # if field == "property_metadata": + # # check that all the fields under property metadata are in attributes + # attributes = ( + # values.get("attributes") if values.get("attributes") else {} + # ) + # property_metadata = meta.get(field) + # if property_metadata is not None: + # for ( + # subfield + # ) in ( + # property_metadata + # ): ## ToDo The names of the fields in attributes only need to be read once so this code can still be sped up. + # if subfield not in attributes: + # raise ValueError( + # f"The keys under the field `property_metadata` need to match with the field names in attributes. The field {subfield} is however not in attributes." + # ) + # # check that the fields under subfield are starting with prefix + # for subsubfield in meta.get(field).get(subfield): + # if subsubfield.startswith("_"): + # cls.check_field_supported_prefix(subsubfield) + # else: + # raise ValueError( + # f"The Provider/Domain specific field {subsubfield} must be prefixed with a prefix that is supported by this database." + # ) + # # At this point I am getting ahead of the specification. There is the intention to allow database specific fields(with the database specific prefixes) here in line with the JSON API specification, but it has not been decided yet how this case should be handled in the property definitions. + # elif field.startswith("_"): + # cls.check_field_supported_prefix(field) + # else: + # raise ValueError( + # "The fields under meta either need to be database specific fields or the field `property_metadata'" + # ) + # return values class EntryInfoProperty(BaseModel): From 18e2ef109cdccfe5ccd0c13bd5e86840790eb55e Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Wed, 26 Jul 2023 14:17:51 +0200 Subject: [PATCH 19/86] Slowly reassembling validator to see if this resolves issue github. --- optimade/models/entries.py | 84 +++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 43 deletions(-) diff --git a/optimade/models/entries.py b/optimade/models/entries.py index d7550393c..c399a71e0 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -2,10 +2,7 @@ from datetime import datetime from typing import Dict, List, Optional -from pydantic import ( - BaseModel, - validator, -) +from pydantic import BaseModel, root_validator, validator # pylint: disable=no-name-in-module from optimade.models.jsonapi import Attributes, Meta, Relationships, Resource @@ -181,45 +178,46 @@ def check_field_supported_prefix(field): f"The prefix {prefix} of the field {field} is not supported by this server." ) - # @root_validator(pre=True) - # def check_meta(cls, values): - # """Validator to check whether meta field has been formatted correctly.""" - # - # meta = values.get("meta") - # if meta is not None: - # for field in meta: - # if field == "property_metadata": - # # check that all the fields under property metadata are in attributes - # attributes = ( - # values.get("attributes") if values.get("attributes") else {} - # ) - # property_metadata = meta.get(field) - # if property_metadata is not None: - # for ( - # subfield - # ) in ( - # property_metadata - # ): ## ToDo The names of the fields in attributes only need to be read once so this code can still be sped up. - # if subfield not in attributes: - # raise ValueError( - # f"The keys under the field `property_metadata` need to match with the field names in attributes. The field {subfield} is however not in attributes." - # ) - # # check that the fields under subfield are starting with prefix - # for subsubfield in meta.get(field).get(subfield): - # if subsubfield.startswith("_"): - # cls.check_field_supported_prefix(subsubfield) - # else: - # raise ValueError( - # f"The Provider/Domain specific field {subsubfield} must be prefixed with a prefix that is supported by this database." - # ) - # # At this point I am getting ahead of the specification. There is the intention to allow database specific fields(with the database specific prefixes) here in line with the JSON API specification, but it has not been decided yet how this case should be handled in the property definitions. - # elif field.startswith("_"): - # cls.check_field_supported_prefix(field) - # else: - # raise ValueError( - # "The fields under meta either need to be database specific fields or the field `property_metadata'" - # ) - # return values + @root_validator(pre=True) + def check_meta(cls, values): + pass + # """Validator to check whether meta field has been formatted correctly.""" + # + # meta = values.get("meta") + # if meta is not None: + # for field in meta: + # if field == "property_metadata": + # # check that all the fields under property metadata are in attributes + # attributes = ( + # values.get("attributes") if values.get("attributes") else {} + # ) + # property_metadata = meta.get(field) + # if property_metadata is not None: + # for ( + # subfield + # ) in ( + # property_metadata + # ): ## ToDo The names of the fields in attributes only need to be read once so this code can still be sped up. + # if subfield not in attributes: + # raise ValueError( + # f"The keys under the field `property_metadata` need to match with the field names in attributes. The field {subfield} is however not in attributes." + # ) + # # check that the fields under subfield are starting with prefix + # for subsubfield in meta.get(field).get(subfield): + # if subsubfield.startswith("_"): + # cls.check_field_supported_prefix(subsubfield) + # else: + # raise ValueError( + # f"The Provider/Domain specific field {subsubfield} must be prefixed with a prefix that is supported by this database." + # ) + # # At this point I am getting ahead of the specification. There is the intention to allow database specific fields(with the database specific prefixes) here in line with the JSON API specification, but it has not been decided yet how this case should be handled in the property definitions. + # elif field.startswith("_"): + # cls.check_field_supported_prefix(field) + # else: + # raise ValueError( + # "The fields under meta either need to be database specific fields or the field `property_metadata'" + # ) + return values class EntryInfoProperty(BaseModel): From 7fc19066e65f93bd7d4edc633069949fcf7e4f0a Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Wed, 26 Jul 2023 14:25:46 +0200 Subject: [PATCH 20/86] Slowly reassembling validator to see if this resolves issue github. --- optimade/models/entries.py | 70 +++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/optimade/models/entries.py b/optimade/models/entries.py index c399a71e0..3453c6e98 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -180,43 +180,43 @@ def check_field_supported_prefix(field): @root_validator(pre=True) def check_meta(cls, values): - pass # """Validator to check whether meta field has been formatted correctly.""" # - # meta = values.get("meta") - # if meta is not None: - # for field in meta: - # if field == "property_metadata": - # # check that all the fields under property metadata are in attributes - # attributes = ( - # values.get("attributes") if values.get("attributes") else {} - # ) - # property_metadata = meta.get(field) - # if property_metadata is not None: - # for ( - # subfield - # ) in ( - # property_metadata - # ): ## ToDo The names of the fields in attributes only need to be read once so this code can still be sped up. - # if subfield not in attributes: - # raise ValueError( - # f"The keys under the field `property_metadata` need to match with the field names in attributes. The field {subfield} is however not in attributes." - # ) - # # check that the fields under subfield are starting with prefix - # for subsubfield in meta.get(field).get(subfield): - # if subsubfield.startswith("_"): - # cls.check_field_supported_prefix(subsubfield) - # else: - # raise ValueError( - # f"The Provider/Domain specific field {subsubfield} must be prefixed with a prefix that is supported by this database." - # ) - # # At this point I am getting ahead of the specification. There is the intention to allow database specific fields(with the database specific prefixes) here in line with the JSON API specification, but it has not been decided yet how this case should be handled in the property definitions. - # elif field.startswith("_"): - # cls.check_field_supported_prefix(field) - # else: - # raise ValueError( - # "The fields under meta either need to be database specific fields or the field `property_metadata'" - # ) + meta = values.get("meta") + if meta is not None: + for field in meta: + if field == "property_metadata": + pass + # # check that all the fields under property metadata are in attributes + # attributes = ( + # values.get("attributes") if values.get("attributes") else {} + # ) + # property_metadata = meta.get(field) + # if property_metadata is not None: + # for ( + # subfield + # ) in ( + # property_metadata + # ): ## ToDo The names of the fields in attributes only need to be read once so this code can still be sped up. + # if subfield not in attributes: + # raise ValueError( + # f"The keys under the field `property_metadata` need to match with the field names in attributes. The field {subfield} is however not in attributes." + # ) + # # check that the fields under subfield are starting with prefix + # for subsubfield in meta.get(field).get(subfield): + # if subsubfield.startswith("_"): + # cls.check_field_supported_prefix(subsubfield) + # else: + # raise ValueError( + # f"The Provider/Domain specific field {subsubfield} must be prefixed with a prefix that is supported by this database." + # ) + # # At this point I am getting ahead of the specification. There is the intention to allow database specific fields(with the database specific prefixes) here in line with the JSON API specification, but it has not been decided yet how this case should be handled in the property definitions. + elif field.startswith("_"): + cls.check_field_supported_prefix(field) + else: + raise ValueError( + "The fields under meta either need to be database specific fields or the field `property_metadata'" + ) return values From b6224147a976f818875dc5a2e8a19b955e8aefa6 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Wed, 26 Jul 2023 14:38:26 +0200 Subject: [PATCH 21/86] Slowly reassembling validator to see if this resolves issue github. --- optimade/models/entries.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/optimade/models/entries.py b/optimade/models/entries.py index 3453c6e98..a2dc5104b 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -186,18 +186,14 @@ def check_meta(cls, values): if meta is not None: for field in meta: if field == "property_metadata": - pass - # # check that all the fields under property metadata are in attributes - # attributes = ( - # values.get("attributes") if values.get("attributes") else {} - # ) - # property_metadata = meta.get(field) - # if property_metadata is not None: - # for ( - # subfield - # ) in ( - # property_metadata - # ): ## ToDo The names of the fields in attributes only need to be read once so this code can still be sped up. + # check that all the fields under property metadata are in attributes + (values.get("attributes") if values.get("attributes") else {}) + property_metadata = meta.get(field) + ## ToDo The names of the fields in attributes only need to be read once so this code can still be sped up. + if property_metadata is not None: + for subfield in property_metadata: + pass + # # if subfield not in attributes: # raise ValueError( # f"The keys under the field `property_metadata` need to match with the field names in attributes. The field {subfield} is however not in attributes." From 95bf11bb64f9e717cc3bd72e8a7a9b8aa85857d6 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Wed, 26 Jul 2023 15:34:49 +0200 Subject: [PATCH 22/86] Slowly reassembling validator to see if this resolves issue github. --- optimade/models/entries.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/optimade/models/entries.py b/optimade/models/entries.py index a2dc5104b..b467be8dc 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -187,17 +187,17 @@ def check_meta(cls, values): for field in meta: if field == "property_metadata": # check that all the fields under property metadata are in attributes - (values.get("attributes") if values.get("attributes") else {}) + attributes = ( + values.get("attributes") if values.get("attributes") else {} + ) property_metadata = meta.get(field) ## ToDo The names of the fields in attributes only need to be read once so this code can still be sped up. if property_metadata is not None: for subfield in property_metadata: - pass - # - # if subfield not in attributes: - # raise ValueError( - # f"The keys under the field `property_metadata` need to match with the field names in attributes. The field {subfield} is however not in attributes." - # ) + if subfield not in attributes: + raise ValueError( + f"The keys under the field `property_metadata` need to match with the field names in attributes. The field {subfield} is however not in attributes." + ) # # check that the fields under subfield are starting with prefix # for subsubfield in meta.get(field).get(subfield): # if subsubfield.startswith("_"): From 7c02f388e16abf26023495ea1198649dd7862226 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Wed, 26 Jul 2023 15:48:15 +0200 Subject: [PATCH 23/86] Slowly reassembling validator to see if this resolves issue github. --- optimade/models/entries.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/optimade/models/entries.py b/optimade/models/entries.py index b467be8dc..6a8e6f16b 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -198,10 +198,10 @@ def check_meta(cls, values): raise ValueError( f"The keys under the field `property_metadata` need to match with the field names in attributes. The field {subfield} is however not in attributes." ) - # # check that the fields under subfield are starting with prefix - # for subsubfield in meta.get(field).get(subfield): - # if subsubfield.startswith("_"): - # cls.check_field_supported_prefix(subsubfield) + # # check that the fields under subfield are starting with prefix + for subsubfield in meta.get(field).get(subfield): + if subsubfield.startswith("_"): + cls.check_field_supported_prefix(subsubfield) # else: # raise ValueError( # f"The Provider/Domain specific field {subsubfield} must be prefixed with a prefix that is supported by this database." From 2b52248640367a25f12fa80a5f4fdfc00bde1b56 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:47:15 +0200 Subject: [PATCH 24/86] Slowly reassembling validator to see if this resolves issue github. --- optimade/models/entries.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/optimade/models/entries.py b/optimade/models/entries.py index 6a8e6f16b..4f79b6001 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -190,16 +190,16 @@ def check_meta(cls, values): attributes = ( values.get("attributes") if values.get("attributes") else {} ) - property_metadata = meta.get(field) + property_metadata = meta.get("property_metadata") ## ToDo The names of the fields in attributes only need to be read once so this code can still be sped up. - if property_metadata is not None: + if property_metadata: for subfield in property_metadata: if subfield not in attributes: raise ValueError( f"The keys under the field `property_metadata` need to match with the field names in attributes. The field {subfield} is however not in attributes." ) # # check that the fields under subfield are starting with prefix - for subsubfield in meta.get(field).get(subfield): + for subsubfield in property_metadata.get(subfield, {}): if subsubfield.startswith("_"): cls.check_field_supported_prefix(subsubfield) # else: From 20e228d2832b0469b43d97f01fb0305014fff25c Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Wed, 26 Jul 2023 17:09:59 +0200 Subject: [PATCH 25/86] Slowly reassembling validator to see if this resolves issue github. --- optimade/models/entries.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/optimade/models/entries.py b/optimade/models/entries.py index 4f79b6001..ac7c82b01 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -200,8 +200,9 @@ def check_meta(cls, values): ) # # check that the fields under subfield are starting with prefix for subsubfield in property_metadata.get(subfield, {}): - if subsubfield.startswith("_"): - cls.check_field_supported_prefix(subsubfield) + pass + # if subsubfield.startswith("_"): + # cls.check_field_supported_prefix(subsubfield) # else: # raise ValueError( # f"The Provider/Domain specific field {subsubfield} must be prefixed with a prefix that is supported by this database." From 7be2a0fa2266db6f4607bce94616c4d923b0e29f Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Wed, 26 Jul 2023 17:15:17 +0200 Subject: [PATCH 26/86] Slowly reassembling validator to see if this resolves issue github. --- optimade/models/entries.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/optimade/models/entries.py b/optimade/models/entries.py index ac7c82b01..f15410d11 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -200,8 +200,8 @@ def check_meta(cls, values): ) # # check that the fields under subfield are starting with prefix for subsubfield in property_metadata.get(subfield, {}): - pass - # if subsubfield.startswith("_"): + if subsubfield.startswith("_"): + pass # cls.check_field_supported_prefix(subsubfield) # else: # raise ValueError( From bffb5d1df9681170334c0117939b844d9703121b Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Wed, 26 Jul 2023 17:20:50 +0200 Subject: [PATCH 27/86] Slowly reassembling validator to see if this resolves issue github. --- optimade/models/entries.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/optimade/models/entries.py b/optimade/models/entries.py index f15410d11..4f79b6001 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -201,8 +201,7 @@ def check_meta(cls, values): # # check that the fields under subfield are starting with prefix for subsubfield in property_metadata.get(subfield, {}): if subsubfield.startswith("_"): - pass - # cls.check_field_supported_prefix(subsubfield) + cls.check_field_supported_prefix(subsubfield) # else: # raise ValueError( # f"The Provider/Domain specific field {subsubfield} must be prefixed with a prefix that is supported by this database." From 1f0bbb964b09a3543a4e384437f03c11222b75ba Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Wed, 26 Jul 2023 17:32:07 +0200 Subject: [PATCH 28/86] Slowly reassembling validator to see if this resolves issue github. --- optimade/models/entries.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/optimade/models/entries.py b/optimade/models/entries.py index 4f79b6001..db54420d1 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -201,12 +201,12 @@ def check_meta(cls, values): # # check that the fields under subfield are starting with prefix for subsubfield in property_metadata.get(subfield, {}): if subsubfield.startswith("_"): - cls.check_field_supported_prefix(subsubfield) - # else: - # raise ValueError( - # f"The Provider/Domain specific field {subsubfield} must be prefixed with a prefix that is supported by this database." - # ) - # # At this point I am getting ahead of the specification. There is the intention to allow database specific fields(with the database specific prefixes) here in line with the JSON API specification, but it has not been decided yet how this case should be handled in the property definitions. + pass # cls.check_field_supported_prefix(subsubfield) + else: + raise ValueError( + f"The Provider/Domain specific field {subsubfield} must be prefixed with a prefix that is supported by this database." + ) + # At this point I am getting ahead of the specification. There is the intention to allow database specific fields(with the database specific prefixes) here in line with the JSON API specification, but it has not been decided yet how this case should be handled in the property definitions. elif field.startswith("_"): cls.check_field_supported_prefix(field) else: From 7c0a8be46e47087f1fc520e7371a046df63453d1 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Wed, 26 Jul 2023 17:52:49 +0200 Subject: [PATCH 29/86] Slowly reassembling validator to see if this resolves issue github. --- optimade/models/entries.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/optimade/models/entries.py b/optimade/models/entries.py index db54420d1..e398a0150 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -170,13 +170,13 @@ class EntryResource(Resource): ) def check_field_supported_prefix(field): - from optimade.server.mappers import BaseResourceMapper - - prefix = field.split("_")[1] - if prefix not in BaseResourceMapper.SUPPORTED_PREFIXES: - raise ValueError( - f"The prefix {prefix} of the field {field} is not supported by this server." - ) + # from optimade.server.mappers import BaseResourceMapper + pass + # prefix = field.split("_")[1] + # if prefix not in BaseResourceMapper.SUPPORTED_PREFIXES: + # raise ValueError( + # f"The prefix {prefix} of the field {field} is not supported by this server." + # ) @root_validator(pre=True) def check_meta(cls, values): @@ -201,7 +201,7 @@ def check_meta(cls, values): # # check that the fields under subfield are starting with prefix for subsubfield in property_metadata.get(subfield, {}): if subsubfield.startswith("_"): - pass # cls.check_field_supported_prefix(subsubfield) + cls.check_field_supported_prefix(subsubfield) else: raise ValueError( f"The Provider/Domain specific field {subsubfield} must be prefixed with a prefix that is supported by this database." From 456f34fd78cb6714f01f52c66804e5cd9e29694c Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Wed, 26 Jul 2023 18:10:36 +0200 Subject: [PATCH 30/86] Slowly reassembling validator to see if this resolves issue github. --- optimade/models/entries.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/optimade/models/entries.py b/optimade/models/entries.py index e398a0150..a326e9d7c 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -170,10 +170,11 @@ class EntryResource(Resource): ) def check_field_supported_prefix(field): - # from optimade.server.mappers import BaseResourceMapper - pass - # prefix = field.split("_")[1] - # if prefix not in BaseResourceMapper.SUPPORTED_PREFIXES: + from optimade.server.mappers import BaseResourceMapper + + prefix = field.split("_")[1] + if prefix not in BaseResourceMapper.SUPPORTED_PREFIXES: + pass # raise ValueError( # f"The prefix {prefix} of the field {field} is not supported by this server." # ) From 960327721fdc564005a38776151c1e4f4f099369 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Wed, 26 Jul 2023 18:54:37 +0200 Subject: [PATCH 31/86] Slowly reassembling validator to see if this resolves issue Github. --- optimade/models/entries.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/optimade/models/entries.py b/optimade/models/entries.py index a326e9d7c..80f3c69f2 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -170,14 +170,15 @@ class EntryResource(Resource): ) def check_field_supported_prefix(field): - from optimade.server.mappers import BaseResourceMapper + # from optimade.server.mappers import BaseResourceMapper + from optimade.server.config import CONFIG prefix = field.split("_")[1] - if prefix not in BaseResourceMapper.SUPPORTED_PREFIXES: - pass - # raise ValueError( - # f"The prefix {prefix} of the field {field} is not supported by this server." - # ) + # if prefix not in BaseResourceMapper.SUPPORTED_PREFIXES: + if prefix not in CONFIG.provider.prefix: + raise ValueError( + f"The prefix {prefix} of the field {field} is not supported by this server." + ) @root_validator(pre=True) def check_meta(cls, values): From c621412b576b9e1f2bb1bae2b0e443619f0b165d Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Wed, 26 Jul 2023 19:24:58 +0200 Subject: [PATCH 32/86] Slowly reassembling validator to see if this resolves issue Github. --- optimade/models/entries.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/optimade/models/entries.py b/optimade/models/entries.py index 80f3c69f2..dbabf8cd8 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -171,11 +171,11 @@ class EntryResource(Resource): def check_field_supported_prefix(field): # from optimade.server.mappers import BaseResourceMapper - from optimade.server.config import CONFIG + # from optimade.server.config import CONFIG prefix = field.split("_")[1] # if prefix not in BaseResourceMapper.SUPPORTED_PREFIXES: - if prefix not in CONFIG.provider.prefix: + if prefix not in "exmpl": raise ValueError( f"The prefix {prefix} of the field {field} is not supported by this server." ) From ed447362ba41386eb1a1ff278d1f9f8c14a89f7c Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Wed, 26 Jul 2023 19:51:43 +0200 Subject: [PATCH 33/86] Slowly reassembling validator to see if this resolves issue Github.. --- optimade/models/entries.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/optimade/models/entries.py b/optimade/models/entries.py index dbabf8cd8..a067a8c1f 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -170,12 +170,16 @@ class EntryResource(Resource): ) def check_field_supported_prefix(field): - # from optimade.server.mappers import BaseResourceMapper + import optimade.server.mappers.entries + # from optimade.server.config import CONFIG prefix = field.split("_")[1] - # if prefix not in BaseResourceMapper.SUPPORTED_PREFIXES: - if prefix not in "exmpl": + if ( + prefix + not in optimade.server.mappers.entries.BaseResourceMapper.SUPPORTED_PREFIXES + ): + # if prefix not in "exmpl": raise ValueError( f"The prefix {prefix} of the field {field} is not supported by this server." ) From 4f28acc0bdf027cd5f278b1c91f41cb48ee44002 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Wed, 26 Jul 2023 20:15:02 +0200 Subject: [PATCH 34/86] Slowly reassembling validator to see if this resolves issue Github.. --- optimade/models/entries.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/optimade/models/entries.py b/optimade/models/entries.py index a067a8c1f..3b5fdee5f 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -170,10 +170,12 @@ class EntryResource(Resource): ) def check_field_supported_prefix(field): + import yaml + import optimade.server.mappers.entries # from optimade.server.config import CONFIG - + yaml prefix = field.split("_")[1] if ( prefix From 63af8dc658f14685946998d5ea8240bd51d3475f Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Wed, 26 Jul 2023 20:30:54 +0200 Subject: [PATCH 35/86] Placed yaml import in try except block. --- optimade/models/entries.py | 3 --- optimade/server/config.py | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/optimade/models/entries.py b/optimade/models/entries.py index 3b5fdee5f..37bd0b379 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -170,12 +170,9 @@ class EntryResource(Resource): ) def check_field_supported_prefix(field): - import yaml - import optimade.server.mappers.entries # from optimade.server.config import CONFIG - yaml prefix = field.split("_")[1] if ( prefix diff --git a/optimade/server/config.py b/optimade/server/config.py index 48c71fc86..bd090bb95 100644 --- a/optimade/server/config.py +++ b/optimade/server/config.py @@ -87,8 +87,6 @@ def config_file_settings(settings: BaseSettings) -> Dict[str, Any]: import json import os - import yaml - encoding = settings.__config__.env_file_encoding config_file = Path(os.getenv("OPTIMADE_CONFIG_FILE", DEFAULT_CONFIG_FILE_PATH)) @@ -100,6 +98,8 @@ def config_file_settings(settings: BaseSettings) -> Dict[str, Any]: res = json.loads(config_file_content) except json.JSONDecodeError as json_exc: try: + import yaml + # This can essentially also load JSON files, as JSON is a subset of YAML v1, # but I suspect it is not as rigorous res = yaml.safe_load(config_file_content) From 5b41b71f6f1166014e5db2ffeb34c18747963557 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Wed, 2 Aug 2023 14:15:21 +0200 Subject: [PATCH 36/86] Added more cases to test data and added bugfix for removing associated metadata when a field is excluded. --- optimade/models/entries.py | 73 +++++++++++++---------- optimade/server/data/test_structures.json | 53 +++++++++++++++- optimade/server/routers/utils.py | 9 ++- tests/server/routers/test_structures.py | 2 +- 4 files changed, 100 insertions(+), 37 deletions(-) diff --git a/optimade/models/entries.py b/optimade/models/entries.py index 37bd0b379..608b4cafc 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -2,7 +2,11 @@ from datetime import datetime from typing import Dict, List, Optional -from pydantic import BaseModel, root_validator, validator +from pydantic import ( + BaseModel, + root_validator, + validator, +) # pylint: disable=no-name-in-module from optimade.models.jsonapi import Attributes, Meta, Relationships, Resource @@ -18,6 +22,26 @@ ) +def starts_with_supported_prefix(field): + from optimade.server.mappers.entries import BaseResourceMapper + + prefix = None + if field.startswith("_"): + prefix = field.split("_")[1] + if prefix in BaseResourceMapper.SUPPORTED_PREFIXES: + return True, prefix + return False, prefix + + +def _check_starts_with_supported_prefix(field, message=""): + prefixed, prefix = starts_with_supported_prefix(field) + if not prefixed: + raise ValueError( + f"The field {field} either has no prefix or the prefix {prefix} is not supported by this server." + + message + ) + + class TypedRelationship(Relationship): # This may be updated when moving to Python 3.8 @validator("data") @@ -169,55 +193,38 @@ class EntryResource(Resource): The OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object.""", ) - def check_field_supported_prefix(field): - import optimade.server.mappers.entries - - # from optimade.server.config import CONFIG - prefix = field.split("_")[1] - if ( - prefix - not in optimade.server.mappers.entries.BaseResourceMapper.SUPPORTED_PREFIXES - ): - # if prefix not in "exmpl": - raise ValueError( - f"The prefix {prefix} of the field {field} is not supported by this server." - ) - @root_validator(pre=True) def check_meta(cls, values): - # """Validator to check whether meta field has been formatted correctly.""" - # + """Validator to check whether meta field has been formatted correctly.""" meta = values.get("meta") - if meta is not None: + if meta: for field in meta: if field == "property_metadata": # check that all the fields under property metadata are in attributes - attributes = ( - values.get("attributes") if values.get("attributes") else {} - ) + attributes = values.get("attributes", {}) property_metadata = meta.get("property_metadata") - ## ToDo The names of the fields in attributes only need to be read once so this code can still be sped up. if property_metadata: for subfield in property_metadata: if subfield not in attributes: raise ValueError( f"The keys under the field `property_metadata` need to match with the field names in attributes. The field {subfield} is however not in attributes." ) - # # check that the fields under subfield are starting with prefix - for subsubfield in property_metadata.get(subfield, {}): - if subsubfield.startswith("_"): - cls.check_field_supported_prefix(subsubfield) - else: - raise ValueError( - f"The Provider/Domain specific field {subsubfield} must be prefixed with a prefix that is supported by this database." + # check that the fields under subfield are starting with prefix + subsubfields = property_metadata.get(subfield) + if subsubfields: + for subsubfield in subsubfields: + _check_starts_with_supported_prefix( + subsubfield, + "Currently no OPTIMADE fields have been defined for the per attribute metadata, thus only database and domain specific fields are allowed", ) + # At this point I am getting ahead of the specification. There is the intention to allow database specific fields(with the database specific prefixes) here in line with the JSON API specification, but it has not been decided yet how this case should be handled in the property definitions. - elif field.startswith("_"): - cls.check_field_supported_prefix(field) else: - raise ValueError( - "The fields under meta either need to be database specific fields or the field `property_metadata'" + _check_starts_with_supported_prefix( + field, + 'Currently no OPTIMADE fields other than "property_metadata" have been defined for the per entry "meta" field, thus only database and domain specific fields are allowed.', ) + return values diff --git a/optimade/server/data/test_structures.json b/optimade/server/data/test_structures.json index 9ca447bf6..3d147c5ba 100644 --- a/optimade/server/data/test_structures.json +++ b/optimade/server/data/test_structures.json @@ -6,7 +6,7 @@ "meta": { "property_metadata": { "elements_ratios": { - "_exmpl_originates_from_project":"Actinides" + "_exmpl_originates_from_project":"Pure Metals" } } }, @@ -87,6 +87,13 @@ "_id": { "$oid": "5cfb441f053b174410700d03" }, + "meta": { + "property_metadata": { + "elements_ratios": { + "_exmpl_originates_from_project":"Actinides_Alloys" + } + } + }, "assemblies": null, "chemsys": "Ac-Ag-Ir", "cartesian_site_positions": [ @@ -204,6 +211,13 @@ "_id": { "$oid": "5cfb441f053b174410700d04" }, + "meta": { + "property_metadata": { + "elements_ratios": { + "_exmpl_originates_from_project":"Actinides_Alloys" + } + } + }, "assemblies": null, "chemsys": "Ac-Ag-Pb", "cartesian_site_positions": [ @@ -330,6 +344,13 @@ "_id": { "$oid": "5cfb441f053b174410700d18" }, + "meta": { + "property_metadata": { + "elements_ratios": { + "_exmpl_originates_from_project":"Actinides_Alloys" + } + } + }, "assemblies": null, "chemsys": "Ac-Mg", "cartesian_site_positions": [ @@ -420,6 +441,13 @@ "_id": { "$oid": "5cfb441f053b174410700d1f" }, + "meta": { + "property_metadata": { + "elements_ratios": { + "_exmpl_originates_from_project":null + } + } + }, "assemblies": null, "chemsys": "Ac-O", "cartesian_site_positions": [ @@ -522,6 +550,11 @@ "_id": { "$oid": "5cfb441f053b174410700d6f" }, + "meta": { + "property_metadata": { + "elements_ratios": {} + } + }, "assemblies": null, "chemsys": "Ac-Cu-F-O", "cartesian_site_positions": [ @@ -646,6 +679,13 @@ "_id": { "$oid": "5cfb441f053b174410700dc9" }, + "meta": { + "property_metadata": { + "elements_ratios": { + "_exmpl_originates_from_project":"Pure Metals" + } + } + }, "assemblies": null, "chemsys": "Ag", "cartesian_site_positions": [ @@ -713,6 +753,11 @@ "_id": { "$oid": "5cfb441f053b174410700ddd" }, + "meta": { + "property_metadata": { + "elements_ratios": null + } + }, "assemblies": null, "chemsys": "Ag-Br-Cl-Te", "cartesian_site_positions": [ @@ -903,6 +948,9 @@ "_id": { "$oid": "5cfb441f053b174410700e04" }, + "meta": { + "property_metadata": {} + }, "assemblies": null, "chemsys": "Ag-C-Cl-N-O-S", "cartesian_site_positions": [ @@ -1079,6 +1127,9 @@ "_id": { "$oid": "5cfb441f053b174410700e11" }, + "meta": { + "property_metadata": null + }, "assemblies": null, "chemsys": "Ag-C-Cl-H-N", "cartesian_site_positions": [ diff --git a/optimade/server/routers/utils.py b/optimade/server/routers/utils.py index f72f9f2a0..96a9d42cd 100644 --- a/optimade/server/routers/utils.py +++ b/optimade/server/routers/utils.py @@ -125,8 +125,13 @@ def handle_response_fields( for field in exclude_fields: if field in new_entry["attributes"]: del new_entry["attributes"][field] - if field in new_entry.get("meta", {}).get("property_metadata", {}): - del new_entry["meta"]["property_metadata"][field] + if new_entry.get("meta") and new_entry.get("meta").get( # type: ignore[union-attr] + "property_metadata" + ): + if field in new_entry.get("meta", {}).get( + "property_metadata", {} + ): # type: ignore[union-attr] + del new_entry["meta"]["property_metadata"][field] # Include missing fields that were requested in `response_fields` for field in include_fields: diff --git a/tests/server/routers/test_structures.py b/tests/server/routers/test_structures.py index bf49797f2..7f8bfa726 100644 --- a/tests/server/routers/test_structures.py +++ b/tests/server/routers/test_structures.py @@ -73,7 +73,7 @@ def test_structures_endpoint_data(self): self.json_response["data"]["meta"]["property_metadata"]["elements_ratios"][ "_exmpl_originates_from_project" ] - == "Actinides" + == "Pure Metals" ) From f694d1518895c06810fd9584c7875cdca84b4b6b Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Wed, 2 Aug 2023 15:26:14 +0200 Subject: [PATCH 37/86] Removed seemingly unneccesary mypy ignore exception statement. --- optimade/models/entries.py | 1 - 1 file changed, 1 deletion(-) diff --git a/optimade/models/entries.py b/optimade/models/entries.py index 608b4cafc..f987bec9f 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -8,7 +8,6 @@ validator, ) -# pylint: disable=no-name-in-module from optimade.models.jsonapi import Attributes, Meta, Relationships, Resource from optimade.models.optimade_json import DataType, Relationship from optimade.models.utils import OptimadeField, StrictField, SupportLevel From 40ff136e86bfd8b19774cca3eb5738ee3724530a Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Wed, 2 Aug 2023 15:30:31 +0200 Subject: [PATCH 38/86] remove change in version pyyaml in requirements.txt. --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 9df703569..ed6568ac5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ email_validator==2.0.0.post2 lark==1.1.7 pydantic==1.10.9 -pyyaml==6.0.1 +pyyaml==6.0 requests==2.31.0 uvicorn==0.23.1 From 8f7ca29345f26410faf7e0cca6c595658157ef40 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Wed, 2 Aug 2023 15:59:27 +0200 Subject: [PATCH 39/86] correct spelling mistake --- openapi/index_openapi.json | 6 +- openapi/openapi.json | 126 ++++++++++++++++++------------------- optimade/models/entries.py | 2 +- 3 files changed, 67 insertions(+), 67 deletions(-) diff --git a/openapi/index_openapi.json b/openapi/index_openapi.json index c2079a213..79ad66476 100644 --- a/openapi/index_openapi.json +++ b/openapi/index_openapi.json @@ -474,7 +474,7 @@ "property_metadata": { "title": "Property Metadata", "type": "object", - "description": "A dictionary, where the keys are the names of the properties in the attributes field and the value is a dictionary containing the meta data for that property.\nDatabase-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes)." + "description": "A dictionary, where the keys are the names of the properties in the attributes field and the value is a dictionary containing the metadata for that property.\nDatabase-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes)." } }, "description": "Contains the metadata for the attributes of an entry" @@ -585,8 +585,8 @@ "type": "string", "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "format": "date-time", - "x-optimade-queryable": "must", - "x-optimade-support": "should" + "x-optimade-support": "should", + "x-optimade-queryable": "must" } }, "description": "Contains key-value pairs representing the entry's properties." diff --git a/openapi/openapi.json b/openapi/openapi.json index 8fef2fe68..9f1ecd33e 100644 --- a/openapi/openapi.json +++ b/openapi/openapi.json @@ -1341,8 +1341,8 @@ } }, "description": "Index of the sites (0-based) that belong to each group for each assembly.\n\n- **Examples**:\n - `[[1], [2]]`: two groups, one with the second site, one with the third.\n - `[[1,2], [3]]`: one group with the second and third site, one with the fourth.", - "x-optimade-queryable": "optional", - "x-optimade-support": "must" + "x-optimade-support": "must", + "x-optimade-queryable": "optional" }, "group_probabilities": { "title": "Group Probabilities", @@ -1351,8 +1351,8 @@ "type": "number" }, "description": "Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\nIt SHOULD sum to one.\nSee below for examples of how to specify the probability of the occurrence of a vacancy.\nThe possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.", - "x-optimade-queryable": "optional", - "x-optimade-support": "must" + "x-optimade-support": "must", + "x-optimade-queryable": "optional" } }, "description": "A description of groups of sites that are statistically correlated.\n\n- **Examples** (for each entry of the assemblies list):\n - `{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}`: the first site and the second site never occur at the same time in the unit cell.\n Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.\n - `{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}`: the second and third site are either present together or not present; they form the first group of atoms for this assembly.\n The second group is formed by the fourth site. Sites of the first group (the second and the third) are never present at the same time as the fourth site.\n 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent)." @@ -1724,7 +1724,7 @@ "property_metadata": { "title": "Property Metadata", "type": "object", - "description": "A dictionary, where the keys are the names of the properties in the attributes field and the value is a dictionary containing the meta data for that property.\nDatabase-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes)." + "description": "A dictionary, where the keys are the names of the properties in the attributes field and the value is a dictionary containing the metadata for that property.\nDatabase-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes)." } }, "description": "Contains the metadata for the attributes of an entry" @@ -1835,8 +1835,8 @@ "type": "string", "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "format": "date-time", - "x-optimade-queryable": "must", - "x-optimade-support": "should" + "x-optimade-support": "should", + "x-optimade-queryable": "must" } }, "description": "Contains key-value pairs representing the entry's properties." @@ -2660,8 +2660,8 @@ "type": "string", "description": "The name of the type of an entry.\n- **Type**: string.\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type and ID MUST be returned in response to a request for `//` under the versioned base URL.\n- **Example**: `\"structures\"`", "default": "references", - "x-optimade-queryable": "must", - "x-optimade-support": "must" + "x-optimade-support": "must", + "x-optimade-queryable": "must" }, "links": { "title": "Links", @@ -2715,8 +2715,8 @@ "type": "string", "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "format": "date-time", - "x-optimade-queryable": "must", - "x-optimade-support": "should" + "x-optimade-support": "should", + "x-optimade-queryable": "must" }, "authors": { "title": "Authors", @@ -2725,8 +2725,8 @@ "$ref": "#/components/schemas/Person" }, "description": "List of person objects containing the authors of the reference.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "editors": { "title": "Editors", @@ -2735,8 +2735,8 @@ "$ref": "#/components/schemas/Person" }, "description": "List of person objects containing the editors of the reference.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "doi": { "title": "Doi", @@ -2752,8 +2752,8 @@ "type": "string", "description": "The URL of the reference.", "format": "uri", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "address": { "title": "Address", @@ -3339,8 +3339,8 @@ "type": "string" }, "description": "MUST be a list of strings of all chemical elements composing this species. Each item of the list MUST be one of the following:\n\n- a valid chemical-element symbol, or\n- the special value `\"X\"` to represent a non-chemical element, or\n- the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\n\nIf any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.", - "x-optimade-queryable": "optional", - "x-optimade-support": "must" + "x-optimade-support": "must", + "x-optimade-queryable": "optional" }, "concentration": { "title": "Concentration", @@ -3349,8 +3349,8 @@ "type": "number" }, "description": "MUST be a list of floats, with same length as `chemical_symbols`. The numbers represent the relative concentration of the corresponding chemical symbol in this species. The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\n\n- Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\n- Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\n\nNote that concentrations are uncorrelated between different site (even of the same species).", - "x-optimade-queryable": "optional", - "x-optimade-support": "must" + "x-optimade-support": "must", + "x-optimade-queryable": "optional" }, "mass": { "title": "Mass", @@ -3359,9 +3359,9 @@ "type": "number" }, "description": "If present MUST be a list of floats expressed in a.m.u.\nElements denoting vacancies MUST have masses equal to 0.", - "x-optimade-queryable": "optional", "x-optimade-unit": "a.m.u.", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "original_name": { "title": "Original Name", @@ -3377,8 +3377,8 @@ "type": "string" }, "description": "If provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "nattached": { "title": "Nattached", @@ -3387,8 +3387,8 @@ "type": "integer" }, "description": "If provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the :field:`attached` key.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" } }, "description": "A list describing the species of the sites of this structure.\n\nSpecies can represent pure chemical elements, virtual-crystal atoms representing a\nstatistical occupation of a given site by multiple chemical elements, and/or a\nlocation to which there are attached atoms, i.e., atoms whose precise location are\nunknown beyond that they are attached to that position (frequently used to indicate\nhydrogen atoms attached to another element, e.g., a carbon with three attached\nhydrogens might represent a methyl group, -CH3).\n\n- **Examples**:\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]`: any site with this species is occupied by a Ti atom.\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]`: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.\n - `[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]`: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.\n - `[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]`: any site with this species is occupied by a carbon isotope with mass 12.\n - `[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]`: any site with this species is occupied by a carbon isotope with mass 13.\n - `[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]`: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms." @@ -3466,8 +3466,8 @@ "type": "string", "description": "The name of the type of an entry.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type `` and ID `` MUST be returned in response to a request for `//` under the versioned base URL.\n\n- **Examples**:\n - `\"structures\"`", "default": "structures", - "x-optimade-queryable": "must", - "x-optimade-support": "must" + "x-optimade-support": "must", + "x-optimade-queryable": "must" }, "links": { "title": "Links", @@ -3536,8 +3536,8 @@ "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "format": "date-time", "nullable": true, - "x-optimade-queryable": "must", - "x-optimade-support": "should" + "x-optimade-support": "should", + "x-optimade-queryable": "must" }, "elements": { "title": "Elements", @@ -3547,16 +3547,16 @@ }, "description": "The chemical symbols of the different elements present in the structure.\n\n- **Type**: list of strings.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The strings are the chemical symbols, i.e., either a single uppercase letter or an uppercase letter followed by a number of lowercase letters.\n - The order MUST be alphabetical.\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements_ratios`, if the latter is provided.\n - Note: This property SHOULD NOT contain the string \"X\" to indicate non-chemical elements or \"vacancy\" to indicate vacancies (in contrast to the field `chemical_symbols` for the `species` property).\n\n- **Examples**:\n - `[\"Si\"]`\n - `[\"Al\",\"O\",\"Si\"]`\n\n- **Query examples**:\n - A filter that matches all records of structures that contain Si, Al **and** O, and possibly other elements: `elements HAS ALL \"Si\", \"Al\", \"O\"`.\n - To match structures with exactly these three elements, use `elements HAS ALL \"Si\", \"Al\", \"O\" AND elements LENGTH 3`.\n - Note: length queries on this property can be equivalently formulated by filtering on the `nelements`_ property directly.", "nullable": true, - "x-optimade-queryable": "must", - "x-optimade-support": "should" + "x-optimade-support": "should", + "x-optimade-queryable": "must" }, "nelements": { "title": "Nelements", "type": "integer", "description": "Number of different elements in the structure as an integer.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - MUST be equal to the lengths of the list properties `elements` and `elements_ratios`, if they are provided.\n\n- **Examples**:\n - `3`\n\n- **Querying**:\n - Note: queries on this property can equivalently be formulated using `elements LENGTH`.\n - A filter that matches structures that have exactly 4 elements: `nelements=4`.\n - A filter that matches structures that have between 2 and 7 elements: `nelements>=2 AND nelements<=7`.", "nullable": true, - "x-optimade-queryable": "must", - "x-optimade-support": "should" + "x-optimade-support": "should", + "x-optimade-queryable": "must" }, "elements_ratios": { "title": "Elements Ratios", @@ -3566,16 +3566,16 @@ }, "description": "Relative proportions of different elements in the structure.\n\n- **Type**: list of floats\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - Composed by the proportions of elements in the structure as a list of floating point numbers.\n - The sum of the numbers MUST be 1.0 (within floating point accuracy)\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements`, if the latter is provided.\n\n- **Examples**:\n - `[1.0]`\n - `[0.3333333333333333, 0.2222222222222222, 0.4444444444444444]`\n\n- **Query examples**:\n - Note: Useful filters can be formulated using the set operator syntax for correlated values.\n However, since the values are floating point values, the use of equality comparisons is generally inadvisable.\n - OPTIONAL: a filter that matches structures where approximately 1/3 of the atoms in the structure are the element Al is: `elements:elements_ratios HAS ALL \"Al\":>0.3333, \"Al\":<0.3334`.", "nullable": true, - "x-optimade-queryable": "must", - "x-optimade-support": "should" + "x-optimade-support": "should", + "x-optimade-queryable": "must" }, "chemical_formula_descriptive": { "title": "Chemical Formula Descriptive", "type": "string", "description": "The chemical formula for a structure as a string in a form chosen by the API implementation.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The chemical formula is given as a string consisting of properly capitalized element symbols followed by integers or decimal numbers, balanced parentheses, square, and curly brackets `(`,`)`, `[`,`]`, `{`, `}`, commas, the `+`, `-`, `:` and `=` symbols. The parentheses are allowed to be followed by a number. Spaces are allowed anywhere except within chemical symbols. The order of elements and any groupings indicated by parentheses or brackets are chosen freely by the API implementation.\n - The string SHOULD be arithmetically consistent with the element ratios in the `chemical_formula_reduced` property.\n - It is RECOMMENDED, but not mandatory, that symbols, parentheses and brackets, if used, are used with the meanings prescribed by [IUPAC's Nomenclature of Organic Chemistry](https://www.qmul.ac.uk/sbcs/iupac/bibliog/blue.html).\n\n- **Examples**:\n - `\"(H2O)2 Na\"`\n - `\"NaCl\"`\n - `\"CaCO3\"`\n - `\"CCaO3\"`\n - `\"(CH3)3N+ - [CH2]2-OH = Me3N+ - CH2 - CH2OH\"`\n\n- **Query examples**:\n - Note: the free-form nature of this property is likely to make queries on it across different databases inconsistent.\n - A filter that matches an exactly given formula: `chemical_formula_descriptive=\"(H2O)2 Na\"`.\n - A filter that does a partial match: `chemical_formula_descriptive CONTAINS \"H2O\"`.", "nullable": true, - "x-optimade-queryable": "must", - "x-optimade-support": "should" + "x-optimade-support": "should", + "x-optimade-queryable": "must" }, "chemical_formula_reduced": { "title": "Chemical Formula Reduced", @@ -3583,16 +3583,16 @@ "type": "string", "description": "The reduced chemical formula for a structure as a string with element symbols and integer chemical proportion numbers.\nThe proportion number MUST be omitted if it is 1.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property.\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\n Intricate queries on formula components are instead suggested to be formulated using set-type filter operators on the multi valued `elements` and `elements_ratios` properties.\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\n - Elements MUST be placed in alphabetical order, followed by their integer chemical proportion number.\n - For structures with no partial occupation, the chemical proportion numbers are the smallest integers for which the chemical proportion is exactly correct.\n - For structures with partial occupation, the chemical proportion numbers are integers that within reasonable approximation indicate the correct chemical proportions. The precise details of how to perform the rounding is chosen by the API implementation.\n - No spaces or separators are allowed.\n\n- **Examples**:\n - `\"H2NaO\"`\n - `\"ClNa\"`\n - `\"CCaO3\"`\n\n- **Query examples**:\n - A filter that matches an exactly given formula is `chemical_formula_reduced=\"H2NaO\"`.", "nullable": true, - "x-optimade-queryable": "must", - "x-optimade-support": "should" + "x-optimade-support": "should", + "x-optimade-queryable": "must" }, "chemical_formula_hill": { "title": "Chemical Formula Hill", "pattern": "(^$)|^([A-Z][a-z]?([2-9]|[1-9]\\d+)?)+$", "type": "string", "description": "The chemical formula for a structure in [Hill form](https://dx.doi.org/10.1021/ja02046a005) with element symbols followed by integer chemical proportion numbers. The proportion number MUST be omitted if it is 1.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, only a subset of the filter features MAY be supported.\n - The overall scale factor of the chemical proportions is chosen such that the resulting values are integers that indicate the most chemically relevant unit of which the system is composed.\n For example, if the structure is a repeating unit cell with four hydrogens and four oxygens that represents two hydroperoxide molecules, `chemical_formula_hill` is `\"H2O2\"` (i.e., not `\"HO\"`, nor `\"H4O4\"`).\n - If the chemical insight needed to ascribe a Hill formula to the system is not present, the property MUST be handled as unset.\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\n - Elements MUST be placed in [Hill order](https://dx.doi.org/10.1021/ja02046a005), followed by their integer chemical proportion number.\n Hill order means: if carbon is present, it is placed first, and if also present, hydrogen is placed second.\n After that, all other elements are ordered alphabetically.\n If carbon is not present, all elements are ordered alphabetically.\n - If the system has sites with partial occupation and the total occupations of each element do not all sum up to integers, then the Hill formula SHOULD be handled as unset.\n - No spaces or separators are allowed.\n\n- **Examples**:\n - `\"H2O2\"`\n\n- **Query examples**:\n - A filter that matches an exactly given formula is `chemical_formula_hill=\"H2O2\"`.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "chemical_formula_anonymous": { "title": "Chemical Formula Anonymous", @@ -3600,8 +3600,8 @@ "type": "string", "description": "The anonymous formula is the `chemical_formula_reduced`, but where the elements are instead first ordered by their chemical proportion number, and then, in order left to right, replaced by anonymous symbols A, B, C, ..., Z, Aa, Ba, ..., Za, Ab, Bb, ... and so on.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property.\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\n\n- **Examples**:\n - `\"A2B\"`\n - `\"A42B42C16D12E10F9G5\"`\n\n- **Querying**:\n - A filter that matches an exactly given formula is `chemical_formula_anonymous=\"A2B\"`.", "nullable": true, - "x-optimade-queryable": "must", - "x-optimade-support": "should" + "x-optimade-support": "should", + "x-optimade-queryable": "must" }, "dimension_types": { "title": "Dimension Types", @@ -3613,16 +3613,16 @@ }, "description": "List of three integers.\nFor each of the three directions indicated by the three lattice vectors (see property `lattice_vectors`), this list indicates if the direction is periodic (value `1`) or non-periodic (value `0`).\nNote: the elements in this list each refer to the direction of the corresponding entry in `lattice_vectors` and *not* the Cartesian x, y, z directions.\n\n- **Type**: list of integers.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n - MUST be a list of length 3.\n - Each integer element MUST assume only the value 0 or 1.\n\n- **Examples**:\n - For a molecule: `[0, 0, 0]`\n - For a wire along the direction specified by the third lattice vector: `[0, 0, 1]`\n - For a 2D surface/slab, periodic on the plane defined by the first and third lattice vectors: `[1, 0, 1]`\n - For a bulk 3D system: `[1, 1, 1]`", "nullable": true, - "x-optimade-queryable": "optional", - "x-optimade-support": "should" + "x-optimade-support": "should", + "x-optimade-queryable": "optional" }, "nperiodic_dimensions": { "title": "Nperiodic Dimensions", "type": "integer", "description": "An integer specifying the number of periodic dimensions in the structure, equivalent to the number of non-zero entries in `dimension_types`.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The integer value MUST be between 0 and 3 inclusive and MUST be equal to the sum of the items in the `dimension_types` property.\n - This property only reflects the treatment of the lattice vectors provided for the structure, and not any physical interpretation of the dimensionality of its contents.\n\n- **Examples**:\n - `2` should be indicated in cases where `dimension_types` is any of `[1, 1, 0]`, `[1, 0, 1]`, `[0, 1, 1]`.\n\n- **Query examples**:\n - Match only structures with exactly 3 periodic dimensions: `nperiodic_dimensions=3`\n - Match all structures with 2 or fewer periodic dimensions: `nperiodic_dimensions<=2`", "nullable": true, - "x-optimade-queryable": "must", - "x-optimade-support": "should" + "x-optimade-support": "should", + "x-optimade-queryable": "must" }, "lattice_vectors": { "title": "Lattice Vectors", @@ -3639,9 +3639,9 @@ }, "description": "The three lattice vectors in Cartesian coordinates, in \u00e5ngstr\u00f6m (\u00c5).\n\n- **Type**: list of list of floats or unknown values.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - MUST be a list of three vectors *a*, *b*, and *c*, where each of the vectors MUST BE a list of the vector's coordinates along the x, y, and z Cartesian coordinates.\n (Therefore, the first index runs over the three lattice vectors and the second index runs over the x, y, z Cartesian coordinates).\n - For databases that do not define an absolute Cartesian system (e.g., only defining the length and angles between vectors), the first lattice vector SHOULD be set along *x* and the second on the *xy*-plane.\n - MUST always contain three vectors of three coordinates each, independently of the elements of property `dimension_types`.\n The vectors SHOULD by convention be chosen so the determinant of the `lattice_vectors` matrix is different from zero.\n The vectors in the non-periodic directions have no significance beyond fulfilling these requirements.\n - The coordinates of the lattice vectors of non-periodic dimensions (i.e., those dimensions for which `dimension_types` is `0`) MAY be given as a list of all `null` values.\n If a lattice vector contains the value `null`, all coordinates of that lattice vector MUST be `null`.\n\n- **Examples**:\n - `[[4.0,0.0,0.0],[0.0,4.0,0.0],[0.0,1.0,4.0]]` represents a cell, where the first vector is `(4, 0, 0)`, i.e., a vector aligned along the `x` axis of length 4 \u00c5; the second vector is `(0, 4, 0)`; and the third vector is `(0, 1, 4)`.", "nullable": true, - "x-optimade-queryable": "optional", + "x-optimade-unit": "\u00c5", "x-optimade-support": "should", - "x-optimade-unit": "\u00c5" + "x-optimade-queryable": "optional" }, "cartesian_site_positions": { "title": "Cartesian Site Positions", @@ -3656,17 +3656,17 @@ }, "description": "Cartesian positions of each site in the structure.\nA site is usually used to describe positions of atoms; what atoms can be encountered at a given site is conveyed by the `species_at_sites` property, and the species themselves are described in the `species` property.\n\n- **Type**: list of list of floats\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - It MUST be a list of length equal to the number of sites in the structure, where every element is a list of the three Cartesian coordinates of a site expressed as float values in the unit angstrom (\u00c5).\n - An entry MAY have multiple sites at the same Cartesian position (for a relevant use of this, see e.g., the property `assemblies`).\n\n- **Examples**:\n - `[[0,0,0],[0,0,2]]` indicates a structure with two sites, one sitting at the origin and one along the (positive) *z*-axis, 2 \u00c5 away from the origin.", "nullable": true, - "x-optimade-queryable": "optional", + "x-optimade-unit": "\u00c5", "x-optimade-support": "should", - "x-optimade-unit": "\u00c5" + "x-optimade-queryable": "optional" }, "nsites": { "title": "Nsites", "type": "integer", "description": "An integer specifying the length of the `cartesian_site_positions` property.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `42`\n\n- **Query examples**:\n - Match only structures with exactly 4 sites: `nsites=4`\n - Match structures that have between 2 and 7 sites: `nsites>=2 AND nsites<=7`", "nullable": true, - "x-optimade-queryable": "must", - "x-optimade-support": "should" + "x-optimade-support": "should", + "x-optimade-queryable": "must" }, "species": { "title": "Species", @@ -3676,8 +3676,8 @@ }, "description": "A list describing the species of the sites of this structure.\nSpecies can represent pure chemical elements, virtual-crystal atoms representing a statistical occupation of a given site by multiple chemical elements, and/or a location to which there are attached atoms, i.e., atoms whose precise location are unknown beyond that they are attached to that position (frequently used to indicate hydrogen atoms attached to another element, e.g., a carbon with three attached hydrogens might represent a methyl group, -CH3).\n\n- **Type**: list of dictionary with keys:\n - `name`: string (REQUIRED)\n - `chemical_symbols`: list of strings (REQUIRED)\n - `concentration`: list of float (REQUIRED)\n - `attached`: list of strings (REQUIRED)\n - `nattached`: list of integers (OPTIONAL)\n - `mass`: list of floats (OPTIONAL)\n - `original_name`: string (OPTIONAL).\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - Each list member MUST be a dictionary with the following keys:\n - **name**: REQUIRED; gives the name of the species; the **name** value MUST be unique in the `species` list;\n - **chemical_symbols**: REQUIRED; MUST be a list of strings of all chemical elements composing this species.\n Each item of the list MUST be one of the following:\n - a valid chemical-element symbol, or\n - the special value `\"X\"` to represent a non-chemical element, or\n - the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\n\n If any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.\n\n - **concentration**: REQUIRED; MUST be a list of floats, with same length as `chemical_symbols`.\n The numbers represent the relative concentration of the corresponding chemical symbol in this species.\n The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\n\n - Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\n - Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\n\n Note that concentrations are uncorrelated between different sites (even of the same species).\n\n - **attached**: OPTIONAL; if provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.\n\n - **nattached**: OPTIONAL; if provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the `attached` key.\n\n The implementation MUST include either both or none of the `attached` and `nattached` keys, and if they are provided, they MUST be of the same length.\n Furthermore, if they are provided, the `structure_features` property MUST include the string `site_attachments`.\n\n - **mass**: OPTIONAL. If present MUST be a list of floats, with the same length as `chemical_symbols`, providing element masses expressed in a.m.u.\n Elements denoting vacancies MUST have masses equal to 0.\n\n - **original_name**: OPTIONAL. Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\n\n Note: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.\n\n The main use of this field is for source databases that use species names, containing characters that are not allowed (see description of the list property `species_at_sites`).\n\n - For systems that have only species formed by a single chemical symbol, and that have at most one species per chemical symbol, SHOULD use the chemical symbol as species name (e.g., `\"Ti\"` for titanium, `\"O\"` for oxygen, etc.)\n However, note that this is OPTIONAL, and client implementations MUST NOT assume that the key corresponds to a chemical symbol, nor assume that if the species name is a valid chemical symbol, that it represents a species with that chemical symbol.\n This means that a species `{\"name\": \"C\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]}` is valid and represents a titanium species (and *not* a carbon species).\n - It is NOT RECOMMENDED that a structure includes species that do not have at least one corresponding site.\n\n- **Examples**:\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]`: any site with this species is occupied by a Ti atom.\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]`: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.\n - `[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]`: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.\n - `[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]`: any site with this species is occupied by a carbon isotope with mass 12.\n - `[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]`: any site with this species is occupied by a carbon isotope with mass 13.\n - `[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]`: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms.", "nullable": true, - "x-optimade-queryable": "optional", - "x-optimade-support": "should" + "x-optimade-support": "should", + "x-optimade-queryable": "optional" }, "species_at_sites": { "title": "Species At Sites", @@ -3687,8 +3687,8 @@ }, "description": "Name of the species at each site (where values for sites are specified with the same order of the property `cartesian_site_positions`).\nThe properties of the species are found in the property `species`.\n\n- **Type**: list of strings.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - MUST have length equal to the number of sites in the structure (first dimension of the list property `cartesian_site_positions`).\n - Each species name mentioned in the `species_at_sites` list MUST be described in the list property `species` (i.e. for each value in the `species_at_sites` list there MUST exist exactly one dictionary in the `species` list with the `name` attribute equal to the corresponding `species_at_sites` value).\n - Each site MUST be associated only to a single species.\n **Note**: However, species can represent mixtures of atoms, and multiple species MAY be defined for the same chemical element.\n This latter case is useful when different atoms of the same type need to be grouped or distinguished, for instance in simulation codes to assign different initial spin states.\n\n- **Examples**:\n - `[\"Ti\",\"O2\"]` indicates that the first site is hosting a species labeled `\"Ti\"` and the second a species labeled `\"O2\"`.\n - `[\"Ac\", \"Ac\", \"Ag\", \"Ir\"]` indicating the first two sites contains the `\"Ac\"` species, while the third and fourth sites contain the `\"Ag\"` and `\"Ir\"` species, respectively.", "nullable": true, - "x-optimade-queryable": "optional", - "x-optimade-support": "should" + "x-optimade-support": "should", + "x-optimade-queryable": "optional" }, "assemblies": { "title": "Assemblies", @@ -3697,8 +3697,8 @@ "$ref": "#/components/schemas/Assembly" }, "description": "A description of groups of sites that are statistically correlated.\n\n- **Type**: list of dictionary with keys:\n - `sites_in_groups`: list of list of integers (REQUIRED)\n - `group_probabilities`: list of floats (REQUIRED)\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - The property SHOULD be `null` for entries that have no partial occupancies.\n - If present, the correct flag MUST be set in the list `structure_features`.\n - Client implementations MUST check its presence (as its presence changes the interpretation of the structure).\n - If present, it MUST be a list of dictionaries, each of which represents an assembly and MUST have the following two keys:\n - **sites_in_groups**: Index of the sites (0-based) that belong to each group for each assembly.\n\n Example: `[[1], [2]]`: two groups, one with the second site, one with the third.\n Example: `[[1,2], [3]]`: one group with the second and third site, one with the fourth.\n\n - **group_probabilities**: Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\n It SHOULD sum to one.\n See below for examples of how to specify the probability of the occurrence of a vacancy.\n The possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.\n\n - If a site is not present in any group, it means that it is present with 100 % probability (as if no assembly was specified).\n - A site MUST NOT appear in more than one group.\n\n- **Examples** (for each entry of the assemblies list):\n - `{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}`: the first site and the second site never occur at the same time in the unit cell.\n Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.\n - `{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}`: the second and third site are either present together or not present; they form the first group of atoms for this assembly.\n The second group is formed by the fourth site.\n Sites of the first group (the second and the third) are never present at the same time as the fourth site.\n 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent).\n\n- **Notes**:\n - Assemblies are essential to represent, for instance, the situation where an atom can statistically occupy two different positions (sites).\n\n - By defining groups, it is possible to represent, e.g., the case where a functional molecule (and not just one atom) is either present or absent (or the case where it it is present in two conformations)\n\n - Considerations on virtual alloys and on vacancies: In the special case of a virtual alloy, these specifications allow two different, equivalent ways of specifying them.\n For instance, for a site at the origin with 30 % probability of being occupied by Si, 50 % probability of being occupied by Ge, and 20 % of being a vacancy, the following two representations are possible:\n\n - Using a single species:\n ```json\n {\n \"cartesian_site_positions\": [[0,0,0]],\n \"species_at_sites\": [\"SiGe-vac\"],\n \"species\": [\n {\n \"name\": \"SiGe-vac\",\n \"chemical_symbols\": [\"Si\", \"Ge\", \"vacancy\"],\n \"concentration\": [0.3, 0.5, 0.2]\n }\n ]\n // ...\n }\n ```\n\n - Using multiple species and the assemblies:\n ```json\n {\n \"cartesian_site_positions\": [ [0,0,0], [0,0,0], [0,0,0] ],\n \"species_at_sites\": [\"Si\", \"Ge\", \"vac\"],\n \"species\": [\n { \"name\": \"Si\", \"chemical_symbols\": [\"Si\"], \"concentration\": [1.0] },\n { \"name\": \"Ge\", \"chemical_symbols\": [\"Ge\"], \"concentration\": [1.0] },\n { \"name\": \"vac\", \"chemical_symbols\": [\"vacancy\"], \"concentration\": [1.0] }\n ],\n \"assemblies\": [\n {\n \"sites_in_groups\": [ [0], [1], [2] ],\n \"group_probabilities\": [0.3, 0.5, 0.2]\n }\n ]\n // ...\n }\n ```\n\n - It is up to the database provider to decide which representation to use, typically depending on the internal format in which the structure is stored.\n However, given a structure identified by a unique ID, the API implementation MUST always provide the same representation for it.\n\n - The probabilities of occurrence of different assemblies are uncorrelated.\n So, for instance in the following case with two assemblies:\n ```json\n {\n \"assemblies\": [\n {\n \"sites_in_groups\": [ [0], [1] ],\n \"group_probabilities\": [0.2, 0.8],\n },\n {\n \"sites_in_groups\": [ [2], [3] ],\n \"group_probabilities\": [0.3, 0.7]\n }\n ]\n }\n ```\n\n Site 0 is present with a probability of 20 % and site 1 with a probability of 80 %. These two sites are correlated (either site 0 or 1 is present). Similarly, site 2 is present with a probability of 30 % and site 3 with a probability of 70 %.\n These two sites are correlated (either site 2 or 3 is present).\n However, the presence or absence of sites 0 and 1 is not correlated with the presence or absence of sites 2 and 3 (in the specific example, the pair of sites (0, 2) can occur with 0.2*0.3 = 6 % probability; the pair (0, 3) with 0.2*0.7 = 14 % probability; the pair (1, 2) with 0.8*0.3 = 24 % probability; and the pair (1, 3) with 0.8*0.7 = 56 % probability).", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "structure_features": { "title": "Structure Features", @@ -3707,8 +3707,8 @@ "$ref": "#/components/schemas/StructureFeatures" }, "description": "A list of strings that flag which special features are used by the structure.\n\n- **Type**: list of strings\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property.\n Filters on the list MUST support all mandatory HAS-type queries.\n Filter operators for comparisons on the string components MUST support equality, support for other comparison operators are OPTIONAL.\n - MUST be an empty list if no special features are used.\n - MUST be sorted alphabetically.\n - If a special feature listed below is used, the list MUST contain the corresponding string.\n - If a special feature listed below is not used, the list MUST NOT contain the corresponding string.\n - **List of strings used to indicate special structure features**:\n - `disorder`: this flag MUST be present if any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element.\n - `implicit_atoms`: this flag MUST be present if the structure contains atoms that are not assigned to sites via the property `species_at_sites` (e.g., because their positions are unknown).\n When this flag is present, the properties related to the chemical formula will likely not match the type and count of atoms represented by the `species_at_sites`, `species` and `assemblies` properties.\n - `site_attachments`: this flag MUST be present if any one entry in the `species` list includes `attached` and `nattached`.\n - `assemblies`: this flag MUST be present if the property `assemblies` is present.\n\n- **Examples**: A structure having implicit atoms and using assemblies: `[\"assemblies\", \"implicit_atoms\"]`", - "x-optimade-queryable": "must", - "x-optimade-support": "must" + "x-optimade-support": "must", + "x-optimade-queryable": "must" } }, "description": "This class contains the Field for the attributes used to represent a structure, e.g. unit cell, atoms, positions." diff --git a/optimade/models/entries.py b/optimade/models/entries.py index f987bec9f..535cc7d98 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -129,7 +129,7 @@ class EntryMetadata(Meta): property_metadata: Dict = StrictField( None, - description="""A dictionary, where the keys are the names of the properties in the attributes field and the value is a dictionary containing the meta data for that property. + description="""A dictionary, where the keys are the names of the properties in the attributes field and the value is a dictionary containing the metadata for that property. Database-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes).""", ) From 6a20ef9ab5df11486be0b6884c4bfddff18864f9 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Wed, 2 Aug 2023 19:19:28 +0200 Subject: [PATCH 40/86] moved starts_with_supported_prefix and check_starts_with_supported_prefix functions to BaseresouceMapper. --- optimade/models/entries.py | 28 ++++++------------------- optimade/server/mappers/entries.py | 33 ++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 22 deletions(-) diff --git a/optimade/models/entries.py b/optimade/models/entries.py index 535cc7d98..85ed9d267 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -21,26 +21,6 @@ ) -def starts_with_supported_prefix(field): - from optimade.server.mappers.entries import BaseResourceMapper - - prefix = None - if field.startswith("_"): - prefix = field.split("_")[1] - if prefix in BaseResourceMapper.SUPPORTED_PREFIXES: - return True, prefix - return False, prefix - - -def _check_starts_with_supported_prefix(field, message=""): - prefixed, prefix = starts_with_supported_prefix(field) - if not prefixed: - raise ValueError( - f"The field {field} either has no prefix or the prefix {prefix} is not supported by this server." - + message - ) - - class TypedRelationship(Relationship): # This may be updated when moving to Python 3.8 @validator("data") @@ -195,6 +175,10 @@ class EntryResource(Resource): @root_validator(pre=True) def check_meta(cls, values): """Validator to check whether meta field has been formatted correctly.""" + from optimade.server.mappers.entries import ( + BaseResourceMapper, + ) + meta = values.get("meta") if meta: for field in meta: @@ -212,14 +196,14 @@ def check_meta(cls, values): subsubfields = property_metadata.get(subfield) if subsubfields: for subsubfield in subsubfields: - _check_starts_with_supported_prefix( + BaseResourceMapper.check_starts_with_supported_prefix( subsubfield, "Currently no OPTIMADE fields have been defined for the per attribute metadata, thus only database and domain specific fields are allowed", ) # At this point I am getting ahead of the specification. There is the intention to allow database specific fields(with the database specific prefixes) here in line with the JSON API specification, but it has not been decided yet how this case should be handled in the property definitions. else: - _check_starts_with_supported_prefix( + BaseResourceMapper.check_starts_with_supported_prefix( field, 'Currently no OPTIMADE fields other than "property_metadata" have been defined for the per entry "meta" field, thus only database and domain specific fields are allowed.', ) diff --git a/optimade/server/mappers/entries.py b/optimade/server/mappers/entries.py index 4c5895504..6853c94f3 100644 --- a/optimade/server/mappers/entries.py +++ b/optimade/server/mappers/entries.py @@ -383,3 +383,36 @@ def deserialize( return cls.ENTRY_RESOURCE_CLASS(**cls.map_back(results)) return [cls.ENTRY_RESOURCE_CLASS(**cls.map_back(doc)) for doc in results] + + @staticmethod + def starts_with_supported_prefix(field: str): + """Tests whether the supplied field has a field that is supported by this server. + Parameters: + field: The field/string for which it should be checked that it starts with a supported prefix. + + Returns: + A boolean which is true if the field/string starts with a supported prefix. + A string, containing the prefix if the field has a prefix otherwise it returns 'None'. + """ + + prefix = None + if field.startswith("_"): + prefix = field.split("_")[1] + if prefix in BaseResourceMapper.SUPPORTED_PREFIXES: + return True, prefix + return False, prefix + + @classmethod + def check_starts_with_supported_prefix(cls, field: str, message: str = ""): + """Raises a value error if the field does not start with a supported prefix. + Parameters: + field: The field/string for which it should be checked that it starts with a supported prefix. + message: An additional error message that will be appended to the default error message. + """ + + prefixed, prefix = cls.starts_with_supported_prefix(field) + if not prefixed: + raise ValueError( + f"The field {field} either has no prefix or the prefix {prefix} is not supported by this server." + + message + ) From e95c9b481aa31d664dace150b287b0628ffb2175 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Thu, 3 Aug 2023 11:10:16 +0200 Subject: [PATCH 41/86] Expanded docstring check_starts_with_supported_prefix --- optimade/server/mappers/entries.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/optimade/server/mappers/entries.py b/optimade/server/mappers/entries.py index 6853c94f3..20a3ddb36 100644 --- a/optimade/server/mappers/entries.py +++ b/optimade/server/mappers/entries.py @@ -408,6 +408,8 @@ def check_starts_with_supported_prefix(cls, field: str, message: str = ""): Parameters: field: The field/string for which it should be checked that it starts with a supported prefix. message: An additional error message that will be appended to the default error message. + Rerturns: + Raises a value error when the field has no valid prefix. """ prefixed, prefix = cls.starts_with_supported_prefix(field) From bcd79714d1c40fac706d34d2708b0754a2627ba1 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Thu, 3 Aug 2023 11:15:05 +0200 Subject: [PATCH 42/86] Expanded docstring check_starts_with_supported_prefix --- optimade/server/mappers/entries.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/optimade/server/mappers/entries.py b/optimade/server/mappers/entries.py index 20a3ddb36..d561da9ca 100644 --- a/optimade/server/mappers/entries.py +++ b/optimade/server/mappers/entries.py @@ -408,7 +408,7 @@ def check_starts_with_supported_prefix(cls, field: str, message: str = ""): Parameters: field: The field/string for which it should be checked that it starts with a supported prefix. message: An additional error message that will be appended to the default error message. - Rerturns: + Returns: Raises a value error when the field has no valid prefix. """ From 748baccc681cf515254aa8f4868d9b957607be7e Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Thu, 3 Aug 2023 11:36:10 +0200 Subject: [PATCH 43/86] Added return type for starts_with_supported_prefix and check_starts_with_supported_prefix. --- optimade/server/mappers/entries.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/optimade/server/mappers/entries.py b/optimade/server/mappers/entries.py index d561da9ca..bd566acea 100644 --- a/optimade/server/mappers/entries.py +++ b/optimade/server/mappers/entries.py @@ -1,6 +1,16 @@ import warnings from functools import lru_cache -from typing import Any, Dict, Iterable, List, Optional, Set, Tuple, Type, Union +from typing import ( + Any, + Dict, + Iterable, + List, + Optional, + Set, + Tuple, + Type, + Union, +) from optimade.models.entries import EntryResource @@ -385,7 +395,7 @@ def deserialize( return [cls.ENTRY_RESOURCE_CLASS(**cls.map_back(doc)) for doc in results] @staticmethod - def starts_with_supported_prefix(field: str): + def starts_with_supported_prefix(field: str) -> Tuple[bool, str | None]: """Tests whether the supplied field has a field that is supported by this server. Parameters: field: The field/string for which it should be checked that it starts with a supported prefix. @@ -403,7 +413,7 @@ def starts_with_supported_prefix(field: str): return False, prefix @classmethod - def check_starts_with_supported_prefix(cls, field: str, message: str = ""): + def check_starts_with_supported_prefix(cls, field: str, message: str = "") -> None: """Raises a value error if the field does not start with a supported prefix. Parameters: field: The field/string for which it should be checked that it starts with a supported prefix. From 8430ddd27a1f5ca650db1dcc64e04c9b95eb37fe Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Thu, 3 Aug 2023 11:46:49 +0200 Subject: [PATCH 44/86] Added return type for starts_with_supported_prefix and check_starts_with_supported_prefix. --- optimade/server/mappers/entries.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/optimade/server/mappers/entries.py b/optimade/server/mappers/entries.py index bd566acea..d46566386 100644 --- a/optimade/server/mappers/entries.py +++ b/optimade/server/mappers/entries.py @@ -395,7 +395,7 @@ def deserialize( return [cls.ENTRY_RESOURCE_CLASS(**cls.map_back(doc)) for doc in results] @staticmethod - def starts_with_supported_prefix(field: str) -> Tuple[bool, str | None]: + def starts_with_supported_prefix(field: str) -> Tuple[bool, Union[str, None]]: """Tests whether the supplied field has a field that is supported by this server. Parameters: field: The field/string for which it should be checked that it starts with a supported prefix. From ac26f090d82f850a3a7324330e71759d323232e8 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Thu, 3 Aug 2023 13:56:44 +0200 Subject: [PATCH 45/86] Adjusted validators baseed on suggestion Matthew. Co-authored-by: ml-evs ml-evs@users.noreply.github.com --- optimade/models/entries.py | 65 +++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/optimade/models/entries.py b/optimade/models/entries.py index 85ed9d267..b9e7802a8 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -113,6 +113,23 @@ class EntryMetadata(Meta): Database-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes).""", ) + @validator("property_metadata") + def check_property_metadata_subfields(cls, property_metadata): + from optimade.server.mappers.entries import ( + BaseResourceMapper, + ) + + if property_metadata: + for field in property_metadata: + subfields = property_metadata.get(field) + if subfields: + for subsubfield in subfields: + BaseResourceMapper.check_starts_with_supported_prefix( + subsubfield, + "Currently no OPTIMADE fields have been defined for the per attribute metadata, thus only database and domain specific fields are allowed", + ) + return property_metadata + class EntryResource(Resource): """The base model for an entry resource.""" @@ -180,34 +197,30 @@ def check_meta(cls, values): ) meta = values.get("meta") - if meta: - for field in meta: - if field == "property_metadata": - # check that all the fields under property metadata are in attributes - attributes = values.get("attributes", {}) - property_metadata = meta.get("property_metadata") - if property_metadata: - for subfield in property_metadata: - if subfield not in attributes: - raise ValueError( - f"The keys under the field `property_metadata` need to match with the field names in attributes. The field {subfield} is however not in attributes." - ) - # check that the fields under subfield are starting with prefix - subsubfields = property_metadata.get(subfield) - if subsubfields: - for subsubfield in subsubfields: - BaseResourceMapper.check_starts_with_supported_prefix( - subsubfield, - "Currently no OPTIMADE fields have been defined for the per attribute metadata, thus only database and domain specific fields are allowed", - ) - - # At this point I am getting ahead of the specification. There is the intention to allow database specific fields(with the database specific prefixes) here in line with the JSON API specification, but it has not been decided yet how this case should be handled in the property definitions. - else: - BaseResourceMapper.check_starts_with_supported_prefix( - field, - 'Currently no OPTIMADE fields other than "property_metadata" have been defined for the per entry "meta" field, thus only database and domain specific fields are allowed.', + if not meta: + return values + + if property_metadata := meta.pop("property_metadata", None): + # check that all the fields under property metadata are in attributes + attributes = values.get("attributes", {}) + for subfield in property_metadata: + if subfield not in attributes: + raise ValueError( + f"The keys under the field `property_metadata` need to match with the field names in attributes. The field {subfield} is however not in attributes." ) + for ( + field + ) in ( + meta + ): # At this point I am getting ahead of the specification. There is the intention to allow database specific fields(with the database specific prefixes) here in line with the JSON API specification, but it has not been decided yet how this case should be handled in the property definitions. + BaseResourceMapper.check_starts_with_supported_prefix( + field, + 'Currently no OPTIMADE fields other than "property_metadata" have been defined for the per entry "meta" field, thus only database and domain specific fields are allowed.', + ) + + values["meta"]["property_metadata"] = property_metadata + return values From e1c4901373f4c4923ff063ce5a39600d562c321c Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Thu, 3 Aug 2023 15:48:33 +0200 Subject: [PATCH 46/86] small corrections validators. --- optimade/models/entries.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/optimade/models/entries.py b/optimade/models/entries.py index b9e7802a8..d0bffac77 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -121,11 +121,10 @@ def check_property_metadata_subfields(cls, property_metadata): if property_metadata: for field in property_metadata: - subfields = property_metadata.get(field) - if subfields: - for subsubfield in subfields: + if attribute_meta_dict := property_metadata.get(field): + for subfield in attribute_meta_dict: BaseResourceMapper.check_starts_with_supported_prefix( - subsubfield, + subfield, "Currently no OPTIMADE fields have been defined for the per attribute metadata, thus only database and domain specific fields are allowed", ) return property_metadata @@ -209,11 +208,8 @@ def check_meta(cls, values): f"The keys under the field `property_metadata` need to match with the field names in attributes. The field {subfield} is however not in attributes." ) - for ( - field - ) in ( - meta - ): # At this point I am getting ahead of the specification. There is the intention to allow database specific fields(with the database specific prefixes) here in line with the JSON API specification, but it has not been decided yet how this case should be handled in the property definitions. + # At this point I am getting ahead of the specification. There is the intention to allow database specific fields(with the database specific prefixes) here in line with the JSON API specification, but it has not been decided yet how this case should be handled in the property definitions. + for field in meta: BaseResourceMapper.check_starts_with_supported_prefix( field, 'Currently no OPTIMADE fields other than "property_metadata" have been defined for the per entry "meta" field, thus only database and domain specific fields are allowed.', From 7dab3a7a04b050f47809555be4aa885f7f0b9f78 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Thu, 3 Aug 2023 16:47:15 +0200 Subject: [PATCH 47/86] small correction in removing unrequested metadata. --- optimade/server/routers/utils.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/optimade/server/routers/utils.py b/optimade/server/routers/utils.py index 96a9d42cd..72e92fcf7 100644 --- a/optimade/server/routers/utils.py +++ b/optimade/server/routers/utils.py @@ -125,12 +125,12 @@ def handle_response_fields( for field in exclude_fields: if field in new_entry["attributes"]: del new_entry["attributes"][field] - if new_entry.get("meta") and new_entry.get("meta").get( # type: ignore[union-attr] - "property_metadata" + if new_entry.get("meta") and ( + property_meta_data_fields := new_entry.get("meta").get( # type: ignore[union-attr] + "property_metadata" + ) ): - if field in new_entry.get("meta", {}).get( - "property_metadata", {} - ): # type: ignore[union-attr] + if field in property_meta_data_fields: del new_entry["meta"]["property_metadata"][field] # Include missing fields that were requested in `response_fields` From da8a026a9bfec996fd753a57ecdccf862297e9f9 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Thu, 3 Aug 2023 17:01:03 +0200 Subject: [PATCH 48/86] Added spaces before values 'exmpl_originates_from _project'. --- optimade/server/data/test_structures.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/optimade/server/data/test_structures.json b/optimade/server/data/test_structures.json index 3d147c5ba..c189ad981 100644 --- a/optimade/server/data/test_structures.json +++ b/optimade/server/data/test_structures.json @@ -6,7 +6,7 @@ "meta": { "property_metadata": { "elements_ratios": { - "_exmpl_originates_from_project":"Pure Metals" + "_exmpl_originates_from_project": "Pure Metals" } } }, @@ -90,7 +90,7 @@ "meta": { "property_metadata": { "elements_ratios": { - "_exmpl_originates_from_project":"Actinides_Alloys" + "_exmpl_originates_from_project": "Actinides_Alloys" } } }, @@ -214,7 +214,7 @@ "meta": { "property_metadata": { "elements_ratios": { - "_exmpl_originates_from_project":"Actinides_Alloys" + "_exmpl_originates_from_project": "Actinides_Alloys" } } }, @@ -347,7 +347,7 @@ "meta": { "property_metadata": { "elements_ratios": { - "_exmpl_originates_from_project":"Actinides_Alloys" + "_exmpl_originates_from_project": "Actinides_Alloys" } } }, @@ -444,7 +444,7 @@ "meta": { "property_metadata": { "elements_ratios": { - "_exmpl_originates_from_project":null + "_exmpl_originates_from_project": null } } }, @@ -682,7 +682,7 @@ "meta": { "property_metadata": { "elements_ratios": { - "_exmpl_originates_from_project":"Pure Metals" + "_exmpl_originates_from_project": "Pure Metals" } } }, From 92da84e3d1d317cdf556ef4ba34746e329b7a43e Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Fri, 4 Aug 2023 17:19:02 +0200 Subject: [PATCH 49/86] First draft version of model of jsonlines response. --- docs/api_reference/JSONLines.md | 3 + docs/api_reference/models/jsonlines.md | 5 + optimade/JSONLines.py | 0 optimade/models/jsonlines.py | 124 +++++++++++++++++++++++++ 4 files changed, 132 insertions(+) create mode 100644 docs/api_reference/JSONLines.md create mode 100644 docs/api_reference/models/jsonlines.md create mode 100644 optimade/JSONLines.py create mode 100644 optimade/models/jsonlines.py diff --git a/docs/api_reference/JSONLines.md b/docs/api_reference/JSONLines.md new file mode 100644 index 000000000..63c2638e1 --- /dev/null +++ b/docs/api_reference/JSONLines.md @@ -0,0 +1,3 @@ +# JSONLines + +::: optimade.JSONLines diff --git a/docs/api_reference/models/jsonlines.md b/docs/api_reference/models/jsonlines.md new file mode 100644 index 000000000..e2d6628c7 --- /dev/null +++ b/docs/api_reference/models/jsonlines.md @@ -0,0 +1,5 @@ +# jsonlines + +::: optimade.models.jsonlines + options: + show_if_no_docstring: true diff --git a/optimade/JSONLines.py b/optimade/JSONLines.py new file mode 100644 index 000000000..e69de29bb diff --git a/optimade/models/jsonlines.py b/optimade/models/jsonlines.py new file mode 100644 index 000000000..c96e27b74 --- /dev/null +++ b/optimade/models/jsonlines.py @@ -0,0 +1,124 @@ +from typing import Optional + +from pydantic import BaseModel # pylint: disable=no-name-in-module + +from optimade.models.utils import OptimadeField, SupportLevel + + +class JsonLinesHeader(BaseModel): + optimade_partial_data: dict = OptimadeField( + ..., + description="""An object identifying the response as being on OPTIMADE partial data format. +It MUST contain the following key: +"version": String. Specifies the minor version of the partial data format used. The string MUST be of the format "MAJOR.MINOR", referring to the version of the OPTIMADE standard that describes the format. The version number string MUST NOT be prefixed by, e.g., "v". In implementations of the present version of the standard, the value MUST be exactly 1.2. A client MUST NOT expect to be able to parse the version value if the field is not a string of the format MAJOR.MINOR or if the MAJOR version number is unrecognized. + +- **Type**: Dictionary. + +- **Requirements/Conventions**: + - **Support**: MUST be supported by all implementations, MUST NOT be `null`. + +- **Examples**: + - `""optimade-partial-data": {"version": "1.2.0"}"`""", + support=SupportLevel.MUST, + ) + layout: str = OptimadeField( + ..., + description="""A string either equal to "dense" or "sparse" to indicate whether the returned format uses a dense or sparse layout. + +- **Type**: string. + +- **Requirements/Conventions**: + - **Support**: MUST be supported by all implementations, MUST NOT be `null`. + +- **Examples**: + - `"dense"` + - `"sparse"`""", + support=SupportLevel.MUST, + ) + returned_ranges: Optional[list[dict]] = OptimadeField( + None, + description="""Array of Objects. For dense layout, and sparse layout of one dimensional list properties, the array contains a single element which is a slice object representing the range of data present in the response. In the specific case of a hierarchy of list properties represented as a sparse multi-dimensional array, if the field "returned_ranges" is given, it MUST contain one slice object per dimension of the multi-dimensional array, representing slices for each dimension that cover the data given in the response. + +- **Type**: List of Dictionaries. + +- **Requirements/Conventions**: + - **Support**: SHOULD be supported by all implementations, SHOULD NOT be `null`. + +- **Examples**: + - `""returned_ranges": [{"start": 10, "stop": 20, "step": 2}]"` + - `""returned_ranges": [{"start": 10, "stop": 20, "step": 2}, {"start": 0, "stop": 9, "step": 1}]"`""", + support=SupportLevel.SHOULD, + ) + property_name: Optional[str] = OptimadeField( + None, + description="""The name of the property being provided. + +- **Type**: string. + +- **Requirements/Conventions**: + - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.. + +- **Examples**: + - `"cartesian_site_positions"`""", + support=SupportLevel.OPTIONAL, + ) + entry: Optional[dict] = OptimadeField( + None, + description=""" Object. An object that MUST have the following two keys: + + "id": String. The id of the entry of the property being provided. + "type": String. The type of the entry of the property being provided. + + +- **Type**: string. + +- **Requirements/Conventions**: + - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.. + +- **Examples**: + - `"{"id": "mpf_72", "type": structure"}`""", + support=SupportLevel.OPTIONAL, + ) + has_references: Optional[bool] = OptimadeField( + None, + description=""" An optional boolean to indicate whether any of the data lines in the response contains a reference marker. A value of false means that the client does not have to process any of the lines to detect reference markers, which may speed up the parsing. + +- **Type**: boolean. + +- **Requirements/Conventions**: + - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.. + +- **Examples**: + - `false`""", + support=SupportLevel.OPTIONAL, + ) + item_schema: Optional[dict] = OptimadeField( + None, + description="""An object that represents a JSON Schema that validates the data lines of the response. The format SHOULD be the relevant partial extract of a valid property definition as described in Property Definitions. If a schema is provided, it MUST be a valid JSON schema using the same version of JSON schema as described in that section. +- **Type**: dictionary. + +- **Requirements/Conventions**: + - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.. +""", + support=SupportLevel.OPTIONAL, + ) + + links: Optional[dict] = OptimadeField( + None, + description=""" An object to provide relevant links for the property being provided. It MAY contain the following key: + + "base_url": String. The base URL of the implementation serving the database to which this property belongs. + "item_describedby": String. A URL to an external JSON Schema that validates the data lines of the response. The format and requirements on this schema are the same as for the inline schema field item_schema. + +- **Type**: dictionary. + +- **Requirements/Conventions**: + - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.. +""", + support=SupportLevel.OPTIONAL, + ) + + +class JsonLinesResponse(BaseModel): + header: JsonLinesHeader + data: list From f39c734b19838ad1bf211d92c6ebf9f19c24d2de Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Tue, 8 Aug 2023 18:10:11 +0200 Subject: [PATCH 50/86] renamed files to partialdata. --- .../models/{jsonlines.md => partialdata.md} | 4 ++-- optimade/JSONLines.py | 8 +++++++ .../models/{jsonlines.py => partialdata.py} | 12 +++++------ tests/models/test_partialdata.py | 21 +++++++++++++++++++ 4 files changed, 37 insertions(+), 8 deletions(-) rename docs/api_reference/models/{jsonlines.md => partialdata.md} (50%) rename optimade/models/{jsonlines.py => partialdata.py} (95%) create mode 100644 tests/models/test_partialdata.py diff --git a/docs/api_reference/models/jsonlines.md b/docs/api_reference/models/partialdata.md similarity index 50% rename from docs/api_reference/models/jsonlines.md rename to docs/api_reference/models/partialdata.md index e2d6628c7..0665311ef 100644 --- a/docs/api_reference/models/jsonlines.md +++ b/docs/api_reference/models/partialdata.md @@ -1,5 +1,5 @@ -# jsonlines +# partialdata -::: optimade.models.jsonlines +::: optimade.models.partialdata options: show_if_no_docstring: true diff --git a/optimade/JSONLines.py b/optimade/JSONLines.py index e69de29bb..62d2a2e44 100644 --- a/optimade/JSONLines.py +++ b/optimade/JSONLines.py @@ -0,0 +1,8 @@ +from typing import Union + +from optimade.models.partialdata import PartialDataResponse + + +def to_JSONL(input: Union[list[dict], PartialDataResponse]): + """This function convert a list of dictionaries to the JSONL format which can be send back in an OPTIMADE partial data response""" + pass diff --git a/optimade/models/jsonlines.py b/optimade/models/partialdata.py similarity index 95% rename from optimade/models/jsonlines.py rename to optimade/models/partialdata.py index c96e27b74..1af84fedd 100644 --- a/optimade/models/jsonlines.py +++ b/optimade/models/partialdata.py @@ -1,11 +1,11 @@ -from typing import Optional +from typing import Literal, Optional -from pydantic import BaseModel # pylint: disable=no-name-in-module +from pydantic import BaseModel from optimade.models.utils import OptimadeField, SupportLevel -class JsonLinesHeader(BaseModel): +class PartialDataHeader(BaseModel): optimade_partial_data: dict = OptimadeField( ..., description="""An object identifying the response as being on OPTIMADE partial data format. @@ -21,7 +21,7 @@ class JsonLinesHeader(BaseModel): - `""optimade-partial-data": {"version": "1.2.0"}"`""", support=SupportLevel.MUST, ) - layout: str = OptimadeField( + layout: Literal["dense", "sparse"] = OptimadeField( ..., description="""A string either equal to "dense" or "sparse" to indicate whether the returned format uses a dense or sparse layout. @@ -119,6 +119,6 @@ class JsonLinesHeader(BaseModel): ) -class JsonLinesResponse(BaseModel): - header: JsonLinesHeader +class PartialDataResponse(BaseModel): + header: PartialDataHeader data: list diff --git a/tests/models/test_partialdata.py b/tests/models/test_partialdata.py new file mode 100644 index 000000000..3fcf4b194 --- /dev/null +++ b/tests/models/test_partialdata.py @@ -0,0 +1,21 @@ +from optimade.models.partialdata import PartialDataResponse + + +def test_object_generation(): + test_object = { + "header": { + "optimade_partial_data": {"version": "1.2.0"}, + "layout": "dense", + "returned_ranges": [{"start": 10, "stop": 20, "step": 2}], + }, + "data": [ + 123, + 345, + -12.6, + ["PARTIAL-DATA-NEXT", ["https://example.db.org/value4"]], + ], + } + + PartialDataResponse(**test_object) + + pass From 8c889c6152c79cb55dacd2ee94da516b5e864e78 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Thu, 10 Aug 2023 11:47:51 +0200 Subject: [PATCH 51/86] First draft version of model of jsonlines response. --- docs/api_reference/JSONLines.md | 3 -- docs/api_reference/adapters/jsonl.md | 3 ++ optimade/JSONLines.py | 8 ------ optimade/adapters/jsonl.py | 42 ++++++++++++++++++++++++++++ tests/adapters/test_jsonl.py | 19 +++++++++++++ 5 files changed, 64 insertions(+), 11 deletions(-) delete mode 100644 docs/api_reference/JSONLines.md create mode 100644 docs/api_reference/adapters/jsonl.md delete mode 100644 optimade/JSONLines.py create mode 100644 optimade/adapters/jsonl.py create mode 100644 tests/adapters/test_jsonl.py diff --git a/docs/api_reference/JSONLines.md b/docs/api_reference/JSONLines.md deleted file mode 100644 index 63c2638e1..000000000 --- a/docs/api_reference/JSONLines.md +++ /dev/null @@ -1,3 +0,0 @@ -# JSONLines - -::: optimade.JSONLines diff --git a/docs/api_reference/adapters/jsonl.md b/docs/api_reference/adapters/jsonl.md new file mode 100644 index 000000000..6e9eeb43e --- /dev/null +++ b/docs/api_reference/adapters/jsonl.md @@ -0,0 +1,3 @@ +# jsonl + +::: optimade.adapters.jsonl diff --git a/optimade/JSONLines.py b/optimade/JSONLines.py deleted file mode 100644 index 62d2a2e44..000000000 --- a/optimade/JSONLines.py +++ /dev/null @@ -1,8 +0,0 @@ -from typing import Union - -from optimade.models.partialdata import PartialDataResponse - - -def to_JSONL(input: Union[list[dict], PartialDataResponse]): - """This function convert a list of dictionaries to the JSONL format which can be send back in an OPTIMADE partial data response""" - pass diff --git a/optimade/adapters/jsonl.py b/optimade/adapters/jsonl.py new file mode 100644 index 000000000..60979192c --- /dev/null +++ b/optimade/adapters/jsonl.py @@ -0,0 +1,42 @@ +from io import BufferedReader, BytesIO +from pathlib import Path +from typing import Union + +from jsonlines import Reader, Writer + +from optimade.models.partialdata import PartialDataResponse + + +def to_jsonl(input_data: Union[list[dict], PartialDataResponse]) -> bytes: + """This function convert a list of dictionaries to the JSONL format which can be sent back in an OPTIMADE partial data response""" + temp_file = BytesIO() + writer = Writer(temp_file) + if isinstance(input_data, PartialDataResponse): + writer.write(input_data.header) + input_data = input_data.data + if isinstance(input_data, list): + writer.write_all(input_data) + else: + writer.write(input_data) + writer.close() + file_content = temp_file.getvalue() + temp_file.close() + return file_content + + +def from_jsonl( + jsonl_input: Union[Path, str, bytes] +) -> Union[list, PartialDataResponse]: + if isinstance(jsonl_input, (Path, str)): + fp: Union[BytesIO, BufferedReader] = open(jsonl_input, "rb") + else: + fp = BytesIO(jsonl_input) + decoded = [] + reader = Reader(fp) + for obj in reader: + decoded.append( + obj + ) # Appending is slow, so it would be better to use a more efficient method + reader.close() + fp.close() + return decoded diff --git a/tests/adapters/test_jsonl.py b/tests/adapters/test_jsonl.py new file mode 100644 index 000000000..2bda96c2c --- /dev/null +++ b/tests/adapters/test_jsonl.py @@ -0,0 +1,19 @@ +from optimade.adapters.jsonl import from_jsonl, to_jsonl + + +def test_to_jsonl(): + test_object = [ + { + "optimade_partial_data": {"version": "1.2.0"}, + "layout": "dense", + "returned_ranges": [{"start": 10, "stop": 20, "step": 2}], + }, + 1243, + 345, + -12.6, + ["PARTIAL-DATA-NEXT", ["https://example.db.org/value4"]], + ] + file_content = to_jsonl(test_object) + reprocessed_file = from_jsonl(file_content) + assert test_object == reprocessed_file + pass From 75669c81f98eeeddf2a5208a0debabd383d76592 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Mon, 14 Aug 2023 14:35:56 +0200 Subject: [PATCH 52/86] added metadata fields for partial data protocal to entry models. --- openapi/index_openapi.json | 36 +- openapi/openapi.json | 156 +++++---- optimade/models/entries.py | 47 ++- optimade/server/data/test_structures.json | 383 +--------------------- tests/adapters/test_jsonl.py | 18 +- tests/adapters/testdata.jsonl | 5 + 6 files changed, 191 insertions(+), 454 deletions(-) create mode 100644 tests/adapters/testdata.jsonl diff --git a/openapi/index_openapi.json b/openapi/index_openapi.json index 79ad66476..fae87ce9b 100644 --- a/openapi/index_openapi.json +++ b/openapi/index_openapi.json @@ -475,6 +475,17 @@ "title": "Property Metadata", "type": "object", "description": "A dictionary, where the keys are the names of the properties in the attributes field and the value is a dictionary containing the metadata for that property.\nDatabase-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes)." + }, + "partial_data_links": { + "title": "Partial Data Links", + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PartialDataLink" + } + }, + "description": "A dictionary, where the keys are the names of the properties in the attributes field for which the value is too large to be shared by default.\n For each property one or more links are provided from which the value of the attribute can be retrieved." } }, "description": "Contains the metadata for the attributes of an entry" @@ -585,8 +596,8 @@ "type": "string", "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "format": "date-time", - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" } }, "description": "Contains key-value pairs representing the entry's properties." @@ -1405,6 +1416,27 @@ }, "description": "detail MUST be present" }, + "PartialDataLink": { + "title": "PartialDataLink", + "required": [ + "link", + "format" + ], + "type": "object", + "properties": { + "link": { + "title": "Link", + "maxLength": 65536, + "minLength": 1, + "type": "string", + "format": "uri" + }, + "format": { + "title": "Format", + "type": "string" + } + } + }, "Provider": { "title": "Provider", "required": [ diff --git a/openapi/openapi.json b/openapi/openapi.json index 9f1ecd33e..e2523f4e4 100644 --- a/openapi/openapi.json +++ b/openapi/openapi.json @@ -1341,8 +1341,8 @@ } }, "description": "Index of the sites (0-based) that belong to each group for each assembly.\n\n- **Examples**:\n - `[[1], [2]]`: two groups, one with the second site, one with the third.\n - `[[1,2], [3]]`: one group with the second and third site, one with the fourth.", - "x-optimade-support": "must", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "must" }, "group_probabilities": { "title": "Group Probabilities", @@ -1351,8 +1351,8 @@ "type": "number" }, "description": "Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\nIt SHOULD sum to one.\nSee below for examples of how to specify the probability of the occurrence of a vacancy.\nThe possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.", - "x-optimade-support": "must", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "must" } }, "description": "A description of groups of sites that are statistically correlated.\n\n- **Examples** (for each entry of the assemblies list):\n - `{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}`: the first site and the second site never occur at the same time in the unit cell.\n Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.\n - `{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}`: the second and third site are either present together or not present; they form the first group of atoms for this assembly.\n The second group is formed by the fourth site. Sites of the first group (the second and the third) are never present at the same time as the fourth site.\n 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent)." @@ -1725,6 +1725,17 @@ "title": "Property Metadata", "type": "object", "description": "A dictionary, where the keys are the names of the properties in the attributes field and the value is a dictionary containing the metadata for that property.\nDatabase-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes)." + }, + "partial_data_links": { + "title": "Partial Data Links", + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PartialDataLink" + } + }, + "description": "A dictionary, where the keys are the names of the properties in the attributes field for which the value is too large to be shared by default.\n For each property one or more links are provided from which the value of the attribute can be retrieved." } }, "description": "Contains the metadata for the attributes of an entry" @@ -1835,8 +1846,8 @@ "type": "string", "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "format": "date-time", - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" } }, "description": "Contains key-value pairs representing the entry's properties." @@ -2514,6 +2525,27 @@ }, "description": "detail MUST be present" }, + "PartialDataLink": { + "title": "PartialDataLink", + "required": [ + "link", + "format" + ], + "type": "object", + "properties": { + "link": { + "title": "Link", + "maxLength": 65536, + "minLength": 1, + "type": "string", + "format": "uri" + }, + "format": { + "title": "Format", + "type": "string" + } + } + }, "Periodicity": { "title": "Periodicity", "enum": [ @@ -2660,8 +2692,8 @@ "type": "string", "description": "The name of the type of an entry.\n- **Type**: string.\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type and ID MUST be returned in response to a request for `//` under the versioned base URL.\n- **Example**: `\"structures\"`", "default": "references", - "x-optimade-support": "must", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "must" }, "links": { "title": "Links", @@ -2715,8 +2747,8 @@ "type": "string", "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "format": "date-time", - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" }, "authors": { "title": "Authors", @@ -2725,8 +2757,8 @@ "$ref": "#/components/schemas/Person" }, "description": "List of person objects containing the authors of the reference.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "editors": { "title": "Editors", @@ -2735,8 +2767,8 @@ "$ref": "#/components/schemas/Person" }, "description": "List of person objects containing the editors of the reference.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "doi": { "title": "Doi", @@ -2752,8 +2784,8 @@ "type": "string", "description": "The URL of the reference.", "format": "uri", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "address": { "title": "Address", @@ -3339,8 +3371,8 @@ "type": "string" }, "description": "MUST be a list of strings of all chemical elements composing this species. Each item of the list MUST be one of the following:\n\n- a valid chemical-element symbol, or\n- the special value `\"X\"` to represent a non-chemical element, or\n- the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\n\nIf any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.", - "x-optimade-support": "must", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "must" }, "concentration": { "title": "Concentration", @@ -3349,8 +3381,8 @@ "type": "number" }, "description": "MUST be a list of floats, with same length as `chemical_symbols`. The numbers represent the relative concentration of the corresponding chemical symbol in this species. The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\n\n- Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\n- Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\n\nNote that concentrations are uncorrelated between different site (even of the same species).", - "x-optimade-support": "must", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "must" }, "mass": { "title": "Mass", @@ -3359,9 +3391,9 @@ "type": "number" }, "description": "If present MUST be a list of floats expressed in a.m.u.\nElements denoting vacancies MUST have masses equal to 0.", + "x-optimade-queryable": "optional", "x-optimade-unit": "a.m.u.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-support": "optional" }, "original_name": { "title": "Original Name", @@ -3377,8 +3409,8 @@ "type": "string" }, "description": "If provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "nattached": { "title": "Nattached", @@ -3387,8 +3419,8 @@ "type": "integer" }, "description": "If provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the :field:`attached` key.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" } }, "description": "A list describing the species of the sites of this structure.\n\nSpecies can represent pure chemical elements, virtual-crystal atoms representing a\nstatistical occupation of a given site by multiple chemical elements, and/or a\nlocation to which there are attached atoms, i.e., atoms whose precise location are\nunknown beyond that they are attached to that position (frequently used to indicate\nhydrogen atoms attached to another element, e.g., a carbon with three attached\nhydrogens might represent a methyl group, -CH3).\n\n- **Examples**:\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]`: any site with this species is occupied by a Ti atom.\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]`: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.\n - `[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]`: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.\n - `[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]`: any site with this species is occupied by a carbon isotope with mass 12.\n - `[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]`: any site with this species is occupied by a carbon isotope with mass 13.\n - `[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]`: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms." @@ -3466,8 +3498,8 @@ "type": "string", "description": "The name of the type of an entry.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type `` and ID `` MUST be returned in response to a request for `//` under the versioned base URL.\n\n- **Examples**:\n - `\"structures\"`", "default": "structures", - "x-optimade-support": "must", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "must" }, "links": { "title": "Links", @@ -3536,8 +3568,8 @@ "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "format": "date-time", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" }, "elements": { "title": "Elements", @@ -3547,16 +3579,16 @@ }, "description": "The chemical symbols of the different elements present in the structure.\n\n- **Type**: list of strings.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The strings are the chemical symbols, i.e., either a single uppercase letter or an uppercase letter followed by a number of lowercase letters.\n - The order MUST be alphabetical.\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements_ratios`, if the latter is provided.\n - Note: This property SHOULD NOT contain the string \"X\" to indicate non-chemical elements or \"vacancy\" to indicate vacancies (in contrast to the field `chemical_symbols` for the `species` property).\n\n- **Examples**:\n - `[\"Si\"]`\n - `[\"Al\",\"O\",\"Si\"]`\n\n- **Query examples**:\n - A filter that matches all records of structures that contain Si, Al **and** O, and possibly other elements: `elements HAS ALL \"Si\", \"Al\", \"O\"`.\n - To match structures with exactly these three elements, use `elements HAS ALL \"Si\", \"Al\", \"O\" AND elements LENGTH 3`.\n - Note: length queries on this property can be equivalently formulated by filtering on the `nelements`_ property directly.", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" }, "nelements": { "title": "Nelements", "type": "integer", "description": "Number of different elements in the structure as an integer.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - MUST be equal to the lengths of the list properties `elements` and `elements_ratios`, if they are provided.\n\n- **Examples**:\n - `3`\n\n- **Querying**:\n - Note: queries on this property can equivalently be formulated using `elements LENGTH`.\n - A filter that matches structures that have exactly 4 elements: `nelements=4`.\n - A filter that matches structures that have between 2 and 7 elements: `nelements>=2 AND nelements<=7`.", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" }, "elements_ratios": { "title": "Elements Ratios", @@ -3566,16 +3598,16 @@ }, "description": "Relative proportions of different elements in the structure.\n\n- **Type**: list of floats\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - Composed by the proportions of elements in the structure as a list of floating point numbers.\n - The sum of the numbers MUST be 1.0 (within floating point accuracy)\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements`, if the latter is provided.\n\n- **Examples**:\n - `[1.0]`\n - `[0.3333333333333333, 0.2222222222222222, 0.4444444444444444]`\n\n- **Query examples**:\n - Note: Useful filters can be formulated using the set operator syntax for correlated values.\n However, since the values are floating point values, the use of equality comparisons is generally inadvisable.\n - OPTIONAL: a filter that matches structures where approximately 1/3 of the atoms in the structure are the element Al is: `elements:elements_ratios HAS ALL \"Al\":>0.3333, \"Al\":<0.3334`.", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" }, "chemical_formula_descriptive": { "title": "Chemical Formula Descriptive", "type": "string", "description": "The chemical formula for a structure as a string in a form chosen by the API implementation.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The chemical formula is given as a string consisting of properly capitalized element symbols followed by integers or decimal numbers, balanced parentheses, square, and curly brackets `(`,`)`, `[`,`]`, `{`, `}`, commas, the `+`, `-`, `:` and `=` symbols. The parentheses are allowed to be followed by a number. Spaces are allowed anywhere except within chemical symbols. The order of elements and any groupings indicated by parentheses or brackets are chosen freely by the API implementation.\n - The string SHOULD be arithmetically consistent with the element ratios in the `chemical_formula_reduced` property.\n - It is RECOMMENDED, but not mandatory, that symbols, parentheses and brackets, if used, are used with the meanings prescribed by [IUPAC's Nomenclature of Organic Chemistry](https://www.qmul.ac.uk/sbcs/iupac/bibliog/blue.html).\n\n- **Examples**:\n - `\"(H2O)2 Na\"`\n - `\"NaCl\"`\n - `\"CaCO3\"`\n - `\"CCaO3\"`\n - `\"(CH3)3N+ - [CH2]2-OH = Me3N+ - CH2 - CH2OH\"`\n\n- **Query examples**:\n - Note: the free-form nature of this property is likely to make queries on it across different databases inconsistent.\n - A filter that matches an exactly given formula: `chemical_formula_descriptive=\"(H2O)2 Na\"`.\n - A filter that does a partial match: `chemical_formula_descriptive CONTAINS \"H2O\"`.", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" }, "chemical_formula_reduced": { "title": "Chemical Formula Reduced", @@ -3583,16 +3615,16 @@ "type": "string", "description": "The reduced chemical formula for a structure as a string with element symbols and integer chemical proportion numbers.\nThe proportion number MUST be omitted if it is 1.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property.\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\n Intricate queries on formula components are instead suggested to be formulated using set-type filter operators on the multi valued `elements` and `elements_ratios` properties.\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\n - Elements MUST be placed in alphabetical order, followed by their integer chemical proportion number.\n - For structures with no partial occupation, the chemical proportion numbers are the smallest integers for which the chemical proportion is exactly correct.\n - For structures with partial occupation, the chemical proportion numbers are integers that within reasonable approximation indicate the correct chemical proportions. The precise details of how to perform the rounding is chosen by the API implementation.\n - No spaces or separators are allowed.\n\n- **Examples**:\n - `\"H2NaO\"`\n - `\"ClNa\"`\n - `\"CCaO3\"`\n\n- **Query examples**:\n - A filter that matches an exactly given formula is `chemical_formula_reduced=\"H2NaO\"`.", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" }, "chemical_formula_hill": { "title": "Chemical Formula Hill", "pattern": "(^$)|^([A-Z][a-z]?([2-9]|[1-9]\\d+)?)+$", "type": "string", "description": "The chemical formula for a structure in [Hill form](https://dx.doi.org/10.1021/ja02046a005) with element symbols followed by integer chemical proportion numbers. The proportion number MUST be omitted if it is 1.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, only a subset of the filter features MAY be supported.\n - The overall scale factor of the chemical proportions is chosen such that the resulting values are integers that indicate the most chemically relevant unit of which the system is composed.\n For example, if the structure is a repeating unit cell with four hydrogens and four oxygens that represents two hydroperoxide molecules, `chemical_formula_hill` is `\"H2O2\"` (i.e., not `\"HO\"`, nor `\"H4O4\"`).\n - If the chemical insight needed to ascribe a Hill formula to the system is not present, the property MUST be handled as unset.\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\n - Elements MUST be placed in [Hill order](https://dx.doi.org/10.1021/ja02046a005), followed by their integer chemical proportion number.\n Hill order means: if carbon is present, it is placed first, and if also present, hydrogen is placed second.\n After that, all other elements are ordered alphabetically.\n If carbon is not present, all elements are ordered alphabetically.\n - If the system has sites with partial occupation and the total occupations of each element do not all sum up to integers, then the Hill formula SHOULD be handled as unset.\n - No spaces or separators are allowed.\n\n- **Examples**:\n - `\"H2O2\"`\n\n- **Query examples**:\n - A filter that matches an exactly given formula is `chemical_formula_hill=\"H2O2\"`.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "chemical_formula_anonymous": { "title": "Chemical Formula Anonymous", @@ -3600,8 +3632,8 @@ "type": "string", "description": "The anonymous formula is the `chemical_formula_reduced`, but where the elements are instead first ordered by their chemical proportion number, and then, in order left to right, replaced by anonymous symbols A, B, C, ..., Z, Aa, Ba, ..., Za, Ab, Bb, ... and so on.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property.\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\n\n- **Examples**:\n - `\"A2B\"`\n - `\"A42B42C16D12E10F9G5\"`\n\n- **Querying**:\n - A filter that matches an exactly given formula is `chemical_formula_anonymous=\"A2B\"`.", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" }, "dimension_types": { "title": "Dimension Types", @@ -3613,16 +3645,16 @@ }, "description": "List of three integers.\nFor each of the three directions indicated by the three lattice vectors (see property `lattice_vectors`), this list indicates if the direction is periodic (value `1`) or non-periodic (value `0`).\nNote: the elements in this list each refer to the direction of the corresponding entry in `lattice_vectors` and *not* the Cartesian x, y, z directions.\n\n- **Type**: list of integers.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n - MUST be a list of length 3.\n - Each integer element MUST assume only the value 0 or 1.\n\n- **Examples**:\n - For a molecule: `[0, 0, 0]`\n - For a wire along the direction specified by the third lattice vector: `[0, 0, 1]`\n - For a 2D surface/slab, periodic on the plane defined by the first and third lattice vectors: `[1, 0, 1]`\n - For a bulk 3D system: `[1, 1, 1]`", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "should" }, "nperiodic_dimensions": { "title": "Nperiodic Dimensions", "type": "integer", "description": "An integer specifying the number of periodic dimensions in the structure, equivalent to the number of non-zero entries in `dimension_types`.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The integer value MUST be between 0 and 3 inclusive and MUST be equal to the sum of the items in the `dimension_types` property.\n - This property only reflects the treatment of the lattice vectors provided for the structure, and not any physical interpretation of the dimensionality of its contents.\n\n- **Examples**:\n - `2` should be indicated in cases where `dimension_types` is any of `[1, 1, 0]`, `[1, 0, 1]`, `[0, 1, 1]`.\n\n- **Query examples**:\n - Match only structures with exactly 3 periodic dimensions: `nperiodic_dimensions=3`\n - Match all structures with 2 or fewer periodic dimensions: `nperiodic_dimensions<=2`", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" }, "lattice_vectors": { "title": "Lattice Vectors", @@ -3639,9 +3671,9 @@ }, "description": "The three lattice vectors in Cartesian coordinates, in \u00e5ngstr\u00f6m (\u00c5).\n\n- **Type**: list of list of floats or unknown values.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - MUST be a list of three vectors *a*, *b*, and *c*, where each of the vectors MUST BE a list of the vector's coordinates along the x, y, and z Cartesian coordinates.\n (Therefore, the first index runs over the three lattice vectors and the second index runs over the x, y, z Cartesian coordinates).\n - For databases that do not define an absolute Cartesian system (e.g., only defining the length and angles between vectors), the first lattice vector SHOULD be set along *x* and the second on the *xy*-plane.\n - MUST always contain three vectors of three coordinates each, independently of the elements of property `dimension_types`.\n The vectors SHOULD by convention be chosen so the determinant of the `lattice_vectors` matrix is different from zero.\n The vectors in the non-periodic directions have no significance beyond fulfilling these requirements.\n - The coordinates of the lattice vectors of non-periodic dimensions (i.e., those dimensions for which `dimension_types` is `0`) MAY be given as a list of all `null` values.\n If a lattice vector contains the value `null`, all coordinates of that lattice vector MUST be `null`.\n\n- **Examples**:\n - `[[4.0,0.0,0.0],[0.0,4.0,0.0],[0.0,1.0,4.0]]` represents a cell, where the first vector is `(4, 0, 0)`, i.e., a vector aligned along the `x` axis of length 4 \u00c5; the second vector is `(0, 4, 0)`; and the third vector is `(0, 1, 4)`.", "nullable": true, + "x-optimade-queryable": "optional", "x-optimade-unit": "\u00c5", - "x-optimade-support": "should", - "x-optimade-queryable": "optional" + "x-optimade-support": "should" }, "cartesian_site_positions": { "title": "Cartesian Site Positions", @@ -3656,17 +3688,17 @@ }, "description": "Cartesian positions of each site in the structure.\nA site is usually used to describe positions of atoms; what atoms can be encountered at a given site is conveyed by the `species_at_sites` property, and the species themselves are described in the `species` property.\n\n- **Type**: list of list of floats\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - It MUST be a list of length equal to the number of sites in the structure, where every element is a list of the three Cartesian coordinates of a site expressed as float values in the unit angstrom (\u00c5).\n - An entry MAY have multiple sites at the same Cartesian position (for a relevant use of this, see e.g., the property `assemblies`).\n\n- **Examples**:\n - `[[0,0,0],[0,0,2]]` indicates a structure with two sites, one sitting at the origin and one along the (positive) *z*-axis, 2 \u00c5 away from the origin.", "nullable": true, + "x-optimade-queryable": "optional", "x-optimade-unit": "\u00c5", - "x-optimade-support": "should", - "x-optimade-queryable": "optional" + "x-optimade-support": "should" }, "nsites": { "title": "Nsites", "type": "integer", "description": "An integer specifying the length of the `cartesian_site_positions` property.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `42`\n\n- **Query examples**:\n - Match only structures with exactly 4 sites: `nsites=4`\n - Match structures that have between 2 and 7 sites: `nsites>=2 AND nsites<=7`", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" }, "species": { "title": "Species", @@ -3676,8 +3708,8 @@ }, "description": "A list describing the species of the sites of this structure.\nSpecies can represent pure chemical elements, virtual-crystal atoms representing a statistical occupation of a given site by multiple chemical elements, and/or a location to which there are attached atoms, i.e., atoms whose precise location are unknown beyond that they are attached to that position (frequently used to indicate hydrogen atoms attached to another element, e.g., a carbon with three attached hydrogens might represent a methyl group, -CH3).\n\n- **Type**: list of dictionary with keys:\n - `name`: string (REQUIRED)\n - `chemical_symbols`: list of strings (REQUIRED)\n - `concentration`: list of float (REQUIRED)\n - `attached`: list of strings (REQUIRED)\n - `nattached`: list of integers (OPTIONAL)\n - `mass`: list of floats (OPTIONAL)\n - `original_name`: string (OPTIONAL).\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - Each list member MUST be a dictionary with the following keys:\n - **name**: REQUIRED; gives the name of the species; the **name** value MUST be unique in the `species` list;\n - **chemical_symbols**: REQUIRED; MUST be a list of strings of all chemical elements composing this species.\n Each item of the list MUST be one of the following:\n - a valid chemical-element symbol, or\n - the special value `\"X\"` to represent a non-chemical element, or\n - the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\n\n If any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.\n\n - **concentration**: REQUIRED; MUST be a list of floats, with same length as `chemical_symbols`.\n The numbers represent the relative concentration of the corresponding chemical symbol in this species.\n The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\n\n - Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\n - Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\n\n Note that concentrations are uncorrelated between different sites (even of the same species).\n\n - **attached**: OPTIONAL; if provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.\n\n - **nattached**: OPTIONAL; if provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the `attached` key.\n\n The implementation MUST include either both or none of the `attached` and `nattached` keys, and if they are provided, they MUST be of the same length.\n Furthermore, if they are provided, the `structure_features` property MUST include the string `site_attachments`.\n\n - **mass**: OPTIONAL. If present MUST be a list of floats, with the same length as `chemical_symbols`, providing element masses expressed in a.m.u.\n Elements denoting vacancies MUST have masses equal to 0.\n\n - **original_name**: OPTIONAL. Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\n\n Note: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.\n\n The main use of this field is for source databases that use species names, containing characters that are not allowed (see description of the list property `species_at_sites`).\n\n - For systems that have only species formed by a single chemical symbol, and that have at most one species per chemical symbol, SHOULD use the chemical symbol as species name (e.g., `\"Ti\"` for titanium, `\"O\"` for oxygen, etc.)\n However, note that this is OPTIONAL, and client implementations MUST NOT assume that the key corresponds to a chemical symbol, nor assume that if the species name is a valid chemical symbol, that it represents a species with that chemical symbol.\n This means that a species `{\"name\": \"C\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]}` is valid and represents a titanium species (and *not* a carbon species).\n - It is NOT RECOMMENDED that a structure includes species that do not have at least one corresponding site.\n\n- **Examples**:\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]`: any site with this species is occupied by a Ti atom.\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]`: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.\n - `[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]`: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.\n - `[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]`: any site with this species is occupied by a carbon isotope with mass 12.\n - `[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]`: any site with this species is occupied by a carbon isotope with mass 13.\n - `[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]`: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms.", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "should" }, "species_at_sites": { "title": "Species At Sites", @@ -3687,8 +3719,8 @@ }, "description": "Name of the species at each site (where values for sites are specified with the same order of the property `cartesian_site_positions`).\nThe properties of the species are found in the property `species`.\n\n- **Type**: list of strings.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - MUST have length equal to the number of sites in the structure (first dimension of the list property `cartesian_site_positions`).\n - Each species name mentioned in the `species_at_sites` list MUST be described in the list property `species` (i.e. for each value in the `species_at_sites` list there MUST exist exactly one dictionary in the `species` list with the `name` attribute equal to the corresponding `species_at_sites` value).\n - Each site MUST be associated only to a single species.\n **Note**: However, species can represent mixtures of atoms, and multiple species MAY be defined for the same chemical element.\n This latter case is useful when different atoms of the same type need to be grouped or distinguished, for instance in simulation codes to assign different initial spin states.\n\n- **Examples**:\n - `[\"Ti\",\"O2\"]` indicates that the first site is hosting a species labeled `\"Ti\"` and the second a species labeled `\"O2\"`.\n - `[\"Ac\", \"Ac\", \"Ag\", \"Ir\"]` indicating the first two sites contains the `\"Ac\"` species, while the third and fourth sites contain the `\"Ag\"` and `\"Ir\"` species, respectively.", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "should" }, "assemblies": { "title": "Assemblies", @@ -3697,8 +3729,8 @@ "$ref": "#/components/schemas/Assembly" }, "description": "A description of groups of sites that are statistically correlated.\n\n- **Type**: list of dictionary with keys:\n - `sites_in_groups`: list of list of integers (REQUIRED)\n - `group_probabilities`: list of floats (REQUIRED)\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - The property SHOULD be `null` for entries that have no partial occupancies.\n - If present, the correct flag MUST be set in the list `structure_features`.\n - Client implementations MUST check its presence (as its presence changes the interpretation of the structure).\n - If present, it MUST be a list of dictionaries, each of which represents an assembly and MUST have the following two keys:\n - **sites_in_groups**: Index of the sites (0-based) that belong to each group for each assembly.\n\n Example: `[[1], [2]]`: two groups, one with the second site, one with the third.\n Example: `[[1,2], [3]]`: one group with the second and third site, one with the fourth.\n\n - **group_probabilities**: Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\n It SHOULD sum to one.\n See below for examples of how to specify the probability of the occurrence of a vacancy.\n The possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.\n\n - If a site is not present in any group, it means that it is present with 100 % probability (as if no assembly was specified).\n - A site MUST NOT appear in more than one group.\n\n- **Examples** (for each entry of the assemblies list):\n - `{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}`: the first site and the second site never occur at the same time in the unit cell.\n Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.\n - `{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}`: the second and third site are either present together or not present; they form the first group of atoms for this assembly.\n The second group is formed by the fourth site.\n Sites of the first group (the second and the third) are never present at the same time as the fourth site.\n 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent).\n\n- **Notes**:\n - Assemblies are essential to represent, for instance, the situation where an atom can statistically occupy two different positions (sites).\n\n - By defining groups, it is possible to represent, e.g., the case where a functional molecule (and not just one atom) is either present or absent (or the case where it it is present in two conformations)\n\n - Considerations on virtual alloys and on vacancies: In the special case of a virtual alloy, these specifications allow two different, equivalent ways of specifying them.\n For instance, for a site at the origin with 30 % probability of being occupied by Si, 50 % probability of being occupied by Ge, and 20 % of being a vacancy, the following two representations are possible:\n\n - Using a single species:\n ```json\n {\n \"cartesian_site_positions\": [[0,0,0]],\n \"species_at_sites\": [\"SiGe-vac\"],\n \"species\": [\n {\n \"name\": \"SiGe-vac\",\n \"chemical_symbols\": [\"Si\", \"Ge\", \"vacancy\"],\n \"concentration\": [0.3, 0.5, 0.2]\n }\n ]\n // ...\n }\n ```\n\n - Using multiple species and the assemblies:\n ```json\n {\n \"cartesian_site_positions\": [ [0,0,0], [0,0,0], [0,0,0] ],\n \"species_at_sites\": [\"Si\", \"Ge\", \"vac\"],\n \"species\": [\n { \"name\": \"Si\", \"chemical_symbols\": [\"Si\"], \"concentration\": [1.0] },\n { \"name\": \"Ge\", \"chemical_symbols\": [\"Ge\"], \"concentration\": [1.0] },\n { \"name\": \"vac\", \"chemical_symbols\": [\"vacancy\"], \"concentration\": [1.0] }\n ],\n \"assemblies\": [\n {\n \"sites_in_groups\": [ [0], [1], [2] ],\n \"group_probabilities\": [0.3, 0.5, 0.2]\n }\n ]\n // ...\n }\n ```\n\n - It is up to the database provider to decide which representation to use, typically depending on the internal format in which the structure is stored.\n However, given a structure identified by a unique ID, the API implementation MUST always provide the same representation for it.\n\n - The probabilities of occurrence of different assemblies are uncorrelated.\n So, for instance in the following case with two assemblies:\n ```json\n {\n \"assemblies\": [\n {\n \"sites_in_groups\": [ [0], [1] ],\n \"group_probabilities\": [0.2, 0.8],\n },\n {\n \"sites_in_groups\": [ [2], [3] ],\n \"group_probabilities\": [0.3, 0.7]\n }\n ]\n }\n ```\n\n Site 0 is present with a probability of 20 % and site 1 with a probability of 80 %. These two sites are correlated (either site 0 or 1 is present). Similarly, site 2 is present with a probability of 30 % and site 3 with a probability of 70 %.\n These two sites are correlated (either site 2 or 3 is present).\n However, the presence or absence of sites 0 and 1 is not correlated with the presence or absence of sites 2 and 3 (in the specific example, the pair of sites (0, 2) can occur with 0.2*0.3 = 6 % probability; the pair (0, 3) with 0.2*0.7 = 14 % probability; the pair (1, 2) with 0.8*0.3 = 24 % probability; and the pair (1, 3) with 0.8*0.7 = 56 % probability).", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "structure_features": { "title": "Structure Features", @@ -3707,8 +3739,8 @@ "$ref": "#/components/schemas/StructureFeatures" }, "description": "A list of strings that flag which special features are used by the structure.\n\n- **Type**: list of strings\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property.\n Filters on the list MUST support all mandatory HAS-type queries.\n Filter operators for comparisons on the string components MUST support equality, support for other comparison operators are OPTIONAL.\n - MUST be an empty list if no special features are used.\n - MUST be sorted alphabetically.\n - If a special feature listed below is used, the list MUST contain the corresponding string.\n - If a special feature listed below is not used, the list MUST NOT contain the corresponding string.\n - **List of strings used to indicate special structure features**:\n - `disorder`: this flag MUST be present if any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element.\n - `implicit_atoms`: this flag MUST be present if the structure contains atoms that are not assigned to sites via the property `species_at_sites` (e.g., because their positions are unknown).\n When this flag is present, the properties related to the chemical formula will likely not match the type and count of atoms represented by the `species_at_sites`, `species` and `assemblies` properties.\n - `site_attachments`: this flag MUST be present if any one entry in the `species` list includes `attached` and `nattached`.\n - `assemblies`: this flag MUST be present if the property `assemblies` is present.\n\n- **Examples**: A structure having implicit atoms and using assemblies: `[\"assemblies\", \"implicit_atoms\"]`", - "x-optimade-support": "must", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "must" } }, "description": "This class contains the Field for the attributes used to represent a structure, e.g. unit cell, atoms, positions." diff --git a/optimade/models/entries.py b/optimade/models/entries.py index d0bffac77..5eca558c5 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -2,11 +2,7 @@ from datetime import datetime from typing import Dict, List, Optional -from pydantic import ( - BaseModel, - root_validator, - validator, -) +from pydantic import AnyUrl, BaseModel, root_validator, validator from optimade.models.jsonapi import Attributes, Meta, Relationships, Resource from optimade.models.optimade_json import DataType, Relationship @@ -104,6 +100,11 @@ def cast_immutable_id_to_str(cls, value): return value +class PartialDataLink(BaseModel): + link: AnyUrl + format: str # todo add check that the value of format is in a list of supported formats. + + class EntryMetadata(Meta): """Contains the metadata for the attributes of an entry""" @@ -113,6 +114,12 @@ class EntryMetadata(Meta): Database-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes).""", ) + partial_data_links: Dict[str, list[PartialDataLink]] = StrictField( + None, + description="""A dictionary, where the keys are the names of the properties in the attributes field for which the value is too large to be shared by default. + For each property one or more links are provided from which the value of the attribute can be retrieved.""", + ) + @validator("property_metadata") def check_property_metadata_subfields(cls, property_metadata): from optimade.server.mappers.entries import ( @@ -129,6 +136,25 @@ def check_property_metadata_subfields(cls, property_metadata): ) return property_metadata + @validator("partial_data_links") + def check_partial_data_links_subfields(cls, partial_data_links): + from optimade.server.mappers.entries import ( + BaseResourceMapper, + ) + + if partial_data_links: + for field in partial_data_links: + if attribute_partial_data_link := partial_data_links.get(field): + for subdict in attribute_partial_data_link: + for subfield in subdict.__dict__: + if subfield in ("link", "format"): + continue + BaseResourceMapper.check_starts_with_supported_prefix( + subfield, + "The only OPTIMADE fields defined under the 'partial_data_links' field are 'format'and ĺinks' all other database and domain specific fields must have a database/domain specific prefix.", + ) + return partial_data_links + class EntryResource(Resource): """The base model for an entry resource.""" @@ -199,6 +225,7 @@ def check_meta(cls, values): if not meta: return values + # todo the code for property_metadata and partial_data_links is very similar so it should be possible to reduce the size of the code here. if property_metadata := meta.pop("property_metadata", None): # check that all the fields under property metadata are in attributes attributes = values.get("attributes", {}) @@ -208,6 +235,15 @@ def check_meta(cls, values): f"The keys under the field `property_metadata` need to match with the field names in attributes. The field {subfield} is however not in attributes." ) + if partial_data_links := meta.pop("partial_data_links", None): + # check that all the fields under property metadata are in attributes + attributes = values.get("attributes", {}) + for subfield in partial_data_links: + if subfield not in attributes: + raise ValueError( + f"The keys under the field `partial_data_links` need to match with the field names in attributes. The field {subfield} is however not in attributes." + ) + # At this point I am getting ahead of the specification. There is the intention to allow database specific fields(with the database specific prefixes) here in line with the JSON API specification, but it has not been decided yet how this case should be handled in the property definitions. for field in meta: BaseResourceMapper.check_starts_with_supported_prefix( @@ -216,6 +252,7 @@ def check_meta(cls, values): ) values["meta"]["property_metadata"] = property_metadata + values["meta"]["partial_data_links"] = partial_data_links return values diff --git a/optimade/server/data/test_structures.json b/optimade/server/data/test_structures.json index c189ad981..d9710600f 100644 --- a/optimade/server/data/test_structures.json +++ b/optimade/server/data/test_structures.json @@ -2125,378 +2125,17 @@ }, "assemblies": null, "chemsys": "Ag-B-C-Cl-H-N-O-P", - "cartesian_site_positions": [ - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ] - ], + "cartesian_site_positions": null, + "meta": { + "partial_data_links": { + "cartesian_site_positions": [ + { + "format": "jsonlines", + "link": "https://example.org/optimade/v1.2/extensions/partial_data/structures/mpf_551/a/default_format" + } + ] + } + }, "dimension_types": [ 1, 1, diff --git a/tests/adapters/test_jsonl.py b/tests/adapters/test_jsonl.py index 2bda96c2c..d5843fcc3 100644 --- a/tests/adapters/test_jsonl.py +++ b/tests/adapters/test_jsonl.py @@ -1,19 +1,11 @@ +from pathlib import Path + from optimade.adapters.jsonl import from_jsonl, to_jsonl +test_object = from_jsonl(Path(__file__).parent.resolve() / "testdata.jsonl") + -def test_to_jsonl(): - test_object = [ - { - "optimade_partial_data": {"version": "1.2.0"}, - "layout": "dense", - "returned_ranges": [{"start": 10, "stop": 20, "step": 2}], - }, - 1243, - 345, - -12.6, - ["PARTIAL-DATA-NEXT", ["https://example.db.org/value4"]], - ] +def test_to_and_from_jsonl(): file_content = to_jsonl(test_object) reprocessed_file = from_jsonl(file_content) assert test_object == reprocessed_file - pass diff --git a/tests/adapters/testdata.jsonl b/tests/adapters/testdata.jsonl new file mode 100644 index 000000000..47eb9346b --- /dev/null +++ b/tests/adapters/testdata.jsonl @@ -0,0 +1,5 @@ +{"optimade_partial_data": {"version": "1.2.0"}, "layout": "dense", "returned_ranges": [{"start": 10, "stop": 20, "step": 2}]} +1243 +345 +-12.6 +["PARTIAL-DATA-NEXT", ["https://example.db.org/value4"]] From 9657ff3a0a366fa11a5216712d932b71e8ed3774 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Mon, 14 Aug 2023 16:33:49 +0200 Subject: [PATCH 53/86] Added descriptions for fields partial data links. --- openapi/index_openapi.json | 24 ++-- openapi/openapi.json | 272 +++++++++++++++++++------------------ optimade/models/entries.py | 10 +- 3 files changed, 158 insertions(+), 148 deletions(-) diff --git a/openapi/index_openapi.json b/openapi/index_openapi.json index fae87ce9b..755227873 100644 --- a/openapi/index_openapi.json +++ b/openapi/index_openapi.json @@ -528,15 +528,15 @@ "title": "Id", "type": "string", "description": "An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`", - "x-optimade-queryable": "must", - "x-optimade-support": "must" + "x-optimade-support": "must", + "x-optimade-queryable": "must" }, "type": { "title": "Type", "type": "string", "description": "The name of the type of an entry.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type `` and ID `` MUST be returned in response to a request for `//` under the versioned base URL.\n\n- **Example**: `\"structures\"`", - "x-optimade-queryable": "must", - "x-optimade-support": "must" + "x-optimade-support": "must", + "x-optimade-queryable": "must" }, "links": { "title": "Links", @@ -588,16 +588,16 @@ "title": "Immutable Id", "type": "string", "description": "The entry's immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)", - "x-optimade-queryable": "must", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "must" }, "last_modified": { "title": "Last Modified", "type": "string", "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "format": "date-time", - "x-optimade-queryable": "must", - "x-optimade-support": "should" + "x-optimade-support": "should", + "x-optimade-queryable": "must" } }, "description": "Contains key-value pairs representing the entry's properties." @@ -1137,8 +1137,8 @@ "title": "Id", "type": "string", "description": "An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`", - "x-optimade-queryable": "must", - "x-optimade-support": "must" + "x-optimade-support": "must", + "x-optimade-queryable": "must" }, "type": { "title": "Type", @@ -1429,11 +1429,13 @@ "maxLength": 65536, "minLength": 1, "type": "string", + "description": "String. A JSON API link that points to a location from which the omitted data can be fetched. There is no requirement on the syntax or format for the link URL.", "format": "uri" }, "format": { "title": "Format", - "type": "string" + "type": "string", + "description": "String. The name of the format provided via this link. For one of the objects this format field SHOULD have the value \"jsonlines\", which refers to the format in OPTIMADE JSON lines partial data format." } } }, diff --git a/openapi/openapi.json b/openapi/openapi.json index e2523f4e4..0c7898c45 100644 --- a/openapi/openapi.json +++ b/openapi/openapi.json @@ -1341,8 +1341,8 @@ } }, "description": "Index of the sites (0-based) that belong to each group for each assembly.\n\n- **Examples**:\n - `[[1], [2]]`: two groups, one with the second site, one with the third.\n - `[[1,2], [3]]`: one group with the second and third site, one with the fourth.", - "x-optimade-queryable": "optional", - "x-optimade-support": "must" + "x-optimade-support": "must", + "x-optimade-queryable": "optional" }, "group_probabilities": { "title": "Group Probabilities", @@ -1351,8 +1351,8 @@ "type": "number" }, "description": "Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\nIt SHOULD sum to one.\nSee below for examples of how to specify the probability of the occurrence of a vacancy.\nThe possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.", - "x-optimade-queryable": "optional", - "x-optimade-support": "must" + "x-optimade-support": "must", + "x-optimade-queryable": "optional" } }, "description": "A description of groups of sites that are statistically correlated.\n\n- **Examples** (for each entry of the assemblies list):\n - `{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}`: the first site and the second site never occur at the same time in the unit cell.\n Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.\n - `{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}`: the second and third site are either present together or not present; they form the first group of atoms for this assembly.\n The second group is formed by the fourth site. Sites of the first group (the second and the third) are never present at the same time as the fourth site.\n 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent)." @@ -1778,15 +1778,15 @@ "title": "Id", "type": "string", "description": "An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`", - "x-optimade-queryable": "must", - "x-optimade-support": "must" + "x-optimade-support": "must", + "x-optimade-queryable": "must" }, "type": { "title": "Type", "type": "string", "description": "The name of the type of an entry.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type `` and ID `` MUST be returned in response to a request for `//` under the versioned base URL.\n\n- **Example**: `\"structures\"`", - "x-optimade-queryable": "must", - "x-optimade-support": "must" + "x-optimade-support": "must", + "x-optimade-queryable": "must" }, "links": { "title": "Links", @@ -1838,16 +1838,16 @@ "title": "Immutable Id", "type": "string", "description": "The entry's immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)", - "x-optimade-queryable": "must", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "must" }, "last_modified": { "title": "Last Modified", "type": "string", "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "format": "date-time", - "x-optimade-queryable": "must", - "x-optimade-support": "should" + "x-optimade-support": "should", + "x-optimade-queryable": "must" } }, "description": "Contains key-value pairs representing the entry's properties." @@ -2246,8 +2246,8 @@ "title": "Id", "type": "string", "description": "An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`", - "x-optimade-queryable": "must", - "x-optimade-support": "must" + "x-optimade-support": "must", + "x-optimade-queryable": "must" }, "type": { "title": "Type", @@ -2538,11 +2538,13 @@ "maxLength": 65536, "minLength": 1, "type": "string", + "description": "String. A JSON API link that points to a location from which the omitted data can be fetched. There is no requirement on the syntax or format for the link URL.", "format": "uri" }, "format": { "title": "Format", - "type": "string" + "type": "string", + "description": "String. The name of the format provided via this link. For one of the objects this format field SHOULD have the value \"jsonlines\", which refers to the format in OPTIMADE JSON lines partial data format." } } }, @@ -2566,22 +2568,22 @@ "title": "Name", "type": "string", "description": "Full name of the person, REQUIRED.", - "x-optimade-queryable": "optional", - "x-optimade-support": "must" + "x-optimade-support": "must", + "x-optimade-queryable": "optional" }, "firstname": { "title": "Firstname", "type": "string", "description": "First name of the person.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "lastname": { "title": "Lastname", "type": "string", "description": "Last name of the person.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" } }, "description": "A person, i.e., an author, editor or other." @@ -2683,8 +2685,8 @@ "title": "Id", "type": "string", "description": "An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`", - "x-optimade-queryable": "must", - "x-optimade-support": "must" + "x-optimade-support": "must", + "x-optimade-queryable": "must" }, "type": { "title": "Type", @@ -2692,8 +2694,8 @@ "type": "string", "description": "The name of the type of an entry.\n- **Type**: string.\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type and ID MUST be returned in response to a request for `//` under the versioned base URL.\n- **Example**: `\"structures\"`", "default": "references", - "x-optimade-queryable": "must", - "x-optimade-support": "must" + "x-optimade-support": "must", + "x-optimade-queryable": "must" }, "links": { "title": "Links", @@ -2739,16 +2741,16 @@ "title": "Immutable Id", "type": "string", "description": "The entry's immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)", - "x-optimade-queryable": "must", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "must" }, "last_modified": { "title": "Last Modified", "type": "string", "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "format": "date-time", - "x-optimade-queryable": "must", - "x-optimade-support": "should" + "x-optimade-support": "should", + "x-optimade-queryable": "must" }, "authors": { "title": "Authors", @@ -2757,8 +2759,8 @@ "$ref": "#/components/schemas/Person" }, "description": "List of person objects containing the authors of the reference.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "editors": { "title": "Editors", @@ -2767,15 +2769,15 @@ "$ref": "#/components/schemas/Person" }, "description": "List of person objects containing the editors of the reference.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "doi": { "title": "Doi", "type": "string", "description": "The digital object identifier of the reference.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "url": { "title": "Url", @@ -2784,162 +2786,162 @@ "type": "string", "description": "The URL of the reference.", "format": "uri", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "address": { "title": "Address", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "annote": { "title": "Annote", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "booktitle": { "title": "Booktitle", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "chapter": { "title": "Chapter", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "crossref": { "title": "Crossref", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "edition": { "title": "Edition", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "howpublished": { "title": "Howpublished", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "institution": { "title": "Institution", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "journal": { "title": "Journal", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "key": { "title": "Key", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "month": { "title": "Month", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "note": { "title": "Note", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "number": { "title": "Number", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "organization": { "title": "Organization", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "pages": { "title": "Pages", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "publisher": { "title": "Publisher", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "school": { "title": "School", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "series": { "title": "Series", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "title": { "title": "Title", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "bib_type": { "title": "Bib Type", "type": "string", "description": "Type of the reference, corresponding to the **type** property in the BiBTeX specification.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "volume": { "title": "Volume", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "year": { "title": "Year", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" } }, "description": "Model that stores the attributes of a reference.\n\nMany properties match the meaning described in the\n[BibTeX specification](http://bibtexml.sourceforge.net/btxdoc.pdf)." @@ -3361,8 +3363,8 @@ "title": "Name", "type": "string", "description": "Gives the name of the species; the **name** value MUST be unique in the `species` list.", - "x-optimade-queryable": "optional", - "x-optimade-support": "must" + "x-optimade-support": "must", + "x-optimade-queryable": "optional" }, "chemical_symbols": { "title": "Chemical Symbols", @@ -3371,8 +3373,8 @@ "type": "string" }, "description": "MUST be a list of strings of all chemical elements composing this species. Each item of the list MUST be one of the following:\n\n- a valid chemical-element symbol, or\n- the special value `\"X\"` to represent a non-chemical element, or\n- the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\n\nIf any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.", - "x-optimade-queryable": "optional", - "x-optimade-support": "must" + "x-optimade-support": "must", + "x-optimade-queryable": "optional" }, "concentration": { "title": "Concentration", @@ -3381,8 +3383,8 @@ "type": "number" }, "description": "MUST be a list of floats, with same length as `chemical_symbols`. The numbers represent the relative concentration of the corresponding chemical symbol in this species. The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\n\n- Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\n- Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\n\nNote that concentrations are uncorrelated between different site (even of the same species).", - "x-optimade-queryable": "optional", - "x-optimade-support": "must" + "x-optimade-support": "must", + "x-optimade-queryable": "optional" }, "mass": { "title": "Mass", @@ -3391,16 +3393,16 @@ "type": "number" }, "description": "If present MUST be a list of floats expressed in a.m.u.\nElements denoting vacancies MUST have masses equal to 0.", + "x-optimade-support": "optional", "x-optimade-queryable": "optional", - "x-optimade-unit": "a.m.u.", - "x-optimade-support": "optional" + "x-optimade-unit": "a.m.u." }, "original_name": { "title": "Original Name", "type": "string", "description": "Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\n\nNote: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "attached": { "title": "Attached", @@ -3409,8 +3411,8 @@ "type": "string" }, "description": "If provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "nattached": { "title": "Nattached", @@ -3419,8 +3421,8 @@ "type": "integer" }, "description": "If provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the :field:`attached` key.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" } }, "description": "A list describing the species of the sites of this structure.\n\nSpecies can represent pure chemical elements, virtual-crystal atoms representing a\nstatistical occupation of a given site by multiple chemical elements, and/or a\nlocation to which there are attached atoms, i.e., atoms whose precise location are\nunknown beyond that they are attached to that position (frequently used to indicate\nhydrogen atoms attached to another element, e.g., a carbon with three attached\nhydrogens might represent a methyl group, -CH3).\n\n- **Examples**:\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]`: any site with this species is occupied by a Ti atom.\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]`: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.\n - `[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]`: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.\n - `[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]`: any site with this species is occupied by a carbon isotope with mass 12.\n - `[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]`: any site with this species is occupied by a carbon isotope with mass 13.\n - `[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]`: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms." @@ -3489,8 +3491,8 @@ "title": "Id", "type": "string", "description": "An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`", - "x-optimade-queryable": "must", - "x-optimade-support": "must" + "x-optimade-support": "must", + "x-optimade-queryable": "must" }, "type": { "title": "Type", @@ -3498,8 +3500,8 @@ "type": "string", "description": "The name of the type of an entry.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type `` and ID `` MUST be returned in response to a request for `//` under the versioned base URL.\n\n- **Examples**:\n - `\"structures\"`", "default": "structures", - "x-optimade-queryable": "must", - "x-optimade-support": "must" + "x-optimade-support": "must", + "x-optimade-queryable": "must" }, "links": { "title": "Links", @@ -3559,8 +3561,8 @@ "title": "Immutable Id", "type": "string", "description": "The entry's immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)", - "x-optimade-queryable": "must", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "must" }, "last_modified": { "title": "Last Modified", @@ -3568,8 +3570,8 @@ "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "format": "date-time", "nullable": true, - "x-optimade-queryable": "must", - "x-optimade-support": "should" + "x-optimade-support": "should", + "x-optimade-queryable": "must" }, "elements": { "title": "Elements", @@ -3579,16 +3581,16 @@ }, "description": "The chemical symbols of the different elements present in the structure.\n\n- **Type**: list of strings.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The strings are the chemical symbols, i.e., either a single uppercase letter or an uppercase letter followed by a number of lowercase letters.\n - The order MUST be alphabetical.\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements_ratios`, if the latter is provided.\n - Note: This property SHOULD NOT contain the string \"X\" to indicate non-chemical elements or \"vacancy\" to indicate vacancies (in contrast to the field `chemical_symbols` for the `species` property).\n\n- **Examples**:\n - `[\"Si\"]`\n - `[\"Al\",\"O\",\"Si\"]`\n\n- **Query examples**:\n - A filter that matches all records of structures that contain Si, Al **and** O, and possibly other elements: `elements HAS ALL \"Si\", \"Al\", \"O\"`.\n - To match structures with exactly these three elements, use `elements HAS ALL \"Si\", \"Al\", \"O\" AND elements LENGTH 3`.\n - Note: length queries on this property can be equivalently formulated by filtering on the `nelements`_ property directly.", "nullable": true, - "x-optimade-queryable": "must", - "x-optimade-support": "should" + "x-optimade-support": "should", + "x-optimade-queryable": "must" }, "nelements": { "title": "Nelements", "type": "integer", "description": "Number of different elements in the structure as an integer.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - MUST be equal to the lengths of the list properties `elements` and `elements_ratios`, if they are provided.\n\n- **Examples**:\n - `3`\n\n- **Querying**:\n - Note: queries on this property can equivalently be formulated using `elements LENGTH`.\n - A filter that matches structures that have exactly 4 elements: `nelements=4`.\n - A filter that matches structures that have between 2 and 7 elements: `nelements>=2 AND nelements<=7`.", "nullable": true, - "x-optimade-queryable": "must", - "x-optimade-support": "should" + "x-optimade-support": "should", + "x-optimade-queryable": "must" }, "elements_ratios": { "title": "Elements Ratios", @@ -3598,16 +3600,16 @@ }, "description": "Relative proportions of different elements in the structure.\n\n- **Type**: list of floats\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - Composed by the proportions of elements in the structure as a list of floating point numbers.\n - The sum of the numbers MUST be 1.0 (within floating point accuracy)\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements`, if the latter is provided.\n\n- **Examples**:\n - `[1.0]`\n - `[0.3333333333333333, 0.2222222222222222, 0.4444444444444444]`\n\n- **Query examples**:\n - Note: Useful filters can be formulated using the set operator syntax for correlated values.\n However, since the values are floating point values, the use of equality comparisons is generally inadvisable.\n - OPTIONAL: a filter that matches structures where approximately 1/3 of the atoms in the structure are the element Al is: `elements:elements_ratios HAS ALL \"Al\":>0.3333, \"Al\":<0.3334`.", "nullable": true, - "x-optimade-queryable": "must", - "x-optimade-support": "should" + "x-optimade-support": "should", + "x-optimade-queryable": "must" }, "chemical_formula_descriptive": { "title": "Chemical Formula Descriptive", "type": "string", "description": "The chemical formula for a structure as a string in a form chosen by the API implementation.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The chemical formula is given as a string consisting of properly capitalized element symbols followed by integers or decimal numbers, balanced parentheses, square, and curly brackets `(`,`)`, `[`,`]`, `{`, `}`, commas, the `+`, `-`, `:` and `=` symbols. The parentheses are allowed to be followed by a number. Spaces are allowed anywhere except within chemical symbols. The order of elements and any groupings indicated by parentheses or brackets are chosen freely by the API implementation.\n - The string SHOULD be arithmetically consistent with the element ratios in the `chemical_formula_reduced` property.\n - It is RECOMMENDED, but not mandatory, that symbols, parentheses and brackets, if used, are used with the meanings prescribed by [IUPAC's Nomenclature of Organic Chemistry](https://www.qmul.ac.uk/sbcs/iupac/bibliog/blue.html).\n\n- **Examples**:\n - `\"(H2O)2 Na\"`\n - `\"NaCl\"`\n - `\"CaCO3\"`\n - `\"CCaO3\"`\n - `\"(CH3)3N+ - [CH2]2-OH = Me3N+ - CH2 - CH2OH\"`\n\n- **Query examples**:\n - Note: the free-form nature of this property is likely to make queries on it across different databases inconsistent.\n - A filter that matches an exactly given formula: `chemical_formula_descriptive=\"(H2O)2 Na\"`.\n - A filter that does a partial match: `chemical_formula_descriptive CONTAINS \"H2O\"`.", "nullable": true, - "x-optimade-queryable": "must", - "x-optimade-support": "should" + "x-optimade-support": "should", + "x-optimade-queryable": "must" }, "chemical_formula_reduced": { "title": "Chemical Formula Reduced", @@ -3615,16 +3617,16 @@ "type": "string", "description": "The reduced chemical formula for a structure as a string with element symbols and integer chemical proportion numbers.\nThe proportion number MUST be omitted if it is 1.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property.\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\n Intricate queries on formula components are instead suggested to be formulated using set-type filter operators on the multi valued `elements` and `elements_ratios` properties.\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\n - Elements MUST be placed in alphabetical order, followed by their integer chemical proportion number.\n - For structures with no partial occupation, the chemical proportion numbers are the smallest integers for which the chemical proportion is exactly correct.\n - For structures with partial occupation, the chemical proportion numbers are integers that within reasonable approximation indicate the correct chemical proportions. The precise details of how to perform the rounding is chosen by the API implementation.\n - No spaces or separators are allowed.\n\n- **Examples**:\n - `\"H2NaO\"`\n - `\"ClNa\"`\n - `\"CCaO3\"`\n\n- **Query examples**:\n - A filter that matches an exactly given formula is `chemical_formula_reduced=\"H2NaO\"`.", "nullable": true, - "x-optimade-queryable": "must", - "x-optimade-support": "should" + "x-optimade-support": "should", + "x-optimade-queryable": "must" }, "chemical_formula_hill": { "title": "Chemical Formula Hill", "pattern": "(^$)|^([A-Z][a-z]?([2-9]|[1-9]\\d+)?)+$", "type": "string", "description": "The chemical formula for a structure in [Hill form](https://dx.doi.org/10.1021/ja02046a005) with element symbols followed by integer chemical proportion numbers. The proportion number MUST be omitted if it is 1.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, only a subset of the filter features MAY be supported.\n - The overall scale factor of the chemical proportions is chosen such that the resulting values are integers that indicate the most chemically relevant unit of which the system is composed.\n For example, if the structure is a repeating unit cell with four hydrogens and four oxygens that represents two hydroperoxide molecules, `chemical_formula_hill` is `\"H2O2\"` (i.e., not `\"HO\"`, nor `\"H4O4\"`).\n - If the chemical insight needed to ascribe a Hill formula to the system is not present, the property MUST be handled as unset.\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\n - Elements MUST be placed in [Hill order](https://dx.doi.org/10.1021/ja02046a005), followed by their integer chemical proportion number.\n Hill order means: if carbon is present, it is placed first, and if also present, hydrogen is placed second.\n After that, all other elements are ordered alphabetically.\n If carbon is not present, all elements are ordered alphabetically.\n - If the system has sites with partial occupation and the total occupations of each element do not all sum up to integers, then the Hill formula SHOULD be handled as unset.\n - No spaces or separators are allowed.\n\n- **Examples**:\n - `\"H2O2\"`\n\n- **Query examples**:\n - A filter that matches an exactly given formula is `chemical_formula_hill=\"H2O2\"`.", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "chemical_formula_anonymous": { "title": "Chemical Formula Anonymous", @@ -3632,8 +3634,8 @@ "type": "string", "description": "The anonymous formula is the `chemical_formula_reduced`, but where the elements are instead first ordered by their chemical proportion number, and then, in order left to right, replaced by anonymous symbols A, B, C, ..., Z, Aa, Ba, ..., Za, Ab, Bb, ... and so on.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property.\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\n\n- **Examples**:\n - `\"A2B\"`\n - `\"A42B42C16D12E10F9G5\"`\n\n- **Querying**:\n - A filter that matches an exactly given formula is `chemical_formula_anonymous=\"A2B\"`.", "nullable": true, - "x-optimade-queryable": "must", - "x-optimade-support": "should" + "x-optimade-support": "should", + "x-optimade-queryable": "must" }, "dimension_types": { "title": "Dimension Types", @@ -3645,16 +3647,16 @@ }, "description": "List of three integers.\nFor each of the three directions indicated by the three lattice vectors (see property `lattice_vectors`), this list indicates if the direction is periodic (value `1`) or non-periodic (value `0`).\nNote: the elements in this list each refer to the direction of the corresponding entry in `lattice_vectors` and *not* the Cartesian x, y, z directions.\n\n- **Type**: list of integers.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n - MUST be a list of length 3.\n - Each integer element MUST assume only the value 0 or 1.\n\n- **Examples**:\n - For a molecule: `[0, 0, 0]`\n - For a wire along the direction specified by the third lattice vector: `[0, 0, 1]`\n - For a 2D surface/slab, periodic on the plane defined by the first and third lattice vectors: `[1, 0, 1]`\n - For a bulk 3D system: `[1, 1, 1]`", "nullable": true, - "x-optimade-queryable": "optional", - "x-optimade-support": "should" + "x-optimade-support": "should", + "x-optimade-queryable": "optional" }, "nperiodic_dimensions": { "title": "Nperiodic Dimensions", "type": "integer", "description": "An integer specifying the number of periodic dimensions in the structure, equivalent to the number of non-zero entries in `dimension_types`.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The integer value MUST be between 0 and 3 inclusive and MUST be equal to the sum of the items in the `dimension_types` property.\n - This property only reflects the treatment of the lattice vectors provided for the structure, and not any physical interpretation of the dimensionality of its contents.\n\n- **Examples**:\n - `2` should be indicated in cases where `dimension_types` is any of `[1, 1, 0]`, `[1, 0, 1]`, `[0, 1, 1]`.\n\n- **Query examples**:\n - Match only structures with exactly 3 periodic dimensions: `nperiodic_dimensions=3`\n - Match all structures with 2 or fewer periodic dimensions: `nperiodic_dimensions<=2`", "nullable": true, - "x-optimade-queryable": "must", - "x-optimade-support": "should" + "x-optimade-support": "should", + "x-optimade-queryable": "must" }, "lattice_vectors": { "title": "Lattice Vectors", @@ -3671,9 +3673,9 @@ }, "description": "The three lattice vectors in Cartesian coordinates, in \u00e5ngstr\u00f6m (\u00c5).\n\n- **Type**: list of list of floats or unknown values.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - MUST be a list of three vectors *a*, *b*, and *c*, where each of the vectors MUST BE a list of the vector's coordinates along the x, y, and z Cartesian coordinates.\n (Therefore, the first index runs over the three lattice vectors and the second index runs over the x, y, z Cartesian coordinates).\n - For databases that do not define an absolute Cartesian system (e.g., only defining the length and angles between vectors), the first lattice vector SHOULD be set along *x* and the second on the *xy*-plane.\n - MUST always contain three vectors of three coordinates each, independently of the elements of property `dimension_types`.\n The vectors SHOULD by convention be chosen so the determinant of the `lattice_vectors` matrix is different from zero.\n The vectors in the non-periodic directions have no significance beyond fulfilling these requirements.\n - The coordinates of the lattice vectors of non-periodic dimensions (i.e., those dimensions for which `dimension_types` is `0`) MAY be given as a list of all `null` values.\n If a lattice vector contains the value `null`, all coordinates of that lattice vector MUST be `null`.\n\n- **Examples**:\n - `[[4.0,0.0,0.0],[0.0,4.0,0.0],[0.0,1.0,4.0]]` represents a cell, where the first vector is `(4, 0, 0)`, i.e., a vector aligned along the `x` axis of length 4 \u00c5; the second vector is `(0, 4, 0)`; and the third vector is `(0, 1, 4)`.", "nullable": true, - "x-optimade-queryable": "optional", "x-optimade-unit": "\u00c5", - "x-optimade-support": "should" + "x-optimade-support": "should", + "x-optimade-queryable": "optional" }, "cartesian_site_positions": { "title": "Cartesian Site Positions", @@ -3688,17 +3690,17 @@ }, "description": "Cartesian positions of each site in the structure.\nA site is usually used to describe positions of atoms; what atoms can be encountered at a given site is conveyed by the `species_at_sites` property, and the species themselves are described in the `species` property.\n\n- **Type**: list of list of floats\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - It MUST be a list of length equal to the number of sites in the structure, where every element is a list of the three Cartesian coordinates of a site expressed as float values in the unit angstrom (\u00c5).\n - An entry MAY have multiple sites at the same Cartesian position (for a relevant use of this, see e.g., the property `assemblies`).\n\n- **Examples**:\n - `[[0,0,0],[0,0,2]]` indicates a structure with two sites, one sitting at the origin and one along the (positive) *z*-axis, 2 \u00c5 away from the origin.", "nullable": true, - "x-optimade-queryable": "optional", "x-optimade-unit": "\u00c5", - "x-optimade-support": "should" + "x-optimade-support": "should", + "x-optimade-queryable": "optional" }, "nsites": { "title": "Nsites", "type": "integer", "description": "An integer specifying the length of the `cartesian_site_positions` property.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `42`\n\n- **Query examples**:\n - Match only structures with exactly 4 sites: `nsites=4`\n - Match structures that have between 2 and 7 sites: `nsites>=2 AND nsites<=7`", "nullable": true, - "x-optimade-queryable": "must", - "x-optimade-support": "should" + "x-optimade-support": "should", + "x-optimade-queryable": "must" }, "species": { "title": "Species", @@ -3708,8 +3710,8 @@ }, "description": "A list describing the species of the sites of this structure.\nSpecies can represent pure chemical elements, virtual-crystal atoms representing a statistical occupation of a given site by multiple chemical elements, and/or a location to which there are attached atoms, i.e., atoms whose precise location are unknown beyond that they are attached to that position (frequently used to indicate hydrogen atoms attached to another element, e.g., a carbon with three attached hydrogens might represent a methyl group, -CH3).\n\n- **Type**: list of dictionary with keys:\n - `name`: string (REQUIRED)\n - `chemical_symbols`: list of strings (REQUIRED)\n - `concentration`: list of float (REQUIRED)\n - `attached`: list of strings (REQUIRED)\n - `nattached`: list of integers (OPTIONAL)\n - `mass`: list of floats (OPTIONAL)\n - `original_name`: string (OPTIONAL).\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - Each list member MUST be a dictionary with the following keys:\n - **name**: REQUIRED; gives the name of the species; the **name** value MUST be unique in the `species` list;\n - **chemical_symbols**: REQUIRED; MUST be a list of strings of all chemical elements composing this species.\n Each item of the list MUST be one of the following:\n - a valid chemical-element symbol, or\n - the special value `\"X\"` to represent a non-chemical element, or\n - the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\n\n If any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.\n\n - **concentration**: REQUIRED; MUST be a list of floats, with same length as `chemical_symbols`.\n The numbers represent the relative concentration of the corresponding chemical symbol in this species.\n The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\n\n - Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\n - Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\n\n Note that concentrations are uncorrelated between different sites (even of the same species).\n\n - **attached**: OPTIONAL; if provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.\n\n - **nattached**: OPTIONAL; if provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the `attached` key.\n\n The implementation MUST include either both or none of the `attached` and `nattached` keys, and if they are provided, they MUST be of the same length.\n Furthermore, if they are provided, the `structure_features` property MUST include the string `site_attachments`.\n\n - **mass**: OPTIONAL. If present MUST be a list of floats, with the same length as `chemical_symbols`, providing element masses expressed in a.m.u.\n Elements denoting vacancies MUST have masses equal to 0.\n\n - **original_name**: OPTIONAL. Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\n\n Note: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.\n\n The main use of this field is for source databases that use species names, containing characters that are not allowed (see description of the list property `species_at_sites`).\n\n - For systems that have only species formed by a single chemical symbol, and that have at most one species per chemical symbol, SHOULD use the chemical symbol as species name (e.g., `\"Ti\"` for titanium, `\"O\"` for oxygen, etc.)\n However, note that this is OPTIONAL, and client implementations MUST NOT assume that the key corresponds to a chemical symbol, nor assume that if the species name is a valid chemical symbol, that it represents a species with that chemical symbol.\n This means that a species `{\"name\": \"C\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]}` is valid and represents a titanium species (and *not* a carbon species).\n - It is NOT RECOMMENDED that a structure includes species that do not have at least one corresponding site.\n\n- **Examples**:\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]`: any site with this species is occupied by a Ti atom.\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]`: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.\n - `[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]`: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.\n - `[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]`: any site with this species is occupied by a carbon isotope with mass 12.\n - `[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]`: any site with this species is occupied by a carbon isotope with mass 13.\n - `[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]`: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms.", "nullable": true, - "x-optimade-queryable": "optional", - "x-optimade-support": "should" + "x-optimade-support": "should", + "x-optimade-queryable": "optional" }, "species_at_sites": { "title": "Species At Sites", @@ -3719,8 +3721,8 @@ }, "description": "Name of the species at each site (where values for sites are specified with the same order of the property `cartesian_site_positions`).\nThe properties of the species are found in the property `species`.\n\n- **Type**: list of strings.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - MUST have length equal to the number of sites in the structure (first dimension of the list property `cartesian_site_positions`).\n - Each species name mentioned in the `species_at_sites` list MUST be described in the list property `species` (i.e. for each value in the `species_at_sites` list there MUST exist exactly one dictionary in the `species` list with the `name` attribute equal to the corresponding `species_at_sites` value).\n - Each site MUST be associated only to a single species.\n **Note**: However, species can represent mixtures of atoms, and multiple species MAY be defined for the same chemical element.\n This latter case is useful when different atoms of the same type need to be grouped or distinguished, for instance in simulation codes to assign different initial spin states.\n\n- **Examples**:\n - `[\"Ti\",\"O2\"]` indicates that the first site is hosting a species labeled `\"Ti\"` and the second a species labeled `\"O2\"`.\n - `[\"Ac\", \"Ac\", \"Ag\", \"Ir\"]` indicating the first two sites contains the `\"Ac\"` species, while the third and fourth sites contain the `\"Ag\"` and `\"Ir\"` species, respectively.", "nullable": true, - "x-optimade-queryable": "optional", - "x-optimade-support": "should" + "x-optimade-support": "should", + "x-optimade-queryable": "optional" }, "assemblies": { "title": "Assemblies", @@ -3729,8 +3731,8 @@ "$ref": "#/components/schemas/Assembly" }, "description": "A description of groups of sites that are statistically correlated.\n\n- **Type**: list of dictionary with keys:\n - `sites_in_groups`: list of list of integers (REQUIRED)\n - `group_probabilities`: list of floats (REQUIRED)\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - The property SHOULD be `null` for entries that have no partial occupancies.\n - If present, the correct flag MUST be set in the list `structure_features`.\n - Client implementations MUST check its presence (as its presence changes the interpretation of the structure).\n - If present, it MUST be a list of dictionaries, each of which represents an assembly and MUST have the following two keys:\n - **sites_in_groups**: Index of the sites (0-based) that belong to each group for each assembly.\n\n Example: `[[1], [2]]`: two groups, one with the second site, one with the third.\n Example: `[[1,2], [3]]`: one group with the second and third site, one with the fourth.\n\n - **group_probabilities**: Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\n It SHOULD sum to one.\n See below for examples of how to specify the probability of the occurrence of a vacancy.\n The possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.\n\n - If a site is not present in any group, it means that it is present with 100 % probability (as if no assembly was specified).\n - A site MUST NOT appear in more than one group.\n\n- **Examples** (for each entry of the assemblies list):\n - `{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}`: the first site and the second site never occur at the same time in the unit cell.\n Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.\n - `{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}`: the second and third site are either present together or not present; they form the first group of atoms for this assembly.\n The second group is formed by the fourth site.\n Sites of the first group (the second and the third) are never present at the same time as the fourth site.\n 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent).\n\n- **Notes**:\n - Assemblies are essential to represent, for instance, the situation where an atom can statistically occupy two different positions (sites).\n\n - By defining groups, it is possible to represent, e.g., the case where a functional molecule (and not just one atom) is either present or absent (or the case where it it is present in two conformations)\n\n - Considerations on virtual alloys and on vacancies: In the special case of a virtual alloy, these specifications allow two different, equivalent ways of specifying them.\n For instance, for a site at the origin with 30 % probability of being occupied by Si, 50 % probability of being occupied by Ge, and 20 % of being a vacancy, the following two representations are possible:\n\n - Using a single species:\n ```json\n {\n \"cartesian_site_positions\": [[0,0,0]],\n \"species_at_sites\": [\"SiGe-vac\"],\n \"species\": [\n {\n \"name\": \"SiGe-vac\",\n \"chemical_symbols\": [\"Si\", \"Ge\", \"vacancy\"],\n \"concentration\": [0.3, 0.5, 0.2]\n }\n ]\n // ...\n }\n ```\n\n - Using multiple species and the assemblies:\n ```json\n {\n \"cartesian_site_positions\": [ [0,0,0], [0,0,0], [0,0,0] ],\n \"species_at_sites\": [\"Si\", \"Ge\", \"vac\"],\n \"species\": [\n { \"name\": \"Si\", \"chemical_symbols\": [\"Si\"], \"concentration\": [1.0] },\n { \"name\": \"Ge\", \"chemical_symbols\": [\"Ge\"], \"concentration\": [1.0] },\n { \"name\": \"vac\", \"chemical_symbols\": [\"vacancy\"], \"concentration\": [1.0] }\n ],\n \"assemblies\": [\n {\n \"sites_in_groups\": [ [0], [1], [2] ],\n \"group_probabilities\": [0.3, 0.5, 0.2]\n }\n ]\n // ...\n }\n ```\n\n - It is up to the database provider to decide which representation to use, typically depending on the internal format in which the structure is stored.\n However, given a structure identified by a unique ID, the API implementation MUST always provide the same representation for it.\n\n - The probabilities of occurrence of different assemblies are uncorrelated.\n So, for instance in the following case with two assemblies:\n ```json\n {\n \"assemblies\": [\n {\n \"sites_in_groups\": [ [0], [1] ],\n \"group_probabilities\": [0.2, 0.8],\n },\n {\n \"sites_in_groups\": [ [2], [3] ],\n \"group_probabilities\": [0.3, 0.7]\n }\n ]\n }\n ```\n\n Site 0 is present with a probability of 20 % and site 1 with a probability of 80 %. These two sites are correlated (either site 0 or 1 is present). Similarly, site 2 is present with a probability of 30 % and site 3 with a probability of 70 %.\n These two sites are correlated (either site 2 or 3 is present).\n However, the presence or absence of sites 0 and 1 is not correlated with the presence or absence of sites 2 and 3 (in the specific example, the pair of sites (0, 2) can occur with 0.2*0.3 = 6 % probability; the pair (0, 3) with 0.2*0.7 = 14 % probability; the pair (1, 2) with 0.8*0.3 = 24 % probability; and the pair (1, 3) with 0.8*0.7 = 56 % probability).", - "x-optimade-queryable": "optional", - "x-optimade-support": "optional" + "x-optimade-support": "optional", + "x-optimade-queryable": "optional" }, "structure_features": { "title": "Structure Features", @@ -3739,8 +3741,8 @@ "$ref": "#/components/schemas/StructureFeatures" }, "description": "A list of strings that flag which special features are used by the structure.\n\n- **Type**: list of strings\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property.\n Filters on the list MUST support all mandatory HAS-type queries.\n Filter operators for comparisons on the string components MUST support equality, support for other comparison operators are OPTIONAL.\n - MUST be an empty list if no special features are used.\n - MUST be sorted alphabetically.\n - If a special feature listed below is used, the list MUST contain the corresponding string.\n - If a special feature listed below is not used, the list MUST NOT contain the corresponding string.\n - **List of strings used to indicate special structure features**:\n - `disorder`: this flag MUST be present if any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element.\n - `implicit_atoms`: this flag MUST be present if the structure contains atoms that are not assigned to sites via the property `species_at_sites` (e.g., because their positions are unknown).\n When this flag is present, the properties related to the chemical formula will likely not match the type and count of atoms represented by the `species_at_sites`, `species` and `assemblies` properties.\n - `site_attachments`: this flag MUST be present if any one entry in the `species` list includes `attached` and `nattached`.\n - `assemblies`: this flag MUST be present if the property `assemblies` is present.\n\n- **Examples**: A structure having implicit atoms and using assemblies: `[\"assemblies\", \"implicit_atoms\"]`", - "x-optimade-queryable": "must", - "x-optimade-support": "must" + "x-optimade-support": "must", + "x-optimade-queryable": "must" } }, "description": "This class contains the Field for the attributes used to represent a structure, e.g. unit cell, atoms, positions." diff --git a/optimade/models/entries.py b/optimade/models/entries.py index 5eca558c5..4b0a8d145 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -101,8 +101,14 @@ def cast_immutable_id_to_str(cls, value): class PartialDataLink(BaseModel): - link: AnyUrl - format: str # todo add check that the value of format is in a list of supported formats. + link: AnyUrl = OptimadeField( + ..., + description="String. A JSON API link that points to a location from which the omitted data can be fetched. There is no requirement on the syntax or format for the link URL.", + ) + format: str = OptimadeField( + ..., + description='String. The name of the format provided via this link. For one of the objects this format field SHOULD have the value "jsonlines", which refers to the format in OPTIMADE JSON lines partial data format.', + ) # todo add check that the value of format is in a list of supported formats. class EntryMetadata(Meta): From 281551e357d22a667c9fb9c11c819e0f150bc457 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Tue, 22 Aug 2023 12:31:35 +0200 Subject: [PATCH 54/86] Storing intermediate code on the way to implement partial data to get second opinion on behaviour mongomock vs pymogo. --- docs/api_reference/models/partial_data.md | 5 + docs/api_reference/models/partialdata.md | 5 - .../server/mappers/partial_data.md | 3 + .../server/routers/partial_data.md | 3 + openapi/index_openapi.json | 20 +- openapi/openapi.json | 419 ++++++++++++------ optimade/adapters/jsonl.py | 8 +- optimade/models/__init__.py | 2 + .../{partialdata.py => partial_data.py} | 8 +- optimade/models/responses.py | 8 + optimade/server/config.py | 6 +- optimade/server/data/__init__.py | 1 + .../mpf_551_cartesian_site_positions.json | 372 ++++++++++++++++ .../entry_collections/entry_collections.py | 22 +- optimade/server/entry_collections/mongo.py | 36 +- optimade/server/main.py | 28 +- optimade/server/mappers/__init__.py | 2 + optimade/server/mappers/partial_data.py | 9 + optimade/server/query_params.py | 78 +++- optimade/server/routers/partial_data.py | 36 ++ optimade/server/routers/utils.py | 68 ++- tests/models/test_partialdata.py | 4 +- tests/server/routers/test_partial_data.py | 15 + 23 files changed, 988 insertions(+), 170 deletions(-) create mode 100644 docs/api_reference/models/partial_data.md delete mode 100644 docs/api_reference/models/partialdata.md create mode 100644 docs/api_reference/server/mappers/partial_data.md create mode 100644 docs/api_reference/server/routers/partial_data.md rename optimade/models/{partialdata.py => partial_data.py} (97%) create mode 100644 optimade/server/data/mpf_551_cartesian_site_positions.json create mode 100644 optimade/server/mappers/partial_data.py create mode 100644 optimade/server/routers/partial_data.py create mode 100644 tests/server/routers/test_partial_data.py diff --git a/docs/api_reference/models/partial_data.md b/docs/api_reference/models/partial_data.md new file mode 100644 index 000000000..07200e8b8 --- /dev/null +++ b/docs/api_reference/models/partial_data.md @@ -0,0 +1,5 @@ +# partial_data + +::: optimade.models.partial_data + options: + show_if_no_docstring: true diff --git a/docs/api_reference/models/partialdata.md b/docs/api_reference/models/partialdata.md deleted file mode 100644 index 0665311ef..000000000 --- a/docs/api_reference/models/partialdata.md +++ /dev/null @@ -1,5 +0,0 @@ -# partialdata - -::: optimade.models.partialdata - options: - show_if_no_docstring: true diff --git a/docs/api_reference/server/mappers/partial_data.md b/docs/api_reference/server/mappers/partial_data.md new file mode 100644 index 000000000..fee81e882 --- /dev/null +++ b/docs/api_reference/server/mappers/partial_data.md @@ -0,0 +1,3 @@ +# partial_data + +::: optimade.server.mappers.partial_data diff --git a/docs/api_reference/server/routers/partial_data.md b/docs/api_reference/server/routers/partial_data.md new file mode 100644 index 000000000..c9e94cfa4 --- /dev/null +++ b/docs/api_reference/server/routers/partial_data.md @@ -0,0 +1,3 @@ +# partial_data + +::: optimade.server.routers.partial_data diff --git a/openapi/index_openapi.json b/openapi/index_openapi.json index 755227873..4ec0acd36 100644 --- a/openapi/index_openapi.json +++ b/openapi/index_openapi.json @@ -528,15 +528,15 @@ "title": "Id", "type": "string", "description": "An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`", - "x-optimade-support": "must", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "must" }, "type": { "title": "Type", "type": "string", "description": "The name of the type of an entry.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type `` and ID `` MUST be returned in response to a request for `//` under the versioned base URL.\n\n- **Example**: `\"structures\"`", - "x-optimade-support": "must", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "must" }, "links": { "title": "Links", @@ -588,16 +588,16 @@ "title": "Immutable Id", "type": "string", "description": "The entry's immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)", - "x-optimade-support": "optional", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "optional" }, "last_modified": { "title": "Last Modified", "type": "string", "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "format": "date-time", - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" } }, "description": "Contains key-value pairs representing the entry's properties." @@ -1137,8 +1137,8 @@ "title": "Id", "type": "string", "description": "An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`", - "x-optimade-support": "must", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "must" }, "type": { "title": "Type", diff --git a/openapi/openapi.json b/openapi/openapi.json index 0c7898c45..bffdeff8b 100644 --- a/openapi/openapi.json +++ b/openapi/openapi.json @@ -1309,6 +1309,171 @@ } } } + }, + "/partial_data/{entry_id}": { + "get": { + "tags": [ + "partial_data" + ], + "summary": "Get Partial Data", + "operationId": "get_partial_data_partial_data__entry_id__get", + "parameters": [ + { + "required": true, + "schema": { + "title": "Entry Id", + "type": "string" + }, + "name": "entry_id", + "in": "path" + }, + { + "description": "The output format requested (see section Response Format).\nDefaults to the format string 'json', which specifies the standard output format described in this specification.\nExample: `http://example.com/v1/structures?response_format=xml`", + "required": false, + "schema": { + "title": "Response Format", + "type": "string", + "description": "The output format requested (see section Response Format).\nDefaults to the format string 'json', which specifies the standard output format described in this specification.\nExample: `http://example.com/v1/structures?response_format=xml`", + "default": "json" + }, + "name": "response_format", + "in": "query" + }, + { + "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", + "required": false, + "schema": { + "title": "Email Address", + "type": "string", + "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", + "format": "email", + "default": "" + }, + "name": "email_address", + "in": "query" + }, + { + "description": "If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.", + "required": false, + "schema": { + "title": "Api Hint", + "pattern": "(v[0-9]+(\\.[0-9]+)?)?", + "type": "string", + "description": "If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.", + "default": "" + }, + "name": "api_hint", + "in": "query" + }, + { + "description": "A comma-delimited set of fields to be provided in the output.\nIf provided, these fields MUST be returned along with the REQUIRED fields.\nOther OPTIONAL fields MUST NOT be returned when this parameter is present.\nExample: `http://example.com/v1/structures?response_fields=last_modified,nsites`", + "required": false, + "schema": { + "title": "Response Fields", + "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", + "type": "string", + "description": "A comma-delimited set of fields to be provided in the output.\nIf provided, these fields MUST be returned along with the REQUIRED fields.\nOther OPTIONAL fields MUST NOT be returned when this parameter is present.\nExample: `http://example.com/v1/structures?response_fields=last_modified,nsites`", + "default": "" + }, + "name": "response_fields", + "in": "query" + }, + { + "description": "A filter string, in the format described in section API Filtering Format Specification of the specification.", + "required": false, + "schema": { + "title": "Filter", + "type": "string", + "description": "A filter string, in the format described in section API Filtering Format Specification of the specification.", + "default": "" + }, + "name": "filter", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/vnd.api+json": { + "schema": { + "title": "Response Get Partial Data Partial Data Entry Id Get" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "501": { + "description": "Not Implemented", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "553": { + "description": "Version Not Supported", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } } }, "components": { @@ -1341,8 +1506,8 @@ } }, "description": "Index of the sites (0-based) that belong to each group for each assembly.\n\n- **Examples**:\n - `[[1], [2]]`: two groups, one with the second site, one with the third.\n - `[[1,2], [3]]`: one group with the second and third site, one with the fourth.", - "x-optimade-support": "must", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "must" }, "group_probabilities": { "title": "Group Probabilities", @@ -1351,8 +1516,8 @@ "type": "number" }, "description": "Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\nIt SHOULD sum to one.\nSee below for examples of how to specify the probability of the occurrence of a vacancy.\nThe possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.", - "x-optimade-support": "must", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "must" } }, "description": "A description of groups of sites that are statistically correlated.\n\n- **Examples** (for each entry of the assemblies list):\n - `{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}`: the first site and the second site never occur at the same time in the unit cell.\n Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.\n - `{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}`: the second and third site are either present together or not present; they form the first group of atoms for this assembly.\n The second group is formed by the fourth site. Sites of the first group (the second and the third) are never present at the same time as the fourth site.\n 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent)." @@ -1778,15 +1943,15 @@ "title": "Id", "type": "string", "description": "An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`", - "x-optimade-support": "must", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "must" }, "type": { "title": "Type", "type": "string", "description": "The name of the type of an entry.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type `` and ID `` MUST be returned in response to a request for `//` under the versioned base URL.\n\n- **Example**: `\"structures\"`", - "x-optimade-support": "must", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "must" }, "links": { "title": "Links", @@ -1838,16 +2003,16 @@ "title": "Immutable Id", "type": "string", "description": "The entry's immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)", - "x-optimade-support": "optional", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "optional" }, "last_modified": { "title": "Last Modified", "type": "string", "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "format": "date-time", - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" } }, "description": "Contains key-value pairs representing the entry's properties." @@ -2246,8 +2411,8 @@ "title": "Id", "type": "string", "description": "An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`", - "x-optimade-support": "must", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "must" }, "type": { "title": "Type", @@ -2568,22 +2733,22 @@ "title": "Name", "type": "string", "description": "Full name of the person, REQUIRED.", - "x-optimade-support": "must", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "must" }, "firstname": { "title": "Firstname", "type": "string", "description": "First name of the person.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "lastname": { "title": "Lastname", "type": "string", "description": "Last name of the person.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" } }, "description": "A person, i.e., an author, editor or other." @@ -2685,8 +2850,8 @@ "title": "Id", "type": "string", "description": "An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`", - "x-optimade-support": "must", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "must" }, "type": { "title": "Type", @@ -2694,8 +2859,8 @@ "type": "string", "description": "The name of the type of an entry.\n- **Type**: string.\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type and ID MUST be returned in response to a request for `//` under the versioned base URL.\n- **Example**: `\"structures\"`", "default": "references", - "x-optimade-support": "must", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "must" }, "links": { "title": "Links", @@ -2741,16 +2906,16 @@ "title": "Immutable Id", "type": "string", "description": "The entry's immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)", - "x-optimade-support": "optional", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "optional" }, "last_modified": { "title": "Last Modified", "type": "string", "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "format": "date-time", - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" }, "authors": { "title": "Authors", @@ -2759,8 +2924,8 @@ "$ref": "#/components/schemas/Person" }, "description": "List of person objects containing the authors of the reference.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "editors": { "title": "Editors", @@ -2769,15 +2934,15 @@ "$ref": "#/components/schemas/Person" }, "description": "List of person objects containing the editors of the reference.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "doi": { "title": "Doi", "type": "string", "description": "The digital object identifier of the reference.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "url": { "title": "Url", @@ -2793,155 +2958,155 @@ "title": "Address", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "annote": { "title": "Annote", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "booktitle": { "title": "Booktitle", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "chapter": { "title": "Chapter", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "crossref": { "title": "Crossref", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "edition": { "title": "Edition", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "howpublished": { "title": "Howpublished", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "institution": { "title": "Institution", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "journal": { "title": "Journal", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "key": { "title": "Key", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "month": { "title": "Month", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "note": { "title": "Note", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "number": { "title": "Number", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "organization": { "title": "Organization", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "pages": { "title": "Pages", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "publisher": { "title": "Publisher", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "school": { "title": "School", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "series": { "title": "Series", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "title": { "title": "Title", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "bib_type": { "title": "Bib Type", "type": "string", "description": "Type of the reference, corresponding to the **type** property in the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "volume": { "title": "Volume", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "year": { "title": "Year", "type": "string", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" } }, "description": "Model that stores the attributes of a reference.\n\nMany properties match the meaning described in the\n[BibTeX specification](http://bibtexml.sourceforge.net/btxdoc.pdf)." @@ -3363,8 +3528,8 @@ "title": "Name", "type": "string", "description": "Gives the name of the species; the **name** value MUST be unique in the `species` list.", - "x-optimade-support": "must", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "must" }, "chemical_symbols": { "title": "Chemical Symbols", @@ -3373,8 +3538,8 @@ "type": "string" }, "description": "MUST be a list of strings of all chemical elements composing this species. Each item of the list MUST be one of the following:\n\n- a valid chemical-element symbol, or\n- the special value `\"X\"` to represent a non-chemical element, or\n- the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\n\nIf any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.", - "x-optimade-support": "must", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "must" }, "concentration": { "title": "Concentration", @@ -3383,8 +3548,8 @@ "type": "number" }, "description": "MUST be a list of floats, with same length as `chemical_symbols`. The numbers represent the relative concentration of the corresponding chemical symbol in this species. The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\n\n- Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\n- Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\n\nNote that concentrations are uncorrelated between different site (even of the same species).", - "x-optimade-support": "must", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "must" }, "mass": { "title": "Mass", @@ -3393,16 +3558,16 @@ "type": "number" }, "description": "If present MUST be a list of floats expressed in a.m.u.\nElements denoting vacancies MUST have masses equal to 0.", - "x-optimade-support": "optional", "x-optimade-queryable": "optional", + "x-optimade-support": "optional", "x-optimade-unit": "a.m.u." }, "original_name": { "title": "Original Name", "type": "string", "description": "Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\n\nNote: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "attached": { "title": "Attached", @@ -3411,8 +3576,8 @@ "type": "string" }, "description": "If provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "nattached": { "title": "Nattached", @@ -3421,8 +3586,8 @@ "type": "integer" }, "description": "If provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the :field:`attached` key.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" } }, "description": "A list describing the species of the sites of this structure.\n\nSpecies can represent pure chemical elements, virtual-crystal atoms representing a\nstatistical occupation of a given site by multiple chemical elements, and/or a\nlocation to which there are attached atoms, i.e., atoms whose precise location are\nunknown beyond that they are attached to that position (frequently used to indicate\nhydrogen atoms attached to another element, e.g., a carbon with three attached\nhydrogens might represent a methyl group, -CH3).\n\n- **Examples**:\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]`: any site with this species is occupied by a Ti atom.\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]`: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.\n - `[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]`: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.\n - `[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]`: any site with this species is occupied by a carbon isotope with mass 12.\n - `[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]`: any site with this species is occupied by a carbon isotope with mass 13.\n - `[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]`: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms." @@ -3491,8 +3656,8 @@ "title": "Id", "type": "string", "description": "An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`", - "x-optimade-support": "must", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "must" }, "type": { "title": "Type", @@ -3500,8 +3665,8 @@ "type": "string", "description": "The name of the type of an entry.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type `` and ID `` MUST be returned in response to a request for `//` under the versioned base URL.\n\n- **Examples**:\n - `\"structures\"`", "default": "structures", - "x-optimade-support": "must", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "must" }, "links": { "title": "Links", @@ -3561,8 +3726,8 @@ "title": "Immutable Id", "type": "string", "description": "The entry's immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)", - "x-optimade-support": "optional", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "optional" }, "last_modified": { "title": "Last Modified", @@ -3570,8 +3735,8 @@ "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "format": "date-time", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" }, "elements": { "title": "Elements", @@ -3581,16 +3746,16 @@ }, "description": "The chemical symbols of the different elements present in the structure.\n\n- **Type**: list of strings.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The strings are the chemical symbols, i.e., either a single uppercase letter or an uppercase letter followed by a number of lowercase letters.\n - The order MUST be alphabetical.\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements_ratios`, if the latter is provided.\n - Note: This property SHOULD NOT contain the string \"X\" to indicate non-chemical elements or \"vacancy\" to indicate vacancies (in contrast to the field `chemical_symbols` for the `species` property).\n\n- **Examples**:\n - `[\"Si\"]`\n - `[\"Al\",\"O\",\"Si\"]`\n\n- **Query examples**:\n - A filter that matches all records of structures that contain Si, Al **and** O, and possibly other elements: `elements HAS ALL \"Si\", \"Al\", \"O\"`.\n - To match structures with exactly these three elements, use `elements HAS ALL \"Si\", \"Al\", \"O\" AND elements LENGTH 3`.\n - Note: length queries on this property can be equivalently formulated by filtering on the `nelements`_ property directly.", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" }, "nelements": { "title": "Nelements", "type": "integer", "description": "Number of different elements in the structure as an integer.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - MUST be equal to the lengths of the list properties `elements` and `elements_ratios`, if they are provided.\n\n- **Examples**:\n - `3`\n\n- **Querying**:\n - Note: queries on this property can equivalently be formulated using `elements LENGTH`.\n - A filter that matches structures that have exactly 4 elements: `nelements=4`.\n - A filter that matches structures that have between 2 and 7 elements: `nelements>=2 AND nelements<=7`.", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" }, "elements_ratios": { "title": "Elements Ratios", @@ -3600,16 +3765,16 @@ }, "description": "Relative proportions of different elements in the structure.\n\n- **Type**: list of floats\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - Composed by the proportions of elements in the structure as a list of floating point numbers.\n - The sum of the numbers MUST be 1.0 (within floating point accuracy)\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements`, if the latter is provided.\n\n- **Examples**:\n - `[1.0]`\n - `[0.3333333333333333, 0.2222222222222222, 0.4444444444444444]`\n\n- **Query examples**:\n - Note: Useful filters can be formulated using the set operator syntax for correlated values.\n However, since the values are floating point values, the use of equality comparisons is generally inadvisable.\n - OPTIONAL: a filter that matches structures where approximately 1/3 of the atoms in the structure are the element Al is: `elements:elements_ratios HAS ALL \"Al\":>0.3333, \"Al\":<0.3334`.", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" }, "chemical_formula_descriptive": { "title": "Chemical Formula Descriptive", "type": "string", "description": "The chemical formula for a structure as a string in a form chosen by the API implementation.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The chemical formula is given as a string consisting of properly capitalized element symbols followed by integers or decimal numbers, balanced parentheses, square, and curly brackets `(`,`)`, `[`,`]`, `{`, `}`, commas, the `+`, `-`, `:` and `=` symbols. The parentheses are allowed to be followed by a number. Spaces are allowed anywhere except within chemical symbols. The order of elements and any groupings indicated by parentheses or brackets are chosen freely by the API implementation.\n - The string SHOULD be arithmetically consistent with the element ratios in the `chemical_formula_reduced` property.\n - It is RECOMMENDED, but not mandatory, that symbols, parentheses and brackets, if used, are used with the meanings prescribed by [IUPAC's Nomenclature of Organic Chemistry](https://www.qmul.ac.uk/sbcs/iupac/bibliog/blue.html).\n\n- **Examples**:\n - `\"(H2O)2 Na\"`\n - `\"NaCl\"`\n - `\"CaCO3\"`\n - `\"CCaO3\"`\n - `\"(CH3)3N+ - [CH2]2-OH = Me3N+ - CH2 - CH2OH\"`\n\n- **Query examples**:\n - Note: the free-form nature of this property is likely to make queries on it across different databases inconsistent.\n - A filter that matches an exactly given formula: `chemical_formula_descriptive=\"(H2O)2 Na\"`.\n - A filter that does a partial match: `chemical_formula_descriptive CONTAINS \"H2O\"`.", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" }, "chemical_formula_reduced": { "title": "Chemical Formula Reduced", @@ -3617,16 +3782,16 @@ "type": "string", "description": "The reduced chemical formula for a structure as a string with element symbols and integer chemical proportion numbers.\nThe proportion number MUST be omitted if it is 1.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property.\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\n Intricate queries on formula components are instead suggested to be formulated using set-type filter operators on the multi valued `elements` and `elements_ratios` properties.\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\n - Elements MUST be placed in alphabetical order, followed by their integer chemical proportion number.\n - For structures with no partial occupation, the chemical proportion numbers are the smallest integers for which the chemical proportion is exactly correct.\n - For structures with partial occupation, the chemical proportion numbers are integers that within reasonable approximation indicate the correct chemical proportions. The precise details of how to perform the rounding is chosen by the API implementation.\n - No spaces or separators are allowed.\n\n- **Examples**:\n - `\"H2NaO\"`\n - `\"ClNa\"`\n - `\"CCaO3\"`\n\n- **Query examples**:\n - A filter that matches an exactly given formula is `chemical_formula_reduced=\"H2NaO\"`.", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" }, "chemical_formula_hill": { "title": "Chemical Formula Hill", "pattern": "(^$)|^([A-Z][a-z]?([2-9]|[1-9]\\d+)?)+$", "type": "string", "description": "The chemical formula for a structure in [Hill form](https://dx.doi.org/10.1021/ja02046a005) with element symbols followed by integer chemical proportion numbers. The proportion number MUST be omitted if it is 1.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, only a subset of the filter features MAY be supported.\n - The overall scale factor of the chemical proportions is chosen such that the resulting values are integers that indicate the most chemically relevant unit of which the system is composed.\n For example, if the structure is a repeating unit cell with four hydrogens and four oxygens that represents two hydroperoxide molecules, `chemical_formula_hill` is `\"H2O2\"` (i.e., not `\"HO\"`, nor `\"H4O4\"`).\n - If the chemical insight needed to ascribe a Hill formula to the system is not present, the property MUST be handled as unset.\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\n - Elements MUST be placed in [Hill order](https://dx.doi.org/10.1021/ja02046a005), followed by their integer chemical proportion number.\n Hill order means: if carbon is present, it is placed first, and if also present, hydrogen is placed second.\n After that, all other elements are ordered alphabetically.\n If carbon is not present, all elements are ordered alphabetically.\n - If the system has sites with partial occupation and the total occupations of each element do not all sum up to integers, then the Hill formula SHOULD be handled as unset.\n - No spaces or separators are allowed.\n\n- **Examples**:\n - `\"H2O2\"`\n\n- **Query examples**:\n - A filter that matches an exactly given formula is `chemical_formula_hill=\"H2O2\"`.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "chemical_formula_anonymous": { "title": "Chemical Formula Anonymous", @@ -3634,8 +3799,8 @@ "type": "string", "description": "The anonymous formula is the `chemical_formula_reduced`, but where the elements are instead first ordered by their chemical proportion number, and then, in order left to right, replaced by anonymous symbols A, B, C, ..., Z, Aa, Ba, ..., Za, Ab, Bb, ... and so on.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property.\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\n\n- **Examples**:\n - `\"A2B\"`\n - `\"A42B42C16D12E10F9G5\"`\n\n- **Querying**:\n - A filter that matches an exactly given formula is `chemical_formula_anonymous=\"A2B\"`.", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" }, "dimension_types": { "title": "Dimension Types", @@ -3655,8 +3820,8 @@ "type": "integer", "description": "An integer specifying the number of periodic dimensions in the structure, equivalent to the number of non-zero entries in `dimension_types`.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The integer value MUST be between 0 and 3 inclusive and MUST be equal to the sum of the items in the `dimension_types` property.\n - This property only reflects the treatment of the lattice vectors provided for the structure, and not any physical interpretation of the dimensionality of its contents.\n\n- **Examples**:\n - `2` should be indicated in cases where `dimension_types` is any of `[1, 1, 0]`, `[1, 0, 1]`, `[0, 1, 1]`.\n\n- **Query examples**:\n - Match only structures with exactly 3 periodic dimensions: `nperiodic_dimensions=3`\n - Match all structures with 2 or fewer periodic dimensions: `nperiodic_dimensions<=2`", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" }, "lattice_vectors": { "title": "Lattice Vectors", @@ -3673,8 +3838,8 @@ }, "description": "The three lattice vectors in Cartesian coordinates, in \u00e5ngstr\u00f6m (\u00c5).\n\n- **Type**: list of list of floats or unknown values.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - MUST be a list of three vectors *a*, *b*, and *c*, where each of the vectors MUST BE a list of the vector's coordinates along the x, y, and z Cartesian coordinates.\n (Therefore, the first index runs over the three lattice vectors and the second index runs over the x, y, z Cartesian coordinates).\n - For databases that do not define an absolute Cartesian system (e.g., only defining the length and angles between vectors), the first lattice vector SHOULD be set along *x* and the second on the *xy*-plane.\n - MUST always contain three vectors of three coordinates each, independently of the elements of property `dimension_types`.\n The vectors SHOULD by convention be chosen so the determinant of the `lattice_vectors` matrix is different from zero.\n The vectors in the non-periodic directions have no significance beyond fulfilling these requirements.\n - The coordinates of the lattice vectors of non-periodic dimensions (i.e., those dimensions for which `dimension_types` is `0`) MAY be given as a list of all `null` values.\n If a lattice vector contains the value `null`, all coordinates of that lattice vector MUST be `null`.\n\n- **Examples**:\n - `[[4.0,0.0,0.0],[0.0,4.0,0.0],[0.0,1.0,4.0]]` represents a cell, where the first vector is `(4, 0, 0)`, i.e., a vector aligned along the `x` axis of length 4 \u00c5; the second vector is `(0, 4, 0)`; and the third vector is `(0, 1, 4)`.", "nullable": true, - "x-optimade-unit": "\u00c5", "x-optimade-support": "should", + "x-optimade-unit": "\u00c5", "x-optimade-queryable": "optional" }, "cartesian_site_positions": { @@ -3690,8 +3855,8 @@ }, "description": "Cartesian positions of each site in the structure.\nA site is usually used to describe positions of atoms; what atoms can be encountered at a given site is conveyed by the `species_at_sites` property, and the species themselves are described in the `species` property.\n\n- **Type**: list of list of floats\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - It MUST be a list of length equal to the number of sites in the structure, where every element is a list of the three Cartesian coordinates of a site expressed as float values in the unit angstrom (\u00c5).\n - An entry MAY have multiple sites at the same Cartesian position (for a relevant use of this, see e.g., the property `assemblies`).\n\n- **Examples**:\n - `[[0,0,0],[0,0,2]]` indicates a structure with two sites, one sitting at the origin and one along the (positive) *z*-axis, 2 \u00c5 away from the origin.", "nullable": true, - "x-optimade-unit": "\u00c5", "x-optimade-support": "should", + "x-optimade-unit": "\u00c5", "x-optimade-queryable": "optional" }, "nsites": { @@ -3699,8 +3864,8 @@ "type": "integer", "description": "An integer specifying the length of the `cartesian_site_positions` property.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `42`\n\n- **Query examples**:\n - Match only structures with exactly 4 sites: `nsites=4`\n - Match structures that have between 2 and 7 sites: `nsites>=2 AND nsites<=7`", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" }, "species": { "title": "Species", @@ -3710,8 +3875,8 @@ }, "description": "A list describing the species of the sites of this structure.\nSpecies can represent pure chemical elements, virtual-crystal atoms representing a statistical occupation of a given site by multiple chemical elements, and/or a location to which there are attached atoms, i.e., atoms whose precise location are unknown beyond that they are attached to that position (frequently used to indicate hydrogen atoms attached to another element, e.g., a carbon with three attached hydrogens might represent a methyl group, -CH3).\n\n- **Type**: list of dictionary with keys:\n - `name`: string (REQUIRED)\n - `chemical_symbols`: list of strings (REQUIRED)\n - `concentration`: list of float (REQUIRED)\n - `attached`: list of strings (REQUIRED)\n - `nattached`: list of integers (OPTIONAL)\n - `mass`: list of floats (OPTIONAL)\n - `original_name`: string (OPTIONAL).\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - Each list member MUST be a dictionary with the following keys:\n - **name**: REQUIRED; gives the name of the species; the **name** value MUST be unique in the `species` list;\n - **chemical_symbols**: REQUIRED; MUST be a list of strings of all chemical elements composing this species.\n Each item of the list MUST be one of the following:\n - a valid chemical-element symbol, or\n - the special value `\"X\"` to represent a non-chemical element, or\n - the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\n\n If any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.\n\n - **concentration**: REQUIRED; MUST be a list of floats, with same length as `chemical_symbols`.\n The numbers represent the relative concentration of the corresponding chemical symbol in this species.\n The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\n\n - Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\n - Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\n\n Note that concentrations are uncorrelated between different sites (even of the same species).\n\n - **attached**: OPTIONAL; if provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.\n\n - **nattached**: OPTIONAL; if provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the `attached` key.\n\n The implementation MUST include either both or none of the `attached` and `nattached` keys, and if they are provided, they MUST be of the same length.\n Furthermore, if they are provided, the `structure_features` property MUST include the string `site_attachments`.\n\n - **mass**: OPTIONAL. If present MUST be a list of floats, with the same length as `chemical_symbols`, providing element masses expressed in a.m.u.\n Elements denoting vacancies MUST have masses equal to 0.\n\n - **original_name**: OPTIONAL. Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\n\n Note: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.\n\n The main use of this field is for source databases that use species names, containing characters that are not allowed (see description of the list property `species_at_sites`).\n\n - For systems that have only species formed by a single chemical symbol, and that have at most one species per chemical symbol, SHOULD use the chemical symbol as species name (e.g., `\"Ti\"` for titanium, `\"O\"` for oxygen, etc.)\n However, note that this is OPTIONAL, and client implementations MUST NOT assume that the key corresponds to a chemical symbol, nor assume that if the species name is a valid chemical symbol, that it represents a species with that chemical symbol.\n This means that a species `{\"name\": \"C\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]}` is valid and represents a titanium species (and *not* a carbon species).\n - It is NOT RECOMMENDED that a structure includes species that do not have at least one corresponding site.\n\n- **Examples**:\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]`: any site with this species is occupied by a Ti atom.\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]`: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.\n - `[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]`: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.\n - `[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]`: any site with this species is occupied by a carbon isotope with mass 12.\n - `[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]`: any site with this species is occupied by a carbon isotope with mass 13.\n - `[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]`: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms.", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "should" }, "species_at_sites": { "title": "Species At Sites", @@ -3721,8 +3886,8 @@ }, "description": "Name of the species at each site (where values for sites are specified with the same order of the property `cartesian_site_positions`).\nThe properties of the species are found in the property `species`.\n\n- **Type**: list of strings.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - MUST have length equal to the number of sites in the structure (first dimension of the list property `cartesian_site_positions`).\n - Each species name mentioned in the `species_at_sites` list MUST be described in the list property `species` (i.e. for each value in the `species_at_sites` list there MUST exist exactly one dictionary in the `species` list with the `name` attribute equal to the corresponding `species_at_sites` value).\n - Each site MUST be associated only to a single species.\n **Note**: However, species can represent mixtures of atoms, and multiple species MAY be defined for the same chemical element.\n This latter case is useful when different atoms of the same type need to be grouped or distinguished, for instance in simulation codes to assign different initial spin states.\n\n- **Examples**:\n - `[\"Ti\",\"O2\"]` indicates that the first site is hosting a species labeled `\"Ti\"` and the second a species labeled `\"O2\"`.\n - `[\"Ac\", \"Ac\", \"Ag\", \"Ir\"]` indicating the first two sites contains the `\"Ac\"` species, while the third and fourth sites contain the `\"Ag\"` and `\"Ir\"` species, respectively.", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "should" }, "assemblies": { "title": "Assemblies", @@ -3731,8 +3896,8 @@ "$ref": "#/components/schemas/Assembly" }, "description": "A description of groups of sites that are statistically correlated.\n\n- **Type**: list of dictionary with keys:\n - `sites_in_groups`: list of list of integers (REQUIRED)\n - `group_probabilities`: list of floats (REQUIRED)\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - The property SHOULD be `null` for entries that have no partial occupancies.\n - If present, the correct flag MUST be set in the list `structure_features`.\n - Client implementations MUST check its presence (as its presence changes the interpretation of the structure).\n - If present, it MUST be a list of dictionaries, each of which represents an assembly and MUST have the following two keys:\n - **sites_in_groups**: Index of the sites (0-based) that belong to each group for each assembly.\n\n Example: `[[1], [2]]`: two groups, one with the second site, one with the third.\n Example: `[[1,2], [3]]`: one group with the second and third site, one with the fourth.\n\n - **group_probabilities**: Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\n It SHOULD sum to one.\n See below for examples of how to specify the probability of the occurrence of a vacancy.\n The possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.\n\n - If a site is not present in any group, it means that it is present with 100 % probability (as if no assembly was specified).\n - A site MUST NOT appear in more than one group.\n\n- **Examples** (for each entry of the assemblies list):\n - `{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}`: the first site and the second site never occur at the same time in the unit cell.\n Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.\n - `{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}`: the second and third site are either present together or not present; they form the first group of atoms for this assembly.\n The second group is formed by the fourth site.\n Sites of the first group (the second and the third) are never present at the same time as the fourth site.\n 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent).\n\n- **Notes**:\n - Assemblies are essential to represent, for instance, the situation where an atom can statistically occupy two different positions (sites).\n\n - By defining groups, it is possible to represent, e.g., the case where a functional molecule (and not just one atom) is either present or absent (or the case where it it is present in two conformations)\n\n - Considerations on virtual alloys and on vacancies: In the special case of a virtual alloy, these specifications allow two different, equivalent ways of specifying them.\n For instance, for a site at the origin with 30 % probability of being occupied by Si, 50 % probability of being occupied by Ge, and 20 % of being a vacancy, the following two representations are possible:\n\n - Using a single species:\n ```json\n {\n \"cartesian_site_positions\": [[0,0,0]],\n \"species_at_sites\": [\"SiGe-vac\"],\n \"species\": [\n {\n \"name\": \"SiGe-vac\",\n \"chemical_symbols\": [\"Si\", \"Ge\", \"vacancy\"],\n \"concentration\": [0.3, 0.5, 0.2]\n }\n ]\n // ...\n }\n ```\n\n - Using multiple species and the assemblies:\n ```json\n {\n \"cartesian_site_positions\": [ [0,0,0], [0,0,0], [0,0,0] ],\n \"species_at_sites\": [\"Si\", \"Ge\", \"vac\"],\n \"species\": [\n { \"name\": \"Si\", \"chemical_symbols\": [\"Si\"], \"concentration\": [1.0] },\n { \"name\": \"Ge\", \"chemical_symbols\": [\"Ge\"], \"concentration\": [1.0] },\n { \"name\": \"vac\", \"chemical_symbols\": [\"vacancy\"], \"concentration\": [1.0] }\n ],\n \"assemblies\": [\n {\n \"sites_in_groups\": [ [0], [1], [2] ],\n \"group_probabilities\": [0.3, 0.5, 0.2]\n }\n ]\n // ...\n }\n ```\n\n - It is up to the database provider to decide which representation to use, typically depending on the internal format in which the structure is stored.\n However, given a structure identified by a unique ID, the API implementation MUST always provide the same representation for it.\n\n - The probabilities of occurrence of different assemblies are uncorrelated.\n So, for instance in the following case with two assemblies:\n ```json\n {\n \"assemblies\": [\n {\n \"sites_in_groups\": [ [0], [1] ],\n \"group_probabilities\": [0.2, 0.8],\n },\n {\n \"sites_in_groups\": [ [2], [3] ],\n \"group_probabilities\": [0.3, 0.7]\n }\n ]\n }\n ```\n\n Site 0 is present with a probability of 20 % and site 1 with a probability of 80 %. These two sites are correlated (either site 0 or 1 is present). Similarly, site 2 is present with a probability of 30 % and site 3 with a probability of 70 %.\n These two sites are correlated (either site 2 or 3 is present).\n However, the presence or absence of sites 0 and 1 is not correlated with the presence or absence of sites 2 and 3 (in the specific example, the pair of sites (0, 2) can occur with 0.2*0.3 = 6 % probability; the pair (0, 3) with 0.2*0.7 = 14 % probability; the pair (1, 2) with 0.8*0.3 = 24 % probability; and the pair (1, 3) with 0.8*0.7 = 56 % probability).", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "structure_features": { "title": "Structure Features", @@ -3741,8 +3906,8 @@ "$ref": "#/components/schemas/StructureFeatures" }, "description": "A list of strings that flag which special features are used by the structure.\n\n- **Type**: list of strings\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property.\n Filters on the list MUST support all mandatory HAS-type queries.\n Filter operators for comparisons on the string components MUST support equality, support for other comparison operators are OPTIONAL.\n - MUST be an empty list if no special features are used.\n - MUST be sorted alphabetically.\n - If a special feature listed below is used, the list MUST contain the corresponding string.\n - If a special feature listed below is not used, the list MUST NOT contain the corresponding string.\n - **List of strings used to indicate special structure features**:\n - `disorder`: this flag MUST be present if any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element.\n - `implicit_atoms`: this flag MUST be present if the structure contains atoms that are not assigned to sites via the property `species_at_sites` (e.g., because their positions are unknown).\n When this flag is present, the properties related to the chemical formula will likely not match the type and count of atoms represented by the `species_at_sites`, `species` and `assemblies` properties.\n - `site_attachments`: this flag MUST be present if any one entry in the `species` list includes `attached` and `nattached`.\n - `assemblies`: this flag MUST be present if the property `assemblies` is present.\n\n- **Examples**: A structure having implicit atoms and using assemblies: `[\"assemblies\", \"implicit_atoms\"]`", - "x-optimade-support": "must", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "must" } }, "description": "This class contains the Field for the attributes used to represent a structure, e.g. unit cell, atoms, positions." diff --git a/optimade/adapters/jsonl.py b/optimade/adapters/jsonl.py index 60979192c..9f7bfac21 100644 --- a/optimade/adapters/jsonl.py +++ b/optimade/adapters/jsonl.py @@ -4,14 +4,14 @@ from jsonlines import Reader, Writer -from optimade.models.partialdata import PartialDataResponse +from optimade.models.partial_data import PartialDataResource -def to_jsonl(input_data: Union[list[dict], PartialDataResponse]) -> bytes: +def to_jsonl(input_data: Union[list[dict], PartialDataResource]) -> bytes: """This function convert a list of dictionaries to the JSONL format which can be sent back in an OPTIMADE partial data response""" temp_file = BytesIO() writer = Writer(temp_file) - if isinstance(input_data, PartialDataResponse): + if isinstance(input_data, PartialDataResource): writer.write(input_data.header) input_data = input_data.data if isinstance(input_data, list): @@ -26,7 +26,7 @@ def to_jsonl(input_data: Union[list[dict], PartialDataResponse]) -> bytes: def from_jsonl( jsonl_input: Union[Path, str, bytes] -) -> Union[list, PartialDataResponse]: +) -> Union[list, PartialDataResource]: if isinstance(jsonl_input, (Path, str)): fp: Union[BytesIO, BufferedReader] = open(jsonl_input, "rb") else: diff --git a/optimade/models/__init__.py b/optimade/models/__init__.py index 018560412..af5814317 100644 --- a/optimade/models/__init__.py +++ b/optimade/models/__init__.py @@ -5,6 +5,7 @@ from .jsonapi import * # noqa: F403 from .links import * # noqa: F403 from .optimade_json import * # noqa: F403 +from .partial_data import * # noqa: F403 from .references import * # noqa: F403 from .responses import * # noqa: F403 from .structures import * # noqa: F403 @@ -18,6 +19,7 @@ + index_metadb.__all__ # type: ignore[name-defined] # noqa: F405 + links.__all__ # type: ignore[name-defined] # noqa: F405 + optimade_json.__all__ # type: ignore[name-defined] # noqa: F405 + + partial_data.__all__ # type: ignore[name-defined] # noqa: F405 + references.__all__ # type: ignore[name-defined] # noqa: F405 + responses.__all__ # type: ignore[name-defined] # noqa: F405 + structures.__all__ # type: ignore[name-defined] # noqa: F405 diff --git a/optimade/models/partialdata.py b/optimade/models/partial_data.py similarity index 97% rename from optimade/models/partialdata.py rename to optimade/models/partial_data.py index 1af84fedd..610640359 100644 --- a/optimade/models/partialdata.py +++ b/optimade/models/partial_data.py @@ -2,8 +2,14 @@ from pydantic import BaseModel +from optimade.models.entries import EntryResource from optimade.models.utils import OptimadeField, SupportLevel +__all__ = ( + "PartialDataHeader", + "PartialDataResource", +) + class PartialDataHeader(BaseModel): optimade_partial_data: dict = OptimadeField( @@ -119,6 +125,6 @@ class PartialDataHeader(BaseModel): ) -class PartialDataResponse(BaseModel): +class PartialDataResource(EntryResource): header: PartialDataHeader data: list diff --git a/optimade/models/responses.py b/optimade/models/responses.py index 01845dc82..abfcdee0d 100644 --- a/optimade/models/responses.py +++ b/optimade/models/responses.py @@ -9,6 +9,7 @@ from optimade.models.jsonapi import Response from optimade.models.links import LinksResource from optimade.models.optimade_json import OptimadeError, ResponseMeta, Success +from optimade.models.partial_data import PartialDataResource from optimade.models.references import ReferenceResource from optimade.models.structures import StructureResource from optimade.models.utils import StrictField @@ -25,6 +26,7 @@ "StructureResponseMany", "ReferenceResponseOne", "ReferenceResponseMany", + "PartialDataResponse", ) @@ -115,3 +117,9 @@ class ReferenceResponseMany(EntryResponseMany): description="List of unique OPTIMADE references entry resource objects.", uniqueItems=True, ) + + +class PartialDataResponse(EntryResponseOne): + data: Union[PartialDataResource, Dict[str, Any], None] = StrictField( + ..., description="(Part of) the data for a single property of an entry." + ) diff --git a/optimade/server/config.py b/optimade/server/config.py index bd090bb95..1346cb3c7 100644 --- a/optimade/server/config.py +++ b/optimade/server/config.py @@ -14,7 +14,7 @@ from pydantic.env_settings import SettingsSourceCallable from optimade import __api_version__, __version__ -from optimade.models import Implementation, Provider +from optimade.models import Implementation, Provider # type: ignore[attr-defined] DEFAULT_CONFIG_FILE_PATH: str = str(Path.home().joinpath(".optimade.json")) """Default configuration file path. @@ -172,6 +172,10 @@ class ServerConfig(BaseSettings): "structures", description="Mongo collection name for /structures endpoint resources", ) + partial_data_collection: str = Field( + "fs.files", + description="Mongo Grid FS system containing the data that needs to be returned via the partial data mechanism.", + ) page_limit: int = Field(20, description="Default number of resources per page") page_limit_max: int = Field( 500, description="Max allowed number of resources per page" diff --git a/optimade/server/data/__init__.py b/optimade/server/data/__init__.py index 87060d387..4b0c41087 100644 --- a/optimade/server/data/__init__.py +++ b/optimade/server/data/__init__.py @@ -10,6 +10,7 @@ "providers": "providers.json", } +data_files = ["mpf_551_cartesian_site_positions.json"] for var, path in data_paths.items(): try: diff --git a/optimade/server/data/mpf_551_cartesian_site_positions.json b/optimade/server/data/mpf_551_cartesian_site_positions.json new file mode 100644 index 000000000..d41a4b4f8 --- /dev/null +++ b/optimade/server/data/mpf_551_cartesian_site_positions.json @@ -0,0 +1,372 @@ +[ + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ], + [ + 0.449480176317956, + 0.449480176317956, + 0.449480176317956 + ] +] diff --git a/optimade/server/entry_collections/entry_collections.py b/optimade/server/entry_collections/entry_collections.py index 6153216ad..02a8dc02f 100644 --- a/optimade/server/entry_collections/entry_collections.py +++ b/optimade/server/entry_collections/entry_collections.py @@ -10,8 +10,12 @@ from optimade.filterparser import LarkParser from optimade.models.entries import EntryResource from optimade.server.config import CONFIG, SupportedBackend -from optimade.server.mappers import BaseResourceMapper -from optimade.server.query_params import EntryListingQueryParams, SingleEntryQueryParams +from optimade.server.mappers import BaseResourceMapper # type: ignore[attr-defined] +from optimade.server.query_params import ( + EntryListingQueryParams, + PartialDataQueryParams, + SingleEntryQueryParams, +) from optimade.warnings import ( FieldValueNotRecognized, QueryParamNotUsed, @@ -137,7 +141,7 @@ def count(self, **kwargs: Any) -> int: def find( self, params: Union[EntryListingQueryParams, SingleEntryQueryParams] - ) -> Tuple[Union[None, Dict, List[Dict]], int, bool, Set[str], Set[str],]: + ) -> Tuple[Union[None, Dict, List[Dict]], int, bool, Set[str], Set[str]]: """ Fetches results and indicates if more data is available. @@ -158,11 +162,14 @@ def find( """ criteria = self.handle_query_params(params) - single_entry = isinstance(params, SingleEntryQueryParams) + single_entry = isinstance( + params, (SingleEntryQueryParams, PartialDataQueryParams) + ) response_fields = criteria.pop("fields") + partial_data = isinstance(params, PartialDataQueryParams) raw_results, data_returned, more_data_available = self._run_db_query( - criteria, single_entry + criteria, single_entry, partial_data ) exclude_fields = self.all_fields - response_fields @@ -220,7 +227,10 @@ def find( @abstractmethod def _run_db_query( - self, criteria: Dict[str, Any], single_entry: bool = False + self, + criteria: Dict[str, Any], + single_entry: bool = False, + partial_data: bool = False, ) -> Tuple[List[Dict[str, Any]], int, bool]: """Run the query on the backend and collect the results. diff --git a/optimade/server/entry_collections/mongo.py b/optimade/server/entry_collections/mongo.py index 03a63bcce..dd442c6a6 100644 --- a/optimade/server/entry_collections/mongo.py +++ b/optimade/server/entry_collections/mongo.py @@ -1,12 +1,16 @@ from typing import Any, Dict, List, Tuple, Type, Union from optimade.filtertransformers.mongo import MongoTransformer -from optimade.models import EntryResource +from optimade.models import EntryResource # type: ignore[attr-defined] from optimade.server.config import CONFIG, SupportedBackend from optimade.server.entry_collections import EntryCollection from optimade.server.logger import LOGGER -from optimade.server.mappers import BaseResourceMapper -from optimade.server.query_params import EntryListingQueryParams, SingleEntryQueryParams +from optimade.server.mappers import BaseResourceMapper # type: ignore[attr-defined] +from optimade.server.query_params import ( + EntryListingQueryParams, + PartialDataQueryParams, + SingleEntryQueryParams, +) if CONFIG.database_backend.value == "mongodb": from pymongo import MongoClient, version_tuple @@ -20,9 +24,11 @@ LOGGER.info("Using: Real MongoDB (pymongo)") elif CONFIG.database_backend.value == "mongomock": + import mongomock.gridfs from mongomock import MongoClient LOGGER.info("Using: Mock MongoDB (mongomock)") + mongomock.gridfs.enable_gridfs_integration() if CONFIG.database_backend.value in ("mongomock", "mongodb"): CLIENT = MongoClient(CONFIG.mongo_uri) @@ -132,10 +138,19 @@ def handle_query_params( if criteria.get("projection", {}).get("_id"): criteria["projection"]["_id"] = {"$toString": "$_id"} + if isinstance(params, PartialDataQueryParams): + entry_id = params.filter.split("=")[1][1:-1] + criteria["filter"] = { + "filename": {"$eq": f"{entry_id}_{params.response_fields}"} + } # Todo make sure response fields has only one value + return criteria def _run_db_query( - self, criteria: Dict[str, Any], single_entry: bool = False + self, + criteria: Dict[str, Any], + single_entry: bool = False, + partial_data: bool = False, ) -> Tuple[List[Dict[str, Any]], int, bool]: """Run the query on the backend and collect the results. @@ -148,7 +163,18 @@ def _run_db_query( entries matching the query and a boolean for whether or not there is more data available. """ - results = list(self.collection.find(**criteria)) + if partial_data: + import gridfs + + fs = gridfs.GridFS(self.collection.database) + # file = fs.find({"filename": }) + for grid_out in fs.find( + {"filename": "mpf_551_cartesian_site_positions.json"} + ): + grid_out.read() + results = [] + else: + results = list(self.collection.find(**criteria)) if CONFIG.database_backend == SupportedBackend.MONGOMOCK and criteria.get( "projection", {} diff --git a/optimade/server/main.py b/optimade/server/main.py index 4f95dcccb..09c8a936d 100644 --- a/optimade/server/main.py +++ b/optimade/server/main.py @@ -25,6 +25,7 @@ info, landing, links, + partial_data, references, structures, versions, @@ -68,6 +69,29 @@ from optimade.server.routers import ENTRY_COLLECTIONS from optimade.server.routers.utils import get_providers + # Todo Do we need to check a file is not already stored in gridfs? + # Load test data from files into gridfs + if CONFIG.database_backend.value in ("mongomock", "mongodb"): + from pathlib import Path + + if CONFIG.database_backend.value == "mongodb": + from pymongo import MongoClient + elif CONFIG.database_backend.value == "mongomock": + import mongomock.gridfs + from mongomock import MongoClient + + mongomock.gridfs.enable_gridfs_integration() + import gridfs + + db = MongoClient(CONFIG.mongo_uri)[ + CONFIG.mongo_database + ] # Somehow importing the client from optimade.server.entry_collections.mongo gives an error that the type of db is not "Database" eventhough it is. + fs = gridfs.GridFS(db) + for filename in getattr(data, "data_files", []): + with open(Path(__file__).parent / "data" / filename, "rb") as f: + a = fs.put(f, filename=filename) + pass + def load_entries(endpoint_name: str, endpoint_collection: EntryCollection): LOGGER.debug("Loading test %s...", endpoint_name) @@ -103,7 +127,7 @@ def load_entries(endpoint_name: str, endpoint_collection: EntryCollection): app.add_exception_handler(exception, handler) # Add various endpoints to unversioned URL -for endpoint in (info, links, references, structures, landing, versions): +for endpoint in (info, links, references, structures, landing, versions, partial_data): app.include_router(endpoint.router) @@ -121,7 +145,7 @@ def add_optional_versioned_base_urls(app: FastAPI): ``` """ for version in ("minor", "patch"): - for endpoint in (info, links, references, structures, landing): + for endpoint in (info, links, references, structures, landing, partial_data): app.include_router(endpoint.router, prefix=BASE_URL_PREFIXES[version]) diff --git a/optimade/server/mappers/__init__.py b/optimade/server/mappers/__init__.py index c38e6ccd0..882abe95b 100644 --- a/optimade/server/mappers/__init__.py +++ b/optimade/server/mappers/__init__.py @@ -1,12 +1,14 @@ # pylint: disable=undefined-variable from .entries import * # noqa: F403 from .links import * # noqa: F403 +from .partial_data import * # noqa: F403 from .references import * # noqa: F403 from .structures import * # noqa: F403 __all__ = ( entries.__all__ # type: ignore[name-defined] # noqa: F405 + links.__all__ # type: ignore[name-defined] # noqa: F405 + + partial_data.__all__ # type: ignore[name-defined] # noqa: F405 + references.__all__ # type: ignore[name-defined] # noqa: F405 + structures.__all__ # type: ignore[name-defined] # noqa: F405 ) diff --git a/optimade/server/mappers/partial_data.py b/optimade/server/mappers/partial_data.py new file mode 100644 index 000000000..c959c6eae --- /dev/null +++ b/optimade/server/mappers/partial_data.py @@ -0,0 +1,9 @@ +from optimade.models.partial_data import PartialDataResource +from optimade.server.mappers.entries import BaseResourceMapper + +__all__ = ("PartialDataMapper",) + + +class PartialDataMapper(BaseResourceMapper): + LENGTH_ALIASES = () + ENTRY_RESOURCE_CLASS = PartialDataResource diff --git a/optimade/server/query_params.py b/optimade/server/query_params.py index c348bc5f3..d52d640a5 100644 --- a/optimade/server/query_params.py +++ b/optimade/server/query_params.py @@ -7,7 +7,7 @@ from optimade.exceptions import BadRequest from optimade.server.config import CONFIG -from optimade.server.mappers import BaseResourceMapper +from optimade.server.mappers import BaseResourceMapper # type: ignore[attr-defined] from optimade.warnings import QueryParamNotUsed, UnknownProviderQueryParameter @@ -330,3 +330,79 @@ def __init__( self.response_fields = response_fields self.include = include self.api_hint = api_hint + + +class PartialDataQueryParams(BaseQueryParams): + """ + Common query params for single entry endpoints. + + Attributes: + response_format (str): The output format requested (see section Response Format). + Defaults to the format string 'json', which specifies the standard output format described in this specification. + + **Example**: `http://example.com/v1/structures?response_format=xml` + + email_address (EmailStr): An email address of the user making the request. + The email SHOULD be that of a person and not an automatic system. + + **Example**: `http://example.com/v1/structures?email_address=user@example.com` + + response_fields (str): A comma-delimited set of fields to be provided in the output. + If provided, these fields MUST be returned along with the REQUIRED fields. + Other OPTIONAL fields MUST NOT be returned when this parameter is present. + + **Example**: `http://example.com/v1/structures?response_fields=last_modified,nsites` + + include (str): A server MAY implement the JSON API concept of returning [compound documents](https://jsonapi.org/format/1.0/#document-compound-documents) + by utilizing the `include` query parameter as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-includes). + + All related resource objects MUST be returned as part of an array value for the top-level `included` field, + see the section JSON Response Schema: Common Fields. + + The value of `include` MUST be a comma-separated list of "relationship paths", as defined in the [JSON API](https://jsonapi.org/format/1.0/#fetching-includes). + If relationship paths are not supported, or a server is unable to identify a relationship path a `400 Bad Request` response MUST be made. + + The **default value** for `include` is `references`. This means `references` entries MUST always be included under the top-level field + `included` as default, since a server assumes if `include` is not specified by a client in the request, it is still specified as `include=references`. + Note, if a client explicitly specifies `include` and leaves out `references`, `references` resource objects MUST NOT be included under the top-level + field `included`, as per the definition of `included`, see section JSON Response Schema: Common Fields. + + **Note**: A query with the parameter `include` set to the empty string means no related resource objects are to be returned under the top-level field `included`. + + api_hint (str): If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, + where MAJOR is a major version and MINOR is a minor version of the API. + For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0. + + """ + + def __init__( + self, + *, + response_format: str = Query( + "json", + description="The output format requested (see section Response Format).\nDefaults to the format string 'json', which specifies the standard output format described in this specification.\nExample: `http://example.com/v1/structures?response_format=xml`", + ), + email_address: EmailStr = Query( + "", + description="An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", + ), + api_hint: str = Query( + "", + description="If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.", + regex=r"(v[0-9]+(\.[0-9]+)?)?", + ), + response_fields: str = Query( + "", + description="A comma-delimited set of fields to be provided in the output.\nIf provided, these fields MUST be returned along with the REQUIRED fields.\nOther OPTIONAL fields MUST NOT be returned when this parameter is present.\nExample: `http://example.com/v1/structures?response_fields=last_modified,nsites`", + regex=r"([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", + ), + filter: str = Query( # pylint: disable=redefined-builtin + "", + description="A filter string, in the format described in section API Filtering Format Specification of the specification.", + ), + ): + self.filter = filter + self.response_format = response_format + self.email_address = email_address + self.response_fields = response_fields + self.api_hint = api_hint diff --git a/optimade/server/routers/partial_data.py b/optimade/server/routers/partial_data.py new file mode 100644 index 000000000..5a9a59436 --- /dev/null +++ b/optimade/server/routers/partial_data.py @@ -0,0 +1,36 @@ +from typing import Any + +from fastapi import APIRouter, Depends, Request + +from optimade.models import PartialDataResource # type: ignore[attr-defined] +from optimade.server.config import CONFIG +from optimade.server.entry_collections import create_collection +from optimade.server.mappers import PartialDataMapper +from optimade.server.query_params import PartialDataQueryParams +from optimade.server.routers.utils import get_partial_entry +from optimade.server.schemas import ERROR_RESPONSES + +router = APIRouter(redirect_slashes=True) + +partial_data_coll = create_collection( + name=CONFIG.partial_data_collection, + resource_cls=PartialDataResource, + resource_mapper=PartialDataMapper, +) + + +@router.get( + "/partial_data/{entry_id:path}", + response_model_exclude_unset=True, + tags=["partial_data"], + responses=ERROR_RESPONSES, +) +def get_partial_data( + request: Request, entry_id: str, params: PartialDataQueryParams = Depends() +) -> Any: + return get_partial_entry( + collection=partial_data_coll, + entry_id=entry_id, + request=request, + params=params, + ) diff --git a/optimade/server/routers/utils.py b/optimade/server/routers/utils.py index 72e92fcf7..91e556972 100644 --- a/optimade/server/routers/utils.py +++ b/optimade/server/routers/utils.py @@ -10,16 +10,20 @@ from optimade import __api_version__ from optimade.exceptions import BadRequest, InternalServerError -from optimade.models import ( - EntryResource, +from optimade.models import ( # type: ignore[attr-defined] + EntryResource, # type: ignore[attr-defined] EntryResponseMany, EntryResponseOne, - ResponseMeta, - ToplevelLinks, + ResponseMeta, # type: ignore[attr-defined] + ToplevelLinks, # type: ignore[attr-defined] ) from optimade.server.config import CONFIG from optimade.server.entry_collections import EntryCollection -from optimade.server.query_params import EntryListingQueryParams, SingleEntryQueryParams +from optimade.server.query_params import ( + EntryListingQueryParams, + PartialDataQueryParams, + SingleEntryQueryParams, +) from optimade.utils import PROVIDER_LIST_URLS, get_providers, mongo_id_for_database __all__ = ( @@ -62,7 +66,7 @@ def meta_values( **kwargs, ) -> ResponseMeta: """Helper to initialize the meta values""" - from optimade.models import ResponseMetaQuery + from optimade.models import ResponseMetaQuery # type: ignore[attr-defined] if isinstance(url, str): url = urllib.parse.urlparse(url) @@ -359,3 +363,55 @@ def get_single_entry( ), included=included, ) + + +def get_partial_entry( + collection: EntryCollection, + entry_id: str, + request: Request, + params: PartialDataQueryParams, +) -> Dict: + # from optimade.server.routers import ENTRY_COLLECTIONS + + params.check_params(request.query_params) + params.filter = f'id="{entry_id}"' # type: ignore[attr-defined] + ( + results, + data_returned, + more_data_available, + fields, + include_fields, + ) = collection.find(params) + + if more_data_available: + raise InternalServerError( + detail=f"more_data_available MUST be False for single entry response, however it is {more_data_available}", + ) + + # include = [] + # if getattr(params, "include", False): + # include.extend(params.include.split(",")) + # + # included = [] + # if results is not None: + # included = get_included_relationships(results, ENTRY_COLLECTIONS, include) + + links = ToplevelLinks(next=None) + + if results is not None and (fields or include_fields): + results = handle_response_fields(results, fields, include_fields)[0] # type: ignore[assignment] + + return dict( + links=links, + data=results if results else None, + meta=meta_values( + url=request.url, + data_returned=data_returned, + data_available=len(collection), + more_data_available=more_data_available, + schema=CONFIG.schema_url + if not CONFIG.is_index + else CONFIG.index_schema_url, + ), + # included=included, + ) diff --git a/tests/models/test_partialdata.py b/tests/models/test_partialdata.py index 3fcf4b194..fba0d4b87 100644 --- a/tests/models/test_partialdata.py +++ b/tests/models/test_partialdata.py @@ -1,4 +1,4 @@ -from optimade.models.partialdata import PartialDataResponse +from optimade.models.partial_data import PartialDataResource def test_object_generation(): @@ -16,6 +16,6 @@ def test_object_generation(): ], } - PartialDataResponse(**test_object) + PartialDataResource(**test_object) pass diff --git a/tests/server/routers/test_partial_data.py b/tests/server/routers/test_partial_data.py new file mode 100644 index 000000000..93a01f66e --- /dev/null +++ b/tests/server/routers/test_partial_data.py @@ -0,0 +1,15 @@ +from optimade.models import PartialDataResponse + +from ..utils import RegularEndpointTests + + +class TestPartialDataEndpoint(RegularEndpointTests): + """Tests for /partial_data/""" + + test_id = "mpf_551" + params = "response_fields=cartesian_site_positions" + request_str = f"/partial_data/{test_id}?{params}" + response_cls = PartialDataResponse + + def test_structures_endpoint_data(self): + pass From e8bbfe10863dbf0de05f550179d902ee5e5c530e Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Mon, 4 Sep 2023 18:04:57 +0200 Subject: [PATCH 55/86] First somewhat working version of partial_data endpoint. --- openapi/openapi.json | 23 +- optimade/models/partial_data.py | 35 ++- optimade/server/config.py | 24 ++- optimade/server/data/__init__.py | 2 +- optimade/server/data/test_structures.json | 2 +- .../entry_collections/entry_collections.py | 102 ++++++--- optimade/server/entry_collections/mongo.py | 200 ++++++++++++++++-- optimade/server/main.py | 18 +- optimade/server/query_params.py | 23 +- optimade/server/routers/structures.py | 25 ++- optimade/server/routers/utils.py | 87 +++++--- tests/models/test_partialdata.py | 25 ++- tests/server/routers/test_partial_data.py | 22 +- tests/server/utils.py | 4 +- 14 files changed, 456 insertions(+), 136 deletions(-) diff --git a/openapi/openapi.json b/openapi/openapi.json index 37974d556..e4abb6de1 100644 --- a/openapi/openapi.json +++ b/openapi/openapi.json @@ -1334,7 +1334,7 @@ "title": "Response Format", "type": "string", "description": "The output format requested (see section Response Format).\nDefaults to the format string 'json', which specifies the standard output format described in this specification.\nExample: `http://example.com/v1/structures?response_format=xml`", - "default": "json" + "default": "jsonlines" }, "name": "response_format", "in": "query" @@ -1389,6 +1389,19 @@ }, "name": "filter", "in": "query" + }, + { + "description": "A list of lists which contains a range for each dimension of the property.", + "required": false, + "schema": { + "title": "Property Ranges", + "type": "array", + "items": {}, + "description": "A list of lists which contains a range for each dimension of the property.", + "default": [] + }, + "name": "property_ranges", + "in": "query" } ], "responses": { @@ -3839,8 +3852,8 @@ "description": "The three lattice vectors in Cartesian coordinates, in \u00e5ngstr\u00f6m (\u00c5).\n\n- **Type**: list of list of floats or unknown values.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - MUST be a list of three vectors *a*, *b*, and *c*, where each of the vectors MUST BE a list of the vector's coordinates along the x, y, and z Cartesian coordinates.\n (Therefore, the first index runs over the three lattice vectors and the second index runs over the x, y, z Cartesian coordinates).\n - For databases that do not define an absolute Cartesian system (e.g., only defining the length and angles between vectors), the first lattice vector SHOULD be set along *x* and the second on the *xy*-plane.\n - MUST always contain three vectors of three coordinates each, independently of the elements of property `dimension_types`.\n The vectors SHOULD by convention be chosen so the determinant of the `lattice_vectors` matrix is different from zero.\n The vectors in the non-periodic directions have no significance beyond fulfilling these requirements.\n - The coordinates of the lattice vectors of non-periodic dimensions (i.e., those dimensions for which `dimension_types` is `0`) MAY be given as a list of all `null` values.\n If a lattice vector contains the value `null`, all coordinates of that lattice vector MUST be `null`.\n\n- **Examples**:\n - `[[4.0,0.0,0.0],[0.0,4.0,0.0],[0.0,1.0,4.0]]` represents a cell, where the first vector is `(4, 0, 0)`, i.e., a vector aligned along the `x` axis of length 4 \u00c5; the second vector is `(0, 4, 0)`; and the third vector is `(0, 1, 4)`.", "nullable": true, "x-optimade-support": "should", - "x-optimade-queryable": "optional", - "x-optimade-unit": "\u00c5" + "x-optimade-unit": "\u00c5", + "x-optimade-queryable": "optional" }, "cartesian_site_positions": { "title": "Cartesian Site Positions", @@ -3856,8 +3869,8 @@ "description": "Cartesian positions of each site in the structure.\nA site is usually used to describe positions of atoms; what atoms can be encountered at a given site is conveyed by the `species_at_sites` property, and the species themselves are described in the `species` property.\n\n- **Type**: list of list of floats\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - It MUST be a list of length equal to the number of sites in the structure, where every element is a list of the three Cartesian coordinates of a site expressed as float values in the unit angstrom (\u00c5).\n - An entry MAY have multiple sites at the same Cartesian position (for a relevant use of this, see e.g., the property `assemblies`).\n\n- **Examples**:\n - `[[0,0,0],[0,0,2]]` indicates a structure with two sites, one sitting at the origin and one along the (positive) *z*-axis, 2 \u00c5 away from the origin.", "nullable": true, "x-optimade-support": "should", - "x-optimade-queryable": "optional", - "x-optimade-unit": "\u00c5" + "x-optimade-unit": "\u00c5", + "x-optimade-queryable": "optional" }, "nsites": { "title": "Nsites", diff --git a/optimade/models/partial_data.py b/optimade/models/partial_data.py index 610640359..d5d717c54 100644 --- a/optimade/models/partial_data.py +++ b/optimade/models/partial_data.py @@ -3,7 +3,7 @@ from pydantic import BaseModel from optimade.models.entries import EntryResource -from optimade.models.utils import OptimadeField, SupportLevel +from optimade.models.utils import OptimadeField, StrictField, SupportLevel __all__ = ( "PartialDataHeader", @@ -26,7 +26,7 @@ class PartialDataHeader(BaseModel): - **Examples**: - `""optimade-partial-data": {"version": "1.2.0"}"`""", support=SupportLevel.MUST, - ) + ) # Todo add format and link fields layout: Literal["dense", "sparse"] = OptimadeField( ..., description="""A string either equal to "dense" or "sparse" to indicate whether the returned format uses a dense or sparse layout. @@ -123,8 +123,37 @@ class PartialDataHeader(BaseModel): """, support=SupportLevel.OPTIONAL, ) + parent_id: Optional[dict] = OptimadeField( + None, + description="""The id of the entry to which this partial data belongs. +""", + support=SupportLevel.OPTIONAL, + ) -class PartialDataResource(EntryResource): +class PartialDataFormat(BaseModel): header: PartialDataHeader data: list + + +class PartialDataResource(EntryResource): + type: str = StrictField( + "partial_data", + description="""The name of the type of an entry. + - **Type**: string. + + - **Requirements/Conventions**: + - **Support**: MUST be supported by all implementations, MUST NOT be `null`. + - **Query**: MUST be a queryable property with support for all mandatory filter features. + - **Response**: REQUIRED in the response. + - MUST be an existing entry type. + - The entry of type `` and ID `` MUST be returned in response to a request for `//` under the versioned base URL. + + - **Examples**: + - `"structures"`""", + regex="^structures$", + support=SupportLevel.MUST, + queryable=SupportLevel.MUST, + ) + + attributes: PartialDataHeader # Todo make a better model for json response diff --git a/optimade/server/config.py b/optimade/server/config.py index 1346cb3c7..a9f0ae778 100644 --- a/optimade/server/config.py +++ b/optimade/server/config.py @@ -67,6 +67,20 @@ class SupportedBackend(Enum): MONGOMOCK = "mongomock" +class SupportedResponseFormats(Enum): + """Enumeration of supported response formats. + + - 'JSON': [JSON](https://www.json.org/json-en.html) + - 'HDF5': [HDF5](https://portal.hdfgroup.org/display/HDF5/HDF5) + - `JSONL`: [JSONL](https://jsonlines.org/) + + """ + + HDF5 = "hdf5" + JSON = "json" + JSONL = "jsonlines" + + def config_file_settings(settings: BaseSettings) -> Dict[str, Any]: """Configuration file settings source. @@ -173,7 +187,7 @@ class ServerConfig(BaseSettings): description="Mongo collection name for /structures endpoint resources", ) partial_data_collection: str = Field( - "fs.files", + "fs", description="Mongo Grid FS system containing the data that needs to be returned via the partial data mechanism.", ) page_limit: int = Field(20, description="Default number of resources per page") @@ -301,6 +315,14 @@ class ServerConfig(BaseSettings): description="""If False, data from the database will not undergo validation before being emitted by the API, and only the mapping of aliases will occur.""", ) + enabled_response_formats: List[SupportedResponseFormats] = Field( + ["json", "jsonlines"], + description="""A list of the response formats that are supported by this server. Must include the "json" format.""", + ) + max_response_size: Optional[Dict[SupportedResponseFormats, int]] = Field( + {"json": 10, "jsonlines": 40}, + description="""This dictionary contains the approximate maximum size for a trajectory response in megabytes for the different response_formats. The keys indicate the response_format and the values the maximum size.""", + ) @validator("implementation", pre=True) def set_implementation_version(cls, v): diff --git a/optimade/server/data/__init__.py b/optimade/server/data/__init__.py index 4b0c41087..dd9c628bb 100644 --- a/optimade/server/data/__init__.py +++ b/optimade/server/data/__init__.py @@ -10,7 +10,7 @@ "providers": "providers.json", } -data_files = ["mpf_551_cartesian_site_positions.json"] +data_files = ["mpf_551:cartesian_site_positions"] for var, path in data_paths.items(): try: diff --git a/optimade/server/data/test_structures.json b/optimade/server/data/test_structures.json index d9710600f..fe9d22879 100644 --- a/optimade/server/data/test_structures.json +++ b/optimade/server/data/test_structures.json @@ -2131,7 +2131,7 @@ "cartesian_site_positions": [ { "format": "jsonlines", - "link": "https://example.org/optimade/v1.2/extensions/partial_data/structures/mpf_551/a/default_format" + "link": "http://localhost:5000/partial_data/mpf_551?response_fields=cartesian_sitePositions" } ] } diff --git a/optimade/server/entry_collections/entry_collections.py b/optimade/server/entry_collections/entry_collections.py index 02a8dc02f..a5aa8756c 100644 --- a/optimade/server/entry_collections/entry_collections.py +++ b/optimade/server/entry_collections/entry_collections.py @@ -44,6 +44,17 @@ def create_collection( SupportedBackend.MONGODB, SupportedBackend.MONGOMOCK, ): + from optimade.models import PartialDataResource + + if resource_cls is PartialDataResource: + from optimade.server.entry_collections.mongo import GridFSCollection + + return GridFSCollection( + name=name, + resource_cls=resource_cls, + resource_mapper=resource_mapper, + ) + from optimade.server.entry_collections.mongo import MongoCollection return MongoCollection( @@ -140,7 +151,10 @@ def count(self, **kwargs: Any) -> int: """ def find( - self, params: Union[EntryListingQueryParams, SingleEntryQueryParams] + self, + params: Union[ + EntryListingQueryParams, SingleEntryQueryParams, PartialDataQueryParams + ], ) -> Tuple[Union[None, Dict, List[Dict]], int, bool, Set[str], Set[str]]: """ Fetches results and indicates if more data is available. @@ -166,47 +180,52 @@ def find( params, (SingleEntryQueryParams, PartialDataQueryParams) ) response_fields = criteria.pop("fields") - partial_data = isinstance(params, PartialDataQueryParams) raw_results, data_returned, more_data_available = self._run_db_query( - criteria, single_entry, partial_data + criteria, single_entry ) exclude_fields = self.all_fields - response_fields - include_fields = ( - response_fields - self.resource_mapper.TOP_LEVEL_NON_ATTRIBUTES_FIELDS - ) - bad_optimade_fields = set() - bad_provider_fields = set() supported_prefixes = self.resource_mapper.SUPPORTED_PREFIXES all_attributes = self.resource_mapper.ALL_ATTRIBUTES - for field in include_fields: - if field not in all_attributes: - if field.startswith("_"): - if any( - field.startswith(f"_{prefix}_") for prefix in supported_prefixes - ): - bad_provider_fields.add(field) - else: - bad_optimade_fields.add(field) - if bad_provider_fields: - warnings.warn( - message=f"Unrecognised field(s) for this provider requested in `response_fields`: {bad_provider_fields}.", - category=UnknownProviderProperty, + if not isinstance(params, PartialDataQueryParams): + include_fields = ( + response_fields - self.resource_mapper.TOP_LEVEL_NON_ATTRIBUTES_FIELDS ) + bad_optimade_fields = set() + bad_provider_fields = set() + + for field in include_fields: + if field not in all_attributes: + if field.startswith("_"): + if any( + field.startswith(f"_{prefix}_") + for prefix in supported_prefixes + ): + bad_provider_fields.add(field) + else: + bad_optimade_fields.add(field) + + if bad_provider_fields: + warnings.warn( + message=f"Unrecognised field(s) for this provider requested in `response_fields`: {bad_provider_fields}.", + category=UnknownProviderProperty, + ) - if bad_optimade_fields: - raise BadRequest( - detail=f"Unrecognised OPTIMADE field(s) in requested `response_fields`: {bad_optimade_fields}." - ) + if bad_optimade_fields: + raise BadRequest( + detail=f"Unrecognised OPTIMADE field(s) in requested `response_fields`: {bad_optimade_fields}." + ) + else: + include_fields = set() results: Union[None, List[Dict], Dict] = None if raw_results: results = [self.resource_mapper.map_back(doc) for doc in raw_results] - + self.generate_links_partial_data(results) if single_entry: results = results[0] # type: ignore[assignment] @@ -225,12 +244,29 @@ def find( include_fields, ) + def generate_links_partial_data(self, results): + for entry in results: + if entry.get("meta", {}) and entry["meta"].get("partial_data_links", {}): + for property in entry["meta"]["partial_data_links"]: + for response_format in CONFIG.enabled_response_formats: + entry["meta"]["partial_data_links"][property].append( + { + "format": str(response_format.value), + "link": CONFIG.base_url + + "/partial_data/" + + entry["id"] + + "?response_fields=" + + property + + "&response_format=" + + str(response_format.value), + } + ) + @abstractmethod def _run_db_query( self, criteria: Dict[str, Any], single_entry: bool = False, - partial_data: bool = False, ) -> Tuple[List[Dict[str, Any]], int, bool]: """Run the query on the backend and collect the results. @@ -303,7 +339,10 @@ def get_attribute_fields(self) -> Set[str]: return set(attributes["properties"].keys()) def handle_query_params( - self, params: Union[EntryListingQueryParams, SingleEntryQueryParams] + self, + params: Union[ + EntryListingQueryParams, SingleEntryQueryParams, PartialDataQueryParams + ], ) -> Dict[str, Any]: """Parse and interpret the backend-agnostic query parameter models into a dictionary that can be used by the specific backend. @@ -335,12 +374,11 @@ def handle_query_params( cursor_kwargs["filter"] = {} # response_format - if ( - getattr(params, "response_format", False) - and params.response_format != "json" + if getattr(params, "response_format", False) and params.response_format not in ( + x.value for x in CONFIG.enabled_response_formats ): raise BadRequest( - detail=f"Response format {params.response_format} is not supported, please use response_format='json'" + detail=f"Response format {params.response_format} is not supported, please use one of the supported response formats: {', '.join((x.value for x in CONFIG.enabled_response_formats))}" ) # page_limit diff --git a/optimade/server/entry_collections/mongo.py b/optimade/server/entry_collections/mongo.py index dd442c6a6..7c68589c3 100644 --- a/optimade/server/entry_collections/mongo.py +++ b/optimade/server/entry_collections/mongo.py @@ -32,6 +32,186 @@ if CONFIG.database_backend.value in ("mongomock", "mongodb"): CLIENT = MongoClient(CONFIG.mongo_uri) + import gridfs + + +# todo make general mongo class for both mongo and gridfs and move _check_aliases there. +class GridFSCollection(EntryCollection): + """Class for querying gridfs collections (implemented by either pymongo or mongomock).""" + + def __init__( + self, + name: str, + resource_cls: Type[EntryResource], + resource_mapper: Type[BaseResourceMapper], + database: str = CONFIG.mongo_database, + ): + """Initialize the GridFSCollection for the given parameters. + + Parameters: + name: The name of the collection. + resource_cls: The type of entry resource that is stored by the collection. + resource_mapper: A resource mapper object that handles aliases and + format changes between deserialization and response. + database: The name of the underlying MongoDB database to connect to. + + """ + super().__init__( + resource_cls, + resource_mapper, + MongoTransformer(mapper=resource_mapper), + ) + db = MongoClient(CONFIG.mongo_uri)[ + database + ] # Somehow importing the client from optimade.server.entry_collections.mongo gives an error that the type of db is not "Database" even though it is. + + self.collection = gridfs.GridFS(db, name) + + # check aliases do not clash with mongo operators + self._check_aliases(self.resource_mapper.all_aliases()) + self._check_aliases(self.resource_mapper.all_length_aliases()) + + def __len__(self) -> int: + """Returns the total number of entries in the collection.""" + return len(self.collection.list()) + + def count(self, **kwargs: Any) -> int: + """Returns the number of entries matching the query specified + by the keyword arguments. + + Parameters: + **kwargs: Query parameters as keyword arguments. The keys + 'filter', 'skip', 'limit', 'hint' and 'maxTimeMS' will be passed + to the `pymongo.collection.Collection.count_documents` method. + + """ + for k in list(kwargs.keys()): + if k not in ("filter", "skip", "limit", "hint", "maxTimeMS"): + del kwargs[k] + if "filter" not in kwargs: # "filter" is needed for count_documents() + kwargs["filter"] = {} + return len(self.collection.find(**kwargs)) + + def insert(self, content: bytes, filename: str) -> None: + """Add the given entries to the underlying database. + + Warning: + No validation is performed on the incoming data. + + Arguments: + data: The entry resource objects to add to the database. + + """ + self.collection.put(content, filename=filename) + + def handle_query_params( + self, + params: Union[ + EntryListingQueryParams, SingleEntryQueryParams, PartialDataQueryParams + ], + ) -> Dict[str, Any]: + """Parse and interpret the backend-agnostic query parameter models into a dictionary + that can be used by MongoDB. + + This Mongo-specific method calls the base `EntryCollection.handle_query_params` method + and adds additional handling of the MongoDB ObjectID type. + + Parameters: + params: The initialized query parameter model from the server. + + Raises: + Forbidden: If too large of a page limit is provided. + BadRequest: If an invalid request is made, e.g., with incorrect fields + or response format. + + Returns: + A dictionary representation of the query parameters. + + """ + + criteria = super().handle_query_params(params) + # Handle MongoDB ObjectIDs: + # - If they were not requested, then explicitly remove them + # - If they were requested, then cast them to strings in the response + if "_id" not in criteria.get("projection", {}): + criteria["projection"]["_id"] = False + + if "page_above" in criteria: + raise NotImplementedError( + "`page_above` is not implemented for this backend." + ) + + if criteria.get("projection", {}).get("_id"): + criteria["projection"]["_id"] = {"$toString": "$_id"} + + if isinstance(params, PartialDataQueryParams): + entry_id = params.filter.split("=")[1][1:-1] + criteria["filter"] = { + "filename": {"$eq": f"{entry_id}:{params.response_fields}"} + } # Todo make sure response fields has only one value + + return criteria + + # todo test if it is more efficient to use the get method of gridfs + def _run_db_query( + self, + criteria: Dict[str, Any], + single_entry: bool = False, + ) -> Tuple[List[Dict[str, Any]], int, bool]: + """Run the query on the backend and collect the results. + + Arguments: + criteria: A dictionary representation of the query parameters. + single_entry: Whether or not the caller is expecting a single entry response. + + Returns: + The list of entries from the database (without any re-mapping), the total number of + entries matching the query and a boolean for whether or not there is more data available. + + """ + + # TODO handle case where the type does not have a fixed width. For example strings or dictionaries. + + max_return_size = 10000000 + property_dimensions: list[int] = [] # todo read this data from metadata + results = [] + filterdict = criteria.pop("filter", {}) + criteria.pop("requested_range", property_dimensions) + gridcursor = self.collection.find( + filterdict + ) # I have tried to use just **criteria as is mentioned in the documentation but this does not seem to work. + more_data_available = False + nresults = 0 + for file_obj in gridcursor: + nresults = +1 + property_name = file_obj.filename.split(":")[1] + # results.append(grid_out.read()) + if file_obj.length > max_return_size: + header = file_obj.readline() + values = header + file_obj.read() + # TODo handle splitting up file + more_data_available = True + else: # case whole file can be returned. + values = file_obj.read() + + results = [ + { + "type": "partial_data", + "id": str(file_obj._id), + "property_name": property_name, + "data": values, + } + ] + break + + return results, nresults, more_data_available + + def _check_aliases(self, aliases): + """Check that aliases do not clash with mongo keywords.""" + if any( + alias[0].startswith("$") or alias[1].startswith("$") for alias in aliases + ): + raise RuntimeError(f"Cannot define an alias starting with a '$': {aliases}") class MongoCollection(EntryCollection): @@ -138,19 +318,12 @@ def handle_query_params( if criteria.get("projection", {}).get("_id"): criteria["projection"]["_id"] = {"$toString": "$_id"} - if isinstance(params, PartialDataQueryParams): - entry_id = params.filter.split("=")[1][1:-1] - criteria["filter"] = { - "filename": {"$eq": f"{entry_id}_{params.response_fields}"} - } # Todo make sure response fields has only one value - return criteria def _run_db_query( self, criteria: Dict[str, Any], single_entry: bool = False, - partial_data: bool = False, ) -> Tuple[List[Dict[str, Any]], int, bool]: """Run the query on the backend and collect the results. @@ -163,18 +336,7 @@ def _run_db_query( entries matching the query and a boolean for whether or not there is more data available. """ - if partial_data: - import gridfs - - fs = gridfs.GridFS(self.collection.database) - # file = fs.find({"filename": }) - for grid_out in fs.find( - {"filename": "mpf_551_cartesian_site_positions.json"} - ): - grid_out.read() - results = [] - else: - results = list(self.collection.find(**criteria)) + results = list(self.collection.find(**criteria)) if CONFIG.database_backend == SupportedBackend.MONGOMOCK and criteria.get( "projection", {} diff --git a/optimade/server/main.py b/optimade/server/main.py index 09c8a936d..0fe687be1 100644 --- a/optimade/server/main.py +++ b/optimade/server/main.py @@ -71,25 +71,15 @@ # Todo Do we need to check a file is not already stored in gridfs? # Load test data from files into gridfs + if CONFIG.database_backend.value in ("mongomock", "mongodb"): from pathlib import Path - if CONFIG.database_backend.value == "mongodb": - from pymongo import MongoClient - elif CONFIG.database_backend.value == "mongomock": - import mongomock.gridfs - from mongomock import MongoClient - - mongomock.gridfs.enable_gridfs_integration() - import gridfs + from optimade.server.routers.partial_data import partial_data_coll - db = MongoClient(CONFIG.mongo_uri)[ - CONFIG.mongo_database - ] # Somehow importing the client from optimade.server.entry_collections.mongo gives an error that the type of db is not "Database" eventhough it is. - fs = gridfs.GridFS(db) for filename in getattr(data, "data_files", []): with open(Path(__file__).parent / "data" / filename, "rb") as f: - a = fs.put(f, filename=filename) + a = partial_data_coll.insert(f, filename=filename) pass def load_entries(endpoint_name: str, endpoint_collection: EntryCollection): @@ -133,7 +123,7 @@ def load_entries(endpoint_name: str, endpoint_collection: EntryCollection): def add_major_version_base_url(app: FastAPI): """Add mandatory vMajor endpoints, i.e. all except versions.""" - for endpoint in (info, links, references, structures, landing): + for endpoint in (info, links, references, structures, landing, partial_data): app.include_router(endpoint.router, prefix=BASE_URL_PREFIXES["major"]) diff --git a/optimade/server/query_params.py b/optimade/server/query_params.py index d52d640a5..50686321b 100644 --- a/optimade/server/query_params.py +++ b/optimade/server/query_params.py @@ -353,22 +353,6 @@ class PartialDataQueryParams(BaseQueryParams): **Example**: `http://example.com/v1/structures?response_fields=last_modified,nsites` - include (str): A server MAY implement the JSON API concept of returning [compound documents](https://jsonapi.org/format/1.0/#document-compound-documents) - by utilizing the `include` query parameter as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-includes). - - All related resource objects MUST be returned as part of an array value for the top-level `included` field, - see the section JSON Response Schema: Common Fields. - - The value of `include` MUST be a comma-separated list of "relationship paths", as defined in the [JSON API](https://jsonapi.org/format/1.0/#fetching-includes). - If relationship paths are not supported, or a server is unable to identify a relationship path a `400 Bad Request` response MUST be made. - - The **default value** for `include` is `references`. This means `references` entries MUST always be included under the top-level field - `included` as default, since a server assumes if `include` is not specified by a client in the request, it is still specified as `include=references`. - Note, if a client explicitly specifies `include` and leaves out `references`, `references` resource objects MUST NOT be included under the top-level - field `included`, as per the definition of `included`, see section JSON Response Schema: Common Fields. - - **Note**: A query with the parameter `include` set to the empty string means no related resource objects are to be returned under the top-level field `included`. - api_hint (str): If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0. @@ -379,7 +363,7 @@ def __init__( self, *, response_format: str = Query( - "json", + "jsonlines", description="The output format requested (see section Response Format).\nDefaults to the format string 'json', which specifies the standard output format described in this specification.\nExample: `http://example.com/v1/structures?response_format=xml`", ), email_address: EmailStr = Query( @@ -400,9 +384,14 @@ def __init__( "", description="A filter string, in the format described in section API Filtering Format Specification of the specification.", ), + property_ranges: list = Query( + [], + description="A list of lists which contains a range for each dimension of the property.", + ), ): self.filter = filter self.response_format = response_format self.email_address = email_address self.response_fields = response_fields self.api_hint = api_hint + self.property_ranges = property_ranges diff --git a/optimade/server/routers/structures.py b/optimade/server/routers/structures.py index 00980b246..f9881b121 100644 --- a/optimade/server/routers/structures.py +++ b/optimade/server/routers/structures.py @@ -10,8 +10,15 @@ from optimade.server.config import CONFIG from optimade.server.entry_collections import create_collection from optimade.server.mappers import StructureMapper -from optimade.server.query_params import EntryListingQueryParams, SingleEntryQueryParams -from optimade.server.routers.utils import get_entries, get_single_entry +from optimade.server.query_params import ( + EntryListingQueryParams, + SingleEntryQueryParams, +) +from optimade.server.routers.utils import ( + get_entries, + get_partial_entry, + get_single_entry, +) from optimade.server.schemas import ERROR_RESPONSES router = APIRouter(redirect_slashes=True) @@ -49,8 +56,20 @@ def get_structures( responses=ERROR_RESPONSES, ) def get_single_structure( - request: Request, entry_id: str, params: SingleEntryQueryParams = Depends() + request: Request, + entry_id: str, + params: SingleEntryQueryParams = Depends(), ) -> Any: + if "property_ranges" in request.query_params: + from optimade.server.routers.partial_data import partial_data_coll + + return get_partial_entry( + collection=partial_data_coll, + entry_id=entry_id, + request=request, + params=params, # type: ignore[arg-type] + ) + return get_single_entry( collection=structures_coll, entry_id=entry_id, diff --git a/optimade/server/routers/utils.py b/optimade/server/routers/utils.py index 91e556972..7fa31267f 100644 --- a/optimade/server/routers/utils.py +++ b/optimade/server/routers/utils.py @@ -1,15 +1,18 @@ # pylint: disable=import-outside-toplevel,too-many-locals +import io import re import urllib.parse from datetime import datetime from typing import Any, Dict, List, Optional, Set, Type, Union -from fastapi import Request +import numpy as np +from fastapi import Request, Response from fastapi.responses import JSONResponse from starlette.datastructures import URL as StarletteURL from optimade import __api_version__ -from optimade.exceptions import BadRequest, InternalServerError +from optimade.adapters.jsonl import to_jsonl +from optimade.exceptions import BadRequest, InternalServerError, NotFound from optimade.models import ( # type: ignore[attr-defined] EntryResource, # type: ignore[attr-defined] EntryResponseMany, @@ -369,12 +372,12 @@ def get_partial_entry( collection: EntryCollection, entry_id: str, request: Request, - params: PartialDataQueryParams, + params: Union[PartialDataQueryParams], ) -> Dict: # from optimade.server.routers import ENTRY_COLLECTIONS params.check_params(request.query_params) - params.filter = f'id="{entry_id}"' # type: ignore[attr-defined] + params.filter = f'parent_id="{entry_id}"' ( results, data_returned, @@ -383,35 +386,59 @@ def get_partial_entry( include_fields, ) = collection.find(params) - if more_data_available: - raise InternalServerError( - detail=f"more_data_available MUST be False for single entry response, however it is {more_data_available}", - ) - - # include = [] - # if getattr(params, "include", False): - # include.extend(params.include.split(",")) - # - # included = [] - # if results is not None: - # included = get_included_relationships(results, ENTRY_COLLECTIONS, include) - links = ToplevelLinks(next=None) if results is not None and (fields or include_fields): results = handle_response_fields(results, fields, include_fields)[0] # type: ignore[assignment] + if results is None: + raise NotFound( + detail=f"No data available for the combination of entry {entry_id} and property {params.response_fields}", + ) + # todo make the implementation of these formats more universal. i.e. allow them for other endpoint as well, + + array = np.load(io.BytesIO(results["attributes"]["data"])) + sliceobj = [] + for i in array.shape: + sliceobj.append({"start": 1, "stop": i, "step": 1}) + header = { + "optimade-partial-data": {"format": "1.2.0"}, + "layout": "dense", + "property_name": params.response_fields, + "returned_ranges": sliceobj, + # "entry": {"id": entry_id, "type": None}, #Todo add type information to metadata entry + "has_references": False, + } # Todo: add support for non_dense data + + if params.response_format == "json": + for key in header: + results["attributes"][key] = header[key] + results["attributes"]["data"] = array.tolist() + return dict( + links=links, + data=results if results else None, + meta=meta_values( + url=request.url, + data_returned=data_returned, + data_available=len(collection), + more_data_available=more_data_available, + schema=CONFIG.schema_url + if not CONFIG.is_index + else CONFIG.index_schema_url, + ), + # included=included, + ) - return dict( - links=links, - data=results if results else None, - meta=meta_values( - url=request.url, - data_returned=data_returned, - data_available=len(collection), - more_data_available=more_data_available, - schema=CONFIG.schema_url - if not CONFIG.is_index - else CONFIG.index_schema_url, - ), - # included=included, + return Response( + content=to_jsonl([header, array.tolist()]), + media_type="application/jsonlines", + headers={ + "Content-disposition": f"attachment; filename={entry_id + ':' + params.response_fields}.jsonl" + }, ) + + +def convert_data_to_str(results): + values = results["attributes"]["data"] + if isinstance(values, bytes): + results["attributes"]["data"] = np.array2string(np.load(io.BytesIO(values))) + return results diff --git a/tests/models/test_partialdata.py b/tests/models/test_partialdata.py index fba0d4b87..e6f1b3582 100644 --- a/tests/models/test_partialdata.py +++ b/tests/models/test_partialdata.py @@ -1,7 +1,7 @@ -from optimade.models.partial_data import PartialDataResource +from optimade.models.partial_data import PartialDataFormat -def test_object_generation(): +def test_partial_data_object_generation(): test_object = { "header": { "optimade_partial_data": {"version": "1.2.0"}, @@ -16,6 +16,23 @@ def test_object_generation(): ], } - PartialDataResource(**test_object) + PartialDataFormat(**test_object) - pass + +# todo finish test below +# def test_json_object_generation(): +# test_object = { +# "header": { +# "optimade_partial_data": {"version": "1.2.0"}, +# "layout": "dense", +# "returned_ranges": [{"start": 10, "stop": 20, "step": 2}], +# }, +# "data": [ +# 123, +# 345, +# -12.6, +# ["PARTIAL-DATA-NEXT", ["https://example.db.org/value4"]], +# ], +# } +# +# PartialDataResource(**test_object) diff --git a/tests/server/routers/test_partial_data.py b/tests/server/routers/test_partial_data.py index 93a01f66e..7ac1fda7c 100644 --- a/tests/server/routers/test_partial_data.py +++ b/tests/server/routers/test_partial_data.py @@ -1,9 +1,9 @@ from optimade.models import PartialDataResponse -from ..utils import RegularEndpointTests +from ..utils import NoJsonEndpointTests -class TestPartialDataEndpoint(RegularEndpointTests): +class TestPartialDataEndpoint(NoJsonEndpointTests): """Tests for /partial_data/""" test_id = "mpf_551" @@ -11,5 +11,19 @@ class TestPartialDataEndpoint(RegularEndpointTests): request_str = f"/partial_data/{test_id}?{params}" response_cls = PartialDataResponse - def test_structures_endpoint_data(self): - pass + +def test_wrong_id_partial_data(check_error_response, client): + """If a non-supported versioned base URL is passed, `553 Version Not Supported` should be returned + + A specific JSON response should also occur. + """ + test_id = "mpf_486" + params = "response_fields=cartesian_site_positions" + request = f"/partial_data/{test_id}?{params}" + check_error_response( + request, + expected_status=404, + expected_title="Not Found", + expected_detail="No data available for the combination of entry mpf_486 and property cartesian_site_positions", + server=client, + ) diff --git a/tests/server/utils.py b/tests/server/utils.py index 6a046c36b..2eaa6d633 100644 --- a/tests/server/utils.py +++ b/tests/server/utils.py @@ -228,9 +228,9 @@ class NoJsonEndpointTests: response: Optional[httpx.Response] = None @pytest.fixture(autouse=True) - def get_response(self, both_clients): + def get_response(self, client): """Get response from client""" - self.response = both_clients.get(self.request_str) + self.response = client.get(self.request_str) yield self.response = None From f67555ea5874cb6a95fea2c39d901724f55cf807 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Tue, 5 Sep 2023 11:53:37 +0200 Subject: [PATCH 56/86] Move _check_aliases to it's own subclass so it can be used for both gridfs and MongoDB. --- optimade/server/entry_collections/mongo.py | 28 +++++++++------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/optimade/server/entry_collections/mongo.py b/optimade/server/entry_collections/mongo.py index 7c68589c3..f46d30a34 100644 --- a/optimade/server/entry_collections/mongo.py +++ b/optimade/server/entry_collections/mongo.py @@ -35,8 +35,16 @@ import gridfs -# todo make general mongo class for both mongo and gridfs and move _check_aliases there. -class GridFSCollection(EntryCollection): +class MongoBaseCollection(EntryCollection): + def _check_aliases(self, aliases): + """Check that aliases do not clash with mongo keywords.""" + if any( + alias[0].startswith("$") or alias[1].startswith("$") for alias in aliases + ): + raise RuntimeError(f"Cannot define an alias starting with a '$': {aliases}") + + +class GridFSCollection(MongoBaseCollection): """Class for querying gridfs collections (implemented by either pymongo or mongomock).""" def __init__( @@ -206,15 +214,8 @@ def _run_db_query( return results, nresults, more_data_available - def _check_aliases(self, aliases): - """Check that aliases do not clash with mongo keywords.""" - if any( - alias[0].startswith("$") or alias[1].startswith("$") for alias in aliases - ): - raise RuntimeError(f"Cannot define an alias starting with a '$': {aliases}") - -class MongoCollection(EntryCollection): +class MongoCollection(MongoBaseCollection): """Class for querying MongoDB collections (implemented by either pymongo or mongomock) containing serialized [`EntryResource`][optimade.models.entries.EntryResource]s objects. @@ -358,10 +359,3 @@ def _run_db_query( more_data_available = False return results, data_returned, more_data_available - - def _check_aliases(self, aliases): - """Check that aliases do not clash with mongo keywords.""" - if any( - alias[0].startswith("$") or alias[1].startswith("$") for alias in aliases - ): - raise RuntimeError(f"Cannot define an alias starting with a '$': {aliases}") From b7907c9c2d95936dc409f2727b227341e409dfc7 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Tue, 5 Sep 2023 16:58:57 +0200 Subject: [PATCH 57/86] Merge branch 'master' into optimade_python_tools_trajectory_0.1 # Conflicts: # README.md # docs/getting_started/client.md # docs/static/default_config.json # openapi/openapi.json # optimade/models/__init__.py # optimade/models/jsonapi.py # optimade/models/structures.py # optimade/server/entry_collections/entry_collections.py # optimade/server/mappers/__init__.py # optimade/server/middleware.py # optimade/server/routers/info.py # optimade/server/routers/utils.py # optimade/server/schemas.py # optimade/server/warnings.py # optimade/validator/config.py # requirements-docs.txt # requirements.txt # setup.py # tests/server/conftest.py # tests/server/test_mappers.py # tests/server/utils.py --- .../mpf_551_cartesian_site_positions.json | 372 ------------------ 1 file changed, 372 deletions(-) delete mode 100644 optimade/server/data/mpf_551_cartesian_site_positions.json diff --git a/optimade/server/data/mpf_551_cartesian_site_positions.json b/optimade/server/data/mpf_551_cartesian_site_positions.json deleted file mode 100644 index d41a4b4f8..000000000 --- a/optimade/server/data/mpf_551_cartesian_site_positions.json +++ /dev/null @@ -1,372 +0,0 @@ -[ - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ], - [ - 0.449480176317956, - 0.449480176317956, - 0.449480176317956 - ] -] From 07defa588a9ddef776c6f5285dc31467f74f5e37 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Tue, 5 Sep 2023 16:59:10 +0200 Subject: [PATCH 58/86] Extra todo's and comments after trying to add validator for format field. --- optimade/models/entries.py | 9 ++++++++- .../server/data/mpf_551:cartesian_site_positions | Bin 0 -> 1904 bytes optimade/server/data/test_structures.json | 7 +------ optimade/server/routers/structures.py | 2 +- optimade/validator/utils.py | 2 -- 5 files changed, 10 insertions(+), 10 deletions(-) create mode 100644 optimade/server/data/mpf_551:cartesian_site_positions diff --git a/optimade/models/entries.py b/optimade/models/entries.py index 4b0a8d145..a4f6a8333 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -101,6 +101,7 @@ def cast_immutable_id_to_str(cls, value): class PartialDataLink(BaseModel): + # Todo add validator for link field. link: AnyUrl = OptimadeField( ..., description="String. A JSON API link that points to a location from which the omitted data can be fetched. There is no requirement on the syntax or format for the link URL.", @@ -108,7 +109,13 @@ class PartialDataLink(BaseModel): format: str = OptimadeField( ..., description='String. The name of the format provided via this link. For one of the objects this format field SHOULD have the value "jsonlines", which refers to the format in OPTIMADE JSON lines partial data format.', - ) # todo add check that the value of format is in a list of supported formats. + ) # todo add check that the value of format is in a list of supported formats. I have already written the validator below but it causes an error I do not understand + + # @validator("format", always=False, check_fields=False) + # def check_if_format_is_supported(cls, value): + # from optimade.server.config import CONFIG + # if value not in [form.value for form in CONFIG.enabled_response_formats]: + # raise ValueError(f"The format {value} is not one of the enabled_formats{CONFIG.enabled_response_formats}.") class EntryMetadata(Meta): diff --git a/optimade/server/data/mpf_551:cartesian_site_positions b/optimade/server/data/mpf_551:cartesian_site_positions new file mode 100644 index 0000000000000000000000000000000000000000..a996c92662e620572703a1f21bf55ff39268ad0a GIT binary patch literal 1904 zcmbR27wQ`j$;eQ~P_3SlTAW;@Zl$1ZlV+i=qoAIaUsO_*m=~X4l#&V(cT3DEP6dh= zXCxM+0{I%|COQhnnmP)#3giN=KFRn7k0W>NN9oaU7)=MG`Czmh7%c~eb2$J2AWTcV literal 0 HcmV?d00001 diff --git a/optimade/server/data/test_structures.json b/optimade/server/data/test_structures.json index fe9d22879..7ef98df69 100644 --- a/optimade/server/data/test_structures.json +++ b/optimade/server/data/test_structures.json @@ -2128,12 +2128,7 @@ "cartesian_site_positions": null, "meta": { "partial_data_links": { - "cartesian_site_positions": [ - { - "format": "jsonlines", - "link": "http://localhost:5000/partial_data/mpf_551?response_fields=cartesian_sitePositions" - } - ] + "cartesian_site_positions": [] } }, "dimension_types": [ diff --git a/optimade/server/routers/structures.py b/optimade/server/routers/structures.py index f9881b121..ad77b33eb 100644 --- a/optimade/server/routers/structures.py +++ b/optimade/server/routers/structures.py @@ -60,7 +60,7 @@ def get_single_structure( entry_id: str, params: SingleEntryQueryParams = Depends(), ) -> Any: - if "property_ranges" in request.query_params: + if "property_ranges" in request.query_params: # todo add test for this from optimade.server.routers.partial_data import partial_data_coll return get_partial_entry( diff --git a/optimade/validator/utils.py b/optimade/validator/utils.py index 81e5e11b7..085db335e 100644 --- a/optimade/validator/utils.py +++ b/optimade/validator/utils.py @@ -408,13 +408,11 @@ class ValidatorLinksResponse(Success): class ValidatorEntryResponseOne(Success): - meta: ResponseMeta = Field(...) data: EntryResource = Field(...) included: Optional[List[Dict[str, Any]]] = Field(None) # type: ignore[assignment] class ValidatorEntryResponseMany(Success): - meta: ResponseMeta = Field(...) data: List[EntryResource] = Field(...) included: Optional[List[Dict[str, Any]]] = Field(None) # type: ignore[assignment] From 64b97bdfe24d43caaddc6992cfcfa3786e22246d Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Tue, 5 Sep 2023 17:24:26 +0200 Subject: [PATCH 59/86] Fixed validator for format field. --- optimade/models/entries.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/optimade/models/entries.py b/optimade/models/entries.py index a4f6a8333..7ef355042 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -111,11 +111,15 @@ class PartialDataLink(BaseModel): description='String. The name of the format provided via this link. For one of the objects this format field SHOULD have the value "jsonlines", which refers to the format in OPTIMADE JSON lines partial data format.', ) # todo add check that the value of format is in a list of supported formats. I have already written the validator below but it causes an error I do not understand - # @validator("format", always=False, check_fields=False) - # def check_if_format_is_supported(cls, value): - # from optimade.server.config import CONFIG - # if value not in [form.value for form in CONFIG.enabled_response_formats]: - # raise ValueError(f"The format {value} is not one of the enabled_formats{CONFIG.enabled_response_formats}.") + @validator("format", always=False, check_fields=False) + def check_if_format_is_supported(cls, value): + from optimade.server.config import CONFIG + + if value not in [form.value for form in CONFIG.enabled_response_formats]: + raise ValueError( + f"The format {value} is not one of the enabled_formats{CONFIG.enabled_response_formats}." + ) + return value class EntryMetadata(Meta): From c908d3f448c74f7515098d5e5854284614678841 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Tue, 12 Sep 2023 16:32:48 +0200 Subject: [PATCH 60/86] first minimal version partial data. --- openapi/openapi.json | 43 +++-- optimade/models/entries.py | 9 +- optimade/server/config.py | 5 +- optimade/server/data/__init__.py | 13 +- ...s => mpf_551:cartesian_site_positions.npy} | Bin .../entry_collections/entry_collections.py | 15 +- optimade/server/entry_collections/mongo.py | 154 ++++++++++++++---- optimade/server/main.py | 27 ++- optimade/server/query_params.py | 9 +- optimade/server/routers/structures.py | 2 +- optimade/server/routers/utils.py | 39 ++++- tests/server/routers/test_partial_data.py | 9 + 12 files changed, 251 insertions(+), 74 deletions(-) rename optimade/server/data/{mpf_551:cartesian_site_positions => mpf_551:cartesian_site_positions.npy} (100%) diff --git a/openapi/openapi.json b/openapi/openapi.json index e4abb6de1..1f7fbcf11 100644 --- a/openapi/openapi.json +++ b/openapi/openapi.json @@ -784,6 +784,17 @@ }, "name": "api_hint", "in": "query" + }, + { + "description": "A list of lists which contains a range for each dimension of the property.", + "required": false, + "schema": { + "title": "Property Ranges", + "type": "string", + "description": "A list of lists which contains a range for each dimension of the property." + }, + "name": "property_ranges", + "in": "query" } ], "responses": { @@ -1202,6 +1213,17 @@ }, "name": "api_hint", "in": "query" + }, + { + "description": "A list of lists which contains a range for each dimension of the property.", + "required": false, + "schema": { + "title": "Property Ranges", + "type": "string", + "description": "A list of lists which contains a range for each dimension of the property." + }, + "name": "property_ranges", + "in": "query" } ], "responses": { @@ -1395,10 +1417,9 @@ "required": false, "schema": { "title": "Property Ranges", - "type": "array", - "items": {}, + "type": "string", "description": "A list of lists which contains a range for each dimension of the property.", - "default": [] + "default": "" }, "name": "property_ranges", "in": "query" @@ -2964,8 +2985,8 @@ "type": "string", "description": "The URL of the reference.", "format": "uri", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "address": { "title": "Address", @@ -3825,8 +3846,8 @@ }, "description": "List of three integers.\nFor each of the three directions indicated by the three lattice vectors (see property `lattice_vectors`), this list indicates if the direction is periodic (value `1`) or non-periodic (value `0`).\nNote: the elements in this list each refer to the direction of the corresponding entry in `lattice_vectors` and *not* the Cartesian x, y, z directions.\n\n- **Type**: list of integers.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n - MUST be a list of length 3.\n - Each integer element MUST assume only the value 0 or 1.\n\n- **Examples**:\n - For a molecule: `[0, 0, 0]`\n - For a wire along the direction specified by the third lattice vector: `[0, 0, 1]`\n - For a 2D surface/slab, periodic on the plane defined by the first and third lattice vectors: `[1, 0, 1]`\n - For a bulk 3D system: `[1, 1, 1]`", "nullable": true, - "x-optimade-support": "should", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "should" }, "nperiodic_dimensions": { "title": "Nperiodic Dimensions", @@ -3851,9 +3872,9 @@ }, "description": "The three lattice vectors in Cartesian coordinates, in \u00e5ngstr\u00f6m (\u00c5).\n\n- **Type**: list of list of floats or unknown values.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - MUST be a list of three vectors *a*, *b*, and *c*, where each of the vectors MUST BE a list of the vector's coordinates along the x, y, and z Cartesian coordinates.\n (Therefore, the first index runs over the three lattice vectors and the second index runs over the x, y, z Cartesian coordinates).\n - For databases that do not define an absolute Cartesian system (e.g., only defining the length and angles between vectors), the first lattice vector SHOULD be set along *x* and the second on the *xy*-plane.\n - MUST always contain three vectors of three coordinates each, independently of the elements of property `dimension_types`.\n The vectors SHOULD by convention be chosen so the determinant of the `lattice_vectors` matrix is different from zero.\n The vectors in the non-periodic directions have no significance beyond fulfilling these requirements.\n - The coordinates of the lattice vectors of non-periodic dimensions (i.e., those dimensions for which `dimension_types` is `0`) MAY be given as a list of all `null` values.\n If a lattice vector contains the value `null`, all coordinates of that lattice vector MUST be `null`.\n\n- **Examples**:\n - `[[4.0,0.0,0.0],[0.0,4.0,0.0],[0.0,1.0,4.0]]` represents a cell, where the first vector is `(4, 0, 0)`, i.e., a vector aligned along the `x` axis of length 4 \u00c5; the second vector is `(0, 4, 0)`; and the third vector is `(0, 1, 4)`.", "nullable": true, - "x-optimade-support": "should", + "x-optimade-queryable": "optional", "x-optimade-unit": "\u00c5", - "x-optimade-queryable": "optional" + "x-optimade-support": "should" }, "cartesian_site_positions": { "title": "Cartesian Site Positions", @@ -3868,9 +3889,9 @@ }, "description": "Cartesian positions of each site in the structure.\nA site is usually used to describe positions of atoms; what atoms can be encountered at a given site is conveyed by the `species_at_sites` property, and the species themselves are described in the `species` property.\n\n- **Type**: list of list of floats\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - It MUST be a list of length equal to the number of sites in the structure, where every element is a list of the three Cartesian coordinates of a site expressed as float values in the unit angstrom (\u00c5).\n - An entry MAY have multiple sites at the same Cartesian position (for a relevant use of this, see e.g., the property `assemblies`).\n\n- **Examples**:\n - `[[0,0,0],[0,0,2]]` indicates a structure with two sites, one sitting at the origin and one along the (positive) *z*-axis, 2 \u00c5 away from the origin.", "nullable": true, - "x-optimade-support": "should", + "x-optimade-queryable": "optional", "x-optimade-unit": "\u00c5", - "x-optimade-queryable": "optional" + "x-optimade-support": "should" }, "nsites": { "title": "Nsites", diff --git a/optimade/models/entries.py b/optimade/models/entries.py index 7ef355042..7719aba6b 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -101,7 +101,6 @@ def cast_immutable_id_to_str(cls, value): class PartialDataLink(BaseModel): - # Todo add validator for link field. link: AnyUrl = OptimadeField( ..., description="String. A JSON API link that points to a location from which the omitted data can be fetched. There is no requirement on the syntax or format for the link URL.", @@ -109,15 +108,15 @@ class PartialDataLink(BaseModel): format: str = OptimadeField( ..., description='String. The name of the format provided via this link. For one of the objects this format field SHOULD have the value "jsonlines", which refers to the format in OPTIMADE JSON lines partial data format.', - ) # todo add check that the value of format is in a list of supported formats. I have already written the validator below but it causes an error I do not understand + ) - @validator("format", always=False, check_fields=False) + @validator("format") def check_if_format_is_supported(cls, value): from optimade.server.config import CONFIG - if value not in [form.value for form in CONFIG.enabled_response_formats]: + if value not in [form.value for form in CONFIG.partial_data_formats]: raise ValueError( - f"The format {value} is not one of the enabled_formats{CONFIG.enabled_response_formats}." + f"The format {value} is not one of the enabled_formats{CONFIG.partial_data_formats}." ) return value diff --git a/optimade/server/config.py b/optimade/server/config.py index a9f0ae778..cddc3bdeb 100644 --- a/optimade/server/config.py +++ b/optimade/server/config.py @@ -76,7 +76,6 @@ class SupportedResponseFormats(Enum): """ - HDF5 = "hdf5" JSON = "json" JSONL = "jsonlines" @@ -315,11 +314,11 @@ class ServerConfig(BaseSettings): description="""If False, data from the database will not undergo validation before being emitted by the API, and only the mapping of aliases will occur.""", ) - enabled_response_formats: List[SupportedResponseFormats] = Field( + partial_data_formats: List[SupportedResponseFormats] = Field( ["json", "jsonlines"], description="""A list of the response formats that are supported by this server. Must include the "json" format.""", ) - max_response_size: Optional[Dict[SupportedResponseFormats, int]] = Field( + max_response_size: Dict[SupportedResponseFormats, int] = Field( {"json": 10, "jsonlines": 40}, description="""This dictionary contains the approximate maximum size for a trajectory response in megabytes for the different response_formats. The keys indicate the response_format and the values the maximum size.""", ) diff --git a/optimade/server/data/__init__.py b/optimade/server/data/__init__.py index dd9c628bb..b4cee3665 100644 --- a/optimade/server/data/__init__.py +++ b/optimade/server/data/__init__.py @@ -10,7 +10,18 @@ "providers": "providers.json", } -data_files = ["mpf_551:cartesian_site_positions"] +data_files = [ + ( + "mpf_551:cartesian_site_positions.npy", + "numpy", + { + "endpoint": "structures", + "parent_id": "mpf_551", + "property_name": "cartesian_site_positions", + "dim_names": ["dim_sites", "dim_cartesian_dimensions"], + }, + ) +] for var, path in data_paths.items(): try: diff --git a/optimade/server/data/mpf_551:cartesian_site_positions b/optimade/server/data/mpf_551:cartesian_site_positions.npy similarity index 100% rename from optimade/server/data/mpf_551:cartesian_site_positions rename to optimade/server/data/mpf_551:cartesian_site_positions.npy diff --git a/optimade/server/entry_collections/entry_collections.py b/optimade/server/entry_collections/entry_collections.py index a5aa8756c..345b9f5f0 100644 --- a/optimade/server/entry_collections/entry_collections.py +++ b/optimade/server/entry_collections/entry_collections.py @@ -9,8 +9,11 @@ from optimade.exceptions import BadRequest, Forbidden, NotFound from optimade.filterparser import LarkParser from optimade.models.entries import EntryResource -from optimade.server.config import CONFIG, SupportedBackend -from optimade.server.mappers import BaseResourceMapper # type: ignore[attr-defined] +from optimade.server.config import CONFIG, SupportedBackend, SupportedResponseFormats +from optimade.server.mappers import ( # type: ignore[attr-defined] + BaseResourceMapper, + PartialDataMapper, +) from optimade.server.query_params import ( EntryListingQueryParams, PartialDataQueryParams, @@ -190,7 +193,7 @@ def find( supported_prefixes = self.resource_mapper.SUPPORTED_PREFIXES all_attributes = self.resource_mapper.ALL_ATTRIBUTES - if not isinstance(params, PartialDataQueryParams): + if not self.resource_mapper == PartialDataMapper: include_fields = ( response_fields - self.resource_mapper.TOP_LEVEL_NON_ATTRIBUTES_FIELDS ) @@ -248,7 +251,7 @@ def generate_links_partial_data(self, results): for entry in results: if entry.get("meta", {}) and entry["meta"].get("partial_data_links", {}): for property in entry["meta"]["partial_data_links"]: - for response_format in CONFIG.enabled_response_formats: + for response_format in CONFIG.partial_data_formats: entry["meta"]["partial_data_links"][property].append( { "format": str(response_format.value), @@ -375,10 +378,10 @@ def handle_query_params( # response_format if getattr(params, "response_format", False) and params.response_format not in ( - x.value for x in CONFIG.enabled_response_formats + x.value for x in SupportedResponseFormats ): raise BadRequest( - detail=f"Response format {params.response_format} is not supported, please use one of the supported response formats: {', '.join((x.value for x in CONFIG.enabled_response_formats))}" + detail=f"Response format {params.response_format} is not supported, please use one of the supported response formats: {', '.join((x.value for x in SupportedResponseFormats))}" ) # page_limit diff --git a/optimade/server/entry_collections/mongo.py b/optimade/server/entry_collections/mongo.py index f46d30a34..3852e148f 100644 --- a/optimade/server/entry_collections/mongo.py +++ b/optimade/server/entry_collections/mongo.py @@ -1,8 +1,9 @@ from typing import Any, Dict, List, Tuple, Type, Union +from optimade.exceptions import BadRequest from optimade.filtertransformers.mongo import MongoTransformer from optimade.models import EntryResource # type: ignore[attr-defined] -from optimade.server.config import CONFIG, SupportedBackend +from optimade.server.config import CONFIG, SupportedBackend, SupportedResponseFormats from optimade.server.entry_collections import EntryCollection from optimade.server.logger import LOGGER from optimade.server.mappers import BaseResourceMapper # type: ignore[attr-defined] @@ -100,23 +101,21 @@ def count(self, **kwargs: Any) -> int: kwargs["filter"] = {} return len(self.collection.find(**kwargs)) - def insert(self, content: bytes, filename: str) -> None: + def insert(self, content: bytes, filename: str, metadata: dict = {}) -> None: """Add the given entries to the underlying database. Warning: No validation is performed on the incoming data. Arguments: - data: The entry resource objects to add to the database. - + content: The file content to add to gridfs. + filename: The filename of the added content. + metadata: extra metadata to add to the gridfs entry. """ - self.collection.put(content, filename=filename) + self.collection.put(content, filename=filename, metadata=metadata) def handle_query_params( - self, - params: Union[ - EntryListingQueryParams, SingleEntryQueryParams, PartialDataQueryParams - ], + self, params: Union[SingleEntryQueryParams, PartialDataQueryParams] ) -> Dict[str, Any]: """Parse and interpret the backend-agnostic query parameter models into a dictionary that can be used by MongoDB. @@ -155,9 +154,21 @@ def handle_query_params( if isinstance(params, PartialDataQueryParams): entry_id = params.filter.split("=")[1][1:-1] criteria["filter"] = { - "filename": {"$eq": f"{entry_id}:{params.response_fields}"} + "filename": { + "$eq": f"{entry_id}:{params.response_fields}.npy" + } # todo Should we add support for other file extensions? } # Todo make sure response fields has only one value + # response_format + if getattr(params, "response_format", False) and params.response_format not in ( + x.value for x in CONFIG.partial_data_formats + ): + raise BadRequest( + detail=f"Response format {params.response_format} is not supported, please use one of the supported response formats: {', '.join((x.value for x in CONFIG.partial_data_formats))}" + ) + criteria["response_format"] = params.response_format + criteria["property_ranges"] = params.property_ranges + return criteria # todo test if it is more efficient to use the get method of gridfs @@ -179,41 +190,112 @@ def _run_db_query( """ # TODO handle case where the type does not have a fixed width. For example strings or dictionaries. - - max_return_size = 10000000 - property_dimensions: list[int] = [] # todo read this data from metadata + response_format = criteria.pop("response_format") + max_return_size = CONFIG.max_response_size[ + SupportedResponseFormats(response_format) + ] # todo adjust for different output formats(take into account that the number of numbers to read is larger for a text based output format than for a binary format. results = [] filterdict = criteria.pop("filter", {}) - criteria.pop("requested_range", property_dimensions) - gridcursor = self.collection.find( - filterdict - ) # I have tried to use just **criteria as is mentioned in the documentation but this does not seem to work. + + # I have tried to use just **criteria as is mentioned in the documentation but this does not seem to work. + gridcursor = self.collection.find(filterdict) more_data_available = False nresults = 0 - for file_obj in gridcursor: - nresults = +1 - property_name = file_obj.filename.split(":")[1] - # results.append(grid_out.read()) - if file_obj.length > max_return_size: - header = file_obj.readline() - values = header + file_obj.read() - # TODo handle splitting up file + # todo add code that can handle very sparse requests where reading individual sections of files is more efficient. + for ( + file_obj + ) in ( + gridcursor + ): # Next throws an error when there are zero files returned, so I use a for loop instead to get one result. + nresults += 1 + metadata = file_obj.metadata + property_ranges = self.parse_property_ranges( + criteria.pop("property_ranges", None), + metadata["sliceobj"], + metadata["dim_names"], + ) + item_size = metadata["dtype"]["itemsize"] + dim_sizes = [ + (i["stop"] - i["start"] + 1) // i["step"] for i in metadata["sliceobj"] + ] + top_stepsize = 1 + for i in dim_sizes[1:]: + top_stepsize *= i + offset = (property_ranges[0]["start"] - 1) * item_size * top_stepsize + file_obj.seek( + offset + ) # set the correct starting point fo the read from the gridfs file system. + if (max_return_size / item_size) < ( + 1 + property_ranges[0]["stop"] - property_ranges[0]["start"] + ) * top_stepsize: # case more data requested then fits in the response more_data_available = True - else: # case whole file can be returned. - values = file_obj.read() - - results = [ - { - "type": "partial_data", - "id": str(file_obj._id), - "property_name": property_name, - "data": values, - } - ] + n_val_return = max_return_size / item_size + n_outer = max( + int(n_val_return / top_stepsize), 1 + ) # always read at least one line for now. + read_size = n_outer * top_stepsize * item_size + shape = [n_outer] + dim_sizes[1:] + else: + read_size = ( + 1 + property_ranges[0]["stop"] - property_ranges[0]["start"] + ) * top_stepsize + shape = ( + 1 + property_ranges[0]["stop"] - property_ranges[0]["start"] + ) + dim_sizes[1:] + + values = file_obj.read(read_size) + entry = { + "id": metadata.get("parent_id", None), + "type": metadata.get("endpoint", None), + } + results = [ + { + "type": "partial_data", + "id": str(file_obj._id), + "property_name": metadata.get("property_name", None), + "entry": entry, + "data": values, + "dtype": metadata["dtype"], + "shape": shape, + "property_ranges": property_ranges, + } + ] + if more_data_available: + property_ranges_str = f"property_ranges={metadata['dim_names'][0]}:{property_ranges[0]['start']+n_outer}:{property_ranges[0]['stop']}:{property_ranges[0]['step']}" + for i, name in enumerate(metadata["dim_names"][1:]): + property_ranges_str += f",{name}:{property_ranges[i+1]['start']}:{property_ranges[i+1]['stop']}:{property_ranges[i+1]['step']}" + results[0][ + "next" + ] = f"{CONFIG.base_url}/partial_data/{metadata['parent_id']}?response_fields={metadata['property_name']}&response_format={response_format}&{property_ranges_str}" break return results, nresults, more_data_available + def parse_property_ranges( + self, property_range_str: str, attribute_sliceobj: list, dim_names: list + ) -> list[dict]: + property_range_dict = {} + if property_range_str: + ranges = [dimrange.split(":") for dimrange in property_range_str.split(",")] + + for subrange in ranges: + property_range_dict[subrange[0]] = { + "start": int(subrange[1]) + if subrange[1] + else attribute_sliceobj[dim_names.index(subrange[0])]["start"], + "stop": int(subrange[2]) + if subrange[2] + else attribute_sliceobj[dim_names.index(subrange[0])]["stop"], + "step": int(subrange[3]) + if subrange[3] + else attribute_sliceobj[dim_names.index(subrange[0])]["step"], + } + for i, dim in enumerate(dim_names): + if dim not in property_range_dict: + property_range_dict[dim] = attribute_sliceobj[i] + + return [property_range_dict[dim] for dim in dim_names] + class MongoCollection(MongoBaseCollection): """Class for querying MongoDB collections (implemented by either pymongo or mongomock) diff --git a/optimade/server/main.py b/optimade/server/main.py index 0fe687be1..56efd4c16 100644 --- a/optimade/server/main.py +++ b/optimade/server/main.py @@ -75,12 +75,33 @@ if CONFIG.database_backend.value in ("mongomock", "mongodb"): from pathlib import Path + import numpy + from optimade.server.routers.partial_data import partial_data_coll - for filename in getattr(data, "data_files", []): + # todo create seperate function for storing data files in gridfs + # read_array_header function originally from https://stackoverflow.com/a/64226659 by https://stackoverflow.com/users/982257/iguananaut + def read_array_header(fobj): + version = numpy.lib.format.read_magic(fobj) + func_name = "read_array_header_" + "_".join(str(v) for v in version) + func = getattr(numpy.lib.format, func_name) + return func(fobj) + + for filename, filetype, metadata in getattr(data, "data_files", []): with open(Path(__file__).parent / "data" / filename, "rb") as f: - a = partial_data_coll.insert(f, filename=filename) - pass + if filetype == "numpy": + numpy_meta = read_array_header(f) + if "slice_obj" not in metadata: + slice_obj = [ + {"start": 1, "stop": i, "step": 1} for i in numpy_meta[0] + ] + metadata["sliceobj"] = slice_obj + if "dtype" not in metadata: + metadata["dtype"] = { + "name": numpy_meta[2].name, + "itemsize": numpy_meta[2].itemsize, + } + partial_data_coll.insert(f, filename=filename, metadata=metadata) def load_entries(endpoint_name: str, endpoint_collection: EntryCollection): LOGGER.debug("Loading test %s...", endpoint_name) diff --git a/optimade/server/query_params.py b/optimade/server/query_params.py index 50686321b..30561c0cd 100644 --- a/optimade/server/query_params.py +++ b/optimade/server/query_params.py @@ -324,12 +324,17 @@ def __init__( description="If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.", regex=r"(v[0-9]+(\.[0-9]+)?)?", ), + property_ranges: str = Query( + None, + description="A list of lists which contains a range for each dimension of the property.", + ), ): self.response_format = response_format self.email_address = email_address self.response_fields = response_fields self.include = include self.api_hint = api_hint + self.property_ranges = property_ranges class PartialDataQueryParams(BaseQueryParams): @@ -384,8 +389,8 @@ def __init__( "", description="A filter string, in the format described in section API Filtering Format Specification of the specification.", ), - property_ranges: list = Query( - [], + property_ranges: str = Query( + "", description="A list of lists which contains a range for each dimension of the property.", ), ): diff --git a/optimade/server/routers/structures.py b/optimade/server/routers/structures.py index ad77b33eb..21cbe9a9c 100644 --- a/optimade/server/routers/structures.py +++ b/optimade/server/routers/structures.py @@ -60,7 +60,7 @@ def get_single_structure( entry_id: str, params: SingleEntryQueryParams = Depends(), ) -> Any: - if "property_ranges" in request.query_params: # todo add test for this + if params.property_ranges is not None: # todo add test for this from optimade.server.routers.partial_data import partial_data_coll return get_partial_entry( diff --git a/optimade/server/routers/utils.py b/optimade/server/routers/utils.py index 7fa31267f..7cbb2257c 100644 --- a/optimade/server/routers/utils.py +++ b/optimade/server/routers/utils.py @@ -388,15 +388,35 @@ def get_partial_entry( links = ToplevelLinks(next=None) - if results is not None and (fields or include_fields): - results = handle_response_fields(results, fields, include_fields)[0] # type: ignore[assignment] if results is None: raise NotFound( detail=f"No data available for the combination of entry {entry_id} and property {params.response_fields}", ) + + array = np.frombuffer( + results["attributes"]["data"], + dtype=getattr(np, results["attributes"]["dtype"]["name"]), + ).reshape(results["attributes"]["shape"]) + # slice array + property_ranges = results["attributes"]["property_ranges"] + slice_ind = [ + slice( + 0, + 1 + property_ranges[0]["stop"] - property_ranges[0]["start"], + property_ranges[0]["step"], + ) + ] + for dim_range in property_ranges[1:]: + slice_ind.append( + slice(dim_range["start"] - 1, dim_range["stop"], dim_range["step"]) + ) + array = array[tuple(slice_ind)] + + if fields or include_fields: + results = handle_response_fields(results, fields, include_fields)[0] # type: ignore[assignment] + # todo make the implementation of these formats more universal. i.e. allow them for other endpoint as well, - array = np.load(io.BytesIO(results["attributes"]["data"])) sliceobj = [] for i in array.shape: sliceobj.append({"start": 1, "stop": i, "step": 1}) @@ -408,14 +428,18 @@ def get_partial_entry( # "entry": {"id": entry_id, "type": None}, #Todo add type information to metadata entry "has_references": False, } # Todo: add support for non_dense data + if more_data_available: + next_link = ["PARTIAL-DATA-NEXT", [results["attributes"].pop("next")]] if params.response_format == "json": for key in header: results["attributes"][key] = header[key] - results["attributes"]["data"] = array.tolist() + results["attributes"]["data"] = array.tolist() + if more_data_available: + results["attributes"]["next"] = next_link return dict( links=links, - data=results if results else None, + data=[results] if results else None, meta=meta_values( url=request.url, data_returned=data_returned, @@ -428,8 +452,11 @@ def get_partial_entry( # included=included, ) + jsonl_content = [header, array.tolist()] + if more_data_available: + jsonl_content.append(next_link) return Response( - content=to_jsonl([header, array.tolist()]), + content=to_jsonl(jsonl_content), media_type="application/jsonlines", headers={ "Content-disposition": f"attachment; filename={entry_id + ':' + params.response_fields}.jsonl" diff --git a/tests/server/routers/test_partial_data.py b/tests/server/routers/test_partial_data.py index 7ac1fda7c..5f0773d85 100644 --- a/tests/server/routers/test_partial_data.py +++ b/tests/server/routers/test_partial_data.py @@ -12,6 +12,15 @@ class TestPartialDataEndpoint(NoJsonEndpointTests): response_cls = PartialDataResponse +def test_property_ranges_link(get_good_response, client): + test_id = "mpf_551" + params = "response_fields=cartesian_site_positions&property_ranges=dim_sites:2:74:1,dim_cartesian_dimensions:1:3:1&response_format=json" + request = f"/partial_data/{test_id}?{params}" + get_good_response( + request, server=client + ) # todo expand test to check content better. + + def test_wrong_id_partial_data(check_error_response, client): """If a non-supported versioned base URL is passed, `553 Version Not Supported` should be returned From 04e9ff606610dc335c18b2a2e567770f9af23432 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Wed, 13 Sep 2023 14:21:52 +0200 Subject: [PATCH 61/86] update openapijson --- openapi/openapi.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/openapi/openapi.json b/openapi/openapi.json index e0bba9fa4..65c829df1 100644 --- a/openapi/openapi.json +++ b/openapi/openapi.json @@ -3359,9 +3359,9 @@ "type": "number" }, "description": "If present MUST be a list of floats expressed in a.m.u.\nElements denoting vacancies MUST have masses equal to 0.", - "x-optimade-unit": "a.m.u.", "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-unit": "a.m.u." }, "original_name": { "title": "Original Name", @@ -3640,8 +3640,8 @@ "description": "The three lattice vectors in Cartesian coordinates, in \u00e5ngstr\u00f6m (\u00c5).\n\n- **Type**: list of list of floats or unknown values.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - MUST be a list of three vectors *a*, *b*, and *c*, where each of the vectors MUST BE a list of the vector's coordinates along the x, y, and z Cartesian coordinates.\n (Therefore, the first index runs over the three lattice vectors and the second index runs over the x, y, z Cartesian coordinates).\n - For databases that do not define an absolute Cartesian system (e.g., only defining the length and angles between vectors), the first lattice vector SHOULD be set along *x* and the second on the *xy*-plane.\n - MUST always contain three vectors of three coordinates each, independently of the elements of property `dimension_types`.\n The vectors SHOULD by convention be chosen so the determinant of the `lattice_vectors` matrix is different from zero.\n The vectors in the non-periodic directions have no significance beyond fulfilling these requirements.\n - The coordinates of the lattice vectors of non-periodic dimensions (i.e., those dimensions for which `dimension_types` is `0`) MAY be given as a list of all `null` values.\n If a lattice vector contains the value `null`, all coordinates of that lattice vector MUST be `null`.\n\n- **Examples**:\n - `[[4.0,0.0,0.0],[0.0,4.0,0.0],[0.0,1.0,4.0]]` represents a cell, where the first vector is `(4, 0, 0)`, i.e., a vector aligned along the `x` axis of length 4 \u00c5; the second vector is `(0, 4, 0)`; and the third vector is `(0, 1, 4)`.", "nullable": true, "x-optimade-support": "should", - "x-optimade-unit": "\u00c5", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-unit": "\u00c5" }, "cartesian_site_positions": { "title": "Cartesian Site Positions", @@ -3657,8 +3657,8 @@ "description": "Cartesian positions of each site in the structure.\nA site is usually used to describe positions of atoms; what atoms can be encountered at a given site is conveyed by the `species_at_sites` property, and the species themselves are described in the `species` property.\n\n- **Type**: list of list of floats\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - It MUST be a list of length equal to the number of sites in the structure, where every element is a list of the three Cartesian coordinates of a site expressed as float values in the unit angstrom (\u00c5).\n - An entry MAY have multiple sites at the same Cartesian position (for a relevant use of this, see e.g., the property `assemblies`).\n\n- **Examples**:\n - `[[0,0,0],[0,0,2]]` indicates a structure with two sites, one sitting at the origin and one along the (positive) *z*-axis, 2 \u00c5 away from the origin.", "nullable": true, "x-optimade-support": "should", - "x-optimade-unit": "\u00c5", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-unit": "\u00c5" }, "nsites": { "title": "Nsites", From 536893481d79d40b159e8db1dedc61f8afe070ee Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Wed, 13 Sep 2023 14:29:52 +0200 Subject: [PATCH 62/86] update openapijson --- openapi/openapi.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openapi/openapi.json b/openapi/openapi.json index 65c829df1..d1d5491e8 100644 --- a/openapi/openapi.json +++ b/openapi/openapi.json @@ -3639,9 +3639,9 @@ }, "description": "The three lattice vectors in Cartesian coordinates, in \u00e5ngstr\u00f6m (\u00c5).\n\n- **Type**: list of list of floats or unknown values.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - MUST be a list of three vectors *a*, *b*, and *c*, where each of the vectors MUST BE a list of the vector's coordinates along the x, y, and z Cartesian coordinates.\n (Therefore, the first index runs over the three lattice vectors and the second index runs over the x, y, z Cartesian coordinates).\n - For databases that do not define an absolute Cartesian system (e.g., only defining the length and angles between vectors), the first lattice vector SHOULD be set along *x* and the second on the *xy*-plane.\n - MUST always contain three vectors of three coordinates each, independently of the elements of property `dimension_types`.\n The vectors SHOULD by convention be chosen so the determinant of the `lattice_vectors` matrix is different from zero.\n The vectors in the non-periodic directions have no significance beyond fulfilling these requirements.\n - The coordinates of the lattice vectors of non-periodic dimensions (i.e., those dimensions for which `dimension_types` is `0`) MAY be given as a list of all `null` values.\n If a lattice vector contains the value `null`, all coordinates of that lattice vector MUST be `null`.\n\n- **Examples**:\n - `[[4.0,0.0,0.0],[0.0,4.0,0.0],[0.0,1.0,4.0]]` represents a cell, where the first vector is `(4, 0, 0)`, i.e., a vector aligned along the `x` axis of length 4 \u00c5; the second vector is `(0, 4, 0)`; and the third vector is `(0, 1, 4)`.", "nullable": true, + "x-optimade-unit": "\u00c5", "x-optimade-support": "should", - "x-optimade-queryable": "optional", - "x-optimade-unit": "\u00c5" + "x-optimade-queryable": "optional" }, "cartesian_site_positions": { "title": "Cartesian Site Positions", @@ -3656,9 +3656,9 @@ }, "description": "Cartesian positions of each site in the structure.\nA site is usually used to describe positions of atoms; what atoms can be encountered at a given site is conveyed by the `species_at_sites` property, and the species themselves are described in the `species` property.\n\n- **Type**: list of list of floats\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - It MUST be a list of length equal to the number of sites in the structure, where every element is a list of the three Cartesian coordinates of a site expressed as float values in the unit angstrom (\u00c5).\n - An entry MAY have multiple sites at the same Cartesian position (for a relevant use of this, see e.g., the property `assemblies`).\n\n- **Examples**:\n - `[[0,0,0],[0,0,2]]` indicates a structure with two sites, one sitting at the origin and one along the (positive) *z*-axis, 2 \u00c5 away from the origin.", "nullable": true, + "x-optimade-unit": "\u00c5", "x-optimade-support": "should", - "x-optimade-queryable": "optional", - "x-optimade-unit": "\u00c5" + "x-optimade-queryable": "optional" }, "nsites": { "title": "Nsites", From bccbe7314662a5b249fd62169d6dd8972b01fea0 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Wed, 13 Sep 2023 15:26:24 +0200 Subject: [PATCH 63/86] update openapijson --- openapi/index_openapi.json | 810 +++++++++---------- openapi/openapi.json | 1534 ++++++++++++++++++------------------ 2 files changed, 1172 insertions(+), 1172 deletions(-) diff --git a/openapi/index_openapi.json b/openapi/index_openapi.json index 5b61b1288..2c2c3165f 100644 --- a/openapi/index_openapi.json +++ b/openapi/index_openapi.json @@ -1,5 +1,5 @@ { - "openapi": "3.0.2", + "openapi": "3.1.0", "info": { "title": "OPTIMADE API - Index meta-database", "description": "The [Open Databases Integration for Materials Design (OPTIMADE) consortium](https://www.optimade.org/) aims to make materials databases interoperational by developing a common REST API.\nThis is the \"special\" index meta-database.\n\nThis specification is generated using [`optimade-python-tools`](https://github.com/Materials-Consortia/optimade-python-tools/tree/v0.25.2) v0.25.2.", @@ -109,8 +109,8 @@ "description": "A filter string, in the format described in section API Filtering Format Specification of the specification.", "required": false, "schema": { - "title": "Filter", "type": "string", + "title": "Filter", "description": "A filter string, in the format described in section API Filtering Format Specification of the specification.", "default": "" }, @@ -121,8 +121,8 @@ "description": "The output format requested (see section Response Format).\nDefaults to the format string 'json', which specifies the standard output format described in this specification.\nExample: `http://example.com/v1/structures?response_format=xml`", "required": false, "schema": { - "title": "Response Format", "type": "string", + "title": "Response Format", "description": "The output format requested (see section Response Format).\nDefaults to the format string 'json', which specifies the standard output format described in this specification.\nExample: `http://example.com/v1/structures?response_format=xml`", "default": "json" }, @@ -133,10 +133,10 @@ "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "required": false, "schema": { - "title": "Email Address", "type": "string", - "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "format": "email", + "title": "Email Address", + "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "default": "" }, "name": "email_address", @@ -146,9 +146,9 @@ "description": "A comma-delimited set of fields to be provided in the output.\nIf provided, these fields MUST be returned along with the REQUIRED fields.\nOther OPTIONAL fields MUST NOT be returned when this parameter is present.\nExample: `http://example.com/v1/structures?response_fields=last_modified,nsites`", "required": false, "schema": { - "title": "Response Fields", - "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", "type": "string", + "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", + "title": "Response Fields", "description": "A comma-delimited set of fields to be provided in the output.\nIf provided, these fields MUST be returned along with the REQUIRED fields.\nOther OPTIONAL fields MUST NOT be returned when this parameter is present.\nExample: `http://example.com/v1/structures?response_fields=last_modified,nsites`", "default": "" }, @@ -159,9 +159,9 @@ "description": "If supporting sortable queries, an implementation MUST use the `sort` query parameter with format as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-sorting).\n\nAn implementation MAY support multiple sort fields for a single query.\nIf it does, it again MUST conform to the JSON API 1.0 specification.\n\nIf an implementation supports sorting for an entry listing endpoint, then the `/info/` endpoint MUST include, for each field name `` in its `data.properties.` response value that can be used for sorting, the key `sortable` with value `true`.\nIf a field name under an entry listing endpoint supporting sorting cannot be used for sorting, the server MUST either leave out the `sortable` key or set it equal to `false` for the specific field name.\nThe set of field names, with `sortable` equal to `true` are allowed to be used in the \"sort fields\" list according to its definition in the JSON API 1.0 specification.\nThe field `sortable` is in addition to each property description and other OPTIONAL fields.\nAn example is shown in the section Entry Listing Info Endpoints.", "required": false, "schema": { - "title": "Sort", - "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", "type": "string", + "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", + "title": "Sort", "description": "If supporting sortable queries, an implementation MUST use the `sort` query parameter with format as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-sorting).\n\nAn implementation MAY support multiple sort fields for a single query.\nIf it does, it again MUST conform to the JSON API 1.0 specification.\n\nIf an implementation supports sorting for an entry listing endpoint, then the `/info/` endpoint MUST include, for each field name `` in its `data.properties.` response value that can be used for sorting, the key `sortable` with value `true`.\nIf a field name under an entry listing endpoint supporting sorting cannot be used for sorting, the server MUST either leave out the `sortable` key or set it equal to `false` for the specific field name.\nThe set of field names, with `sortable` equal to `true` are allowed to be used in the \"sort fields\" list according to its definition in the JSON API 1.0 specification.\nThe field `sortable` is in addition to each property description and other OPTIONAL fields.\nAn example is shown in the section Entry Listing Info Endpoints.", "default": "" }, @@ -172,9 +172,9 @@ "description": "Sets a numerical limit on the number of entries returned.\nSee [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-pagination).\nThe API implementation MUST return no more than the number specified.\nIt MAY return fewer.\nThe database MAY have a maximum limit and not accept larger numbers (in which case an error code -- 403 Forbidden -- MUST be returned).\nThe default limit value is up to the API implementation to decide.\nExample: `http://example.com/optimade/v1/structures?page_limit=100`", "required": false, "schema": { - "title": "Page Limit", - "minimum": 0.0, "type": "integer", + "minimum": 0.0, + "title": "Page Limit", "description": "Sets a numerical limit on the number of entries returned.\nSee [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-pagination).\nThe API implementation MUST return no more than the number specified.\nIt MAY return fewer.\nThe database MAY have a maximum limit and not accept larger numbers (in which case an error code -- 403 Forbidden -- MUST be returned).\nThe default limit value is up to the API implementation to decide.\nExample: `http://example.com/optimade/v1/structures?page_limit=100`", "default": 20 }, @@ -185,9 +185,9 @@ "description": "RECOMMENDED for use with _offset-based_ pagination: using `page_offset` and `page_limit` is RECOMMENDED.\nExample: Skip 50 structures and fetch up to 100: `/structures?page_offset=50&page_limit=100`.", "required": false, "schema": { - "title": "Page Offset", - "minimum": 0.0, "type": "integer", + "minimum": 0.0, + "title": "Page Offset", "description": "RECOMMENDED for use with _offset-based_ pagination: using `page_offset` and `page_limit` is RECOMMENDED.\nExample: Skip 50 structures and fetch up to 100: `/structures?page_offset=50&page_limit=100`.", "default": 0 }, @@ -198,8 +198,8 @@ "description": "RECOMMENDED for use with _page-based_ pagination: using `page_number` and `page_limit` is RECOMMENDED.\nIt is RECOMMENDED that the first page has number 1, i.e., that `page_number` is 1-based.\nExample: Fetch page 2 of up to 50 structures per page: `/structures?page_number=2&page_limit=50`.", "required": false, "schema": { - "title": "Page Number", "type": "integer", + "title": "Page Number", "description": "RECOMMENDED for use with _page-based_ pagination: using `page_number` and `page_limit` is RECOMMENDED.\nIt is RECOMMENDED that the first page has number 1, i.e., that `page_number` is 1-based.\nExample: Fetch page 2 of up to 50 structures per page: `/structures?page_number=2&page_limit=50`." }, "name": "page_number", @@ -209,9 +209,9 @@ "description": "RECOMMENDED for use with _cursor-based_ pagination: using `page_cursor` and `page_limit` is RECOMMENDED.", "required": false, "schema": { - "title": "Page Cursor", - "minimum": 0.0, "type": "integer", + "minimum": 0.0, + "title": "Page Cursor", "description": "RECOMMENDED for use with _cursor-based_ pagination: using `page_cursor` and `page_limit` is RECOMMENDED.", "default": 0 }, @@ -222,8 +222,8 @@ "description": "RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED.\nExample: Fetch up to 100 structures above sort-field value 4000 (in this example, server chooses to fetch results sorted by increasing `id`, so `page_above` value refers to an `id` value): `/structures?page_above=4000&page_limit=100`.", "required": false, "schema": { - "title": "Page Above", "type": "string", + "title": "Page Above", "description": "RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED.\nExample: Fetch up to 100 structures above sort-field value 4000 (in this example, server chooses to fetch results sorted by increasing `id`, so `page_above` value refers to an `id` value): `/structures?page_above=4000&page_limit=100`." }, "name": "page_above", @@ -233,8 +233,8 @@ "description": "RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED.", "required": false, "schema": { - "title": "Page Below", "type": "string", + "title": "Page Below", "description": "RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED." }, "name": "page_below", @@ -244,8 +244,8 @@ "description": "A server MAY implement the JSON API concept of returning [compound documents](https://jsonapi.org/format/1.0/#document-compound-documents) by utilizing the `include` query parameter as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-includes).\n\nAll related resource objects MUST be returned as part of an array value for the top-level `included` field, see the section JSON Response Schema: Common Fields.\n\nThe value of `include` MUST be a comma-separated list of \"relationship paths\", as defined in the [JSON API](https://jsonapi.org/format/1.0/#fetching-includes).\nIf relationship paths are not supported, or a server is unable to identify a relationship path a `400 Bad Request` response MUST be made.\n\nThe **default value** for `include` is `references`.\nThis means `references` entries MUST always be included under the top-level field `included` as default, since a server assumes if `include` is not specified by a client in the request, it is still specified as `include=references`.\nNote, if a client explicitly specifies `include` and leaves out `references`, `references` resource objects MUST NOT be included under the top-level field `included`, as per the definition of `included`, see section JSON Response Schema: Common Fields.\n\n> **Note**: A query with the parameter `include` set to the empty string means no related resource objects are to be returned under the top-level field `included`.", "required": false, "schema": { - "title": "Include", "type": "string", + "title": "Include", "description": "A server MAY implement the JSON API concept of returning [compound documents](https://jsonapi.org/format/1.0/#document-compound-documents) by utilizing the `include` query parameter as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-includes).\n\nAll related resource objects MUST be returned as part of an array value for the top-level `included` field, see the section JSON Response Schema: Common Fields.\n\nThe value of `include` MUST be a comma-separated list of \"relationship paths\", as defined in the [JSON API](https://jsonapi.org/format/1.0/#fetching-includes).\nIf relationship paths are not supported, or a server is unable to identify a relationship path a `400 Bad Request` response MUST be made.\n\nThe **default value** for `include` is `references`.\nThis means `references` entries MUST always be included under the top-level field `included` as default, since a server assumes if `include` is not specified by a client in the request, it is still specified as `include=references`.\nNote, if a client explicitly specifies `include` and leaves out `references`, `references` resource objects MUST NOT be included under the top-level field `included`, as per the definition of `included`, see section JSON Response Schema: Common Fields.\n\n> **Note**: A query with the parameter `include` set to the empty string means no related resource objects are to be returned under the top-level field `included`.", "default": "references" }, @@ -256,9 +256,9 @@ "description": "If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.", "required": false, "schema": { - "title": "Api Hint", - "pattern": "(v[0-9]+(\\.[0-9]+)?)?", "type": "string", + "pattern": "(v[0-9]+(\\.[0-9]+)?)?", + "title": "Api Hint", "description": "If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.", "default": "" }, @@ -376,42 +376,36 @@ "components": { "schemas": { "Aggregate": { - "title": "Aggregate", "enum": [ "ok", "test", "staging", "no" ], + "title": "Aggregate", "description": "Enumeration of aggregate values" }, "Attributes": { - "title": "Attributes", - "type": "object", "properties": {}, + "type": "object", + "title": "Attributes", "description": "Members of the attributes object (\"attributes\") represent information about the resource object in which it's defined.\nThe keys for Attributes MUST NOT be:\n relationships\n links\n id\n type" }, "AvailableApiVersion": { - "title": "AvailableApiVersion", - "required": [ - "url", - "version" - ], - "type": "object", "properties": { "url": { - "title": "Url", + "type": "string", "maxLength": 65536, "minLength": 1, "pattern": ".+/v[0-1](\\.[0-9]+)*/?$", - "type": "string", - "description": "A string specifying a versioned base URL that MUST adhere to the rules in section Base URL", - "format": "uri" + "format": "uri", + "title": "Url", + "description": "A string specifying a versioned base URL that MUST adhere to the rules in section Base URL" }, "version": { - "title": "Version", - "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", "type": "string", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", + "title": "Version", "description": "A string containing the full version number of the API served at that versioned base URL.\nThe version number string MUST NOT be prefixed by, e.g., 'v'.\nExamples: `1.0.0`, `1.0.0-rc.2`.", "example": [ "0.10.1", @@ -420,448 +414,446 @@ ] } }, + "type": "object", + "required": [ + "url", + "version" + ], + "title": "AvailableApiVersion", "description": "A JSON object containing information about an available API version" }, "BaseRelationshipMeta": { - "title": "BaseRelationshipMeta", - "required": [ - "description" - ], - "type": "object", "properties": { "description": { - "title": "Description", "type": "string", + "title": "Description", "description": "OPTIONAL human-readable description of the relationship." } }, + "type": "object", + "required": [ + "description" + ], + "title": "BaseRelationshipMeta", "description": "Specific meta field for base relationship resource" }, "BaseRelationshipResource": { - "title": "BaseRelationshipResource", - "required": [ - "id", - "type" - ], - "type": "object", "properties": { "id": { - "title": "Id", "type": "string", + "title": "Id", "description": "Resource ID" }, "type": { - "title": "Type", "type": "string", + "title": "Type", "description": "Resource type" }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/BaseRelationshipMeta" } ], + "title": "Meta", "description": "Relationship meta field. MUST contain 'description' if supplied." } }, + "type": "object", + "required": [ + "id", + "type" + ], + "title": "BaseRelationshipResource", "description": "Minimum requirements to represent a relationship resource" }, "EntryMetadata": { - "title": "EntryMetadata", - "type": "object", "properties": { "property_metadata": { - "title": "Property Metadata", "type": "object", + "title": "Property Metadata", "description": "A dictionary, where the keys are the names of the properties in the attributes field and the value is a dictionary containing the metadata for that property.\nDatabase-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes)." } }, + "type": "object", + "title": "EntryMetadata", "description": "Contains the metadata for the attributes of an entry" }, "EntryRelationships": { - "title": "EntryRelationships", - "type": "object", "properties": { "references": { - "title": "References", "allOf": [ { "$ref": "#/components/schemas/ReferenceRelationship" } ], + "title": "References", "description": "Object containing links to relationships with entries of the `references` type." }, "structures": { - "title": "Structures", "allOf": [ { "$ref": "#/components/schemas/StructureRelationship" } ], + "title": "Structures", "description": "Object containing links to relationships with entries of the `structures` type." } }, + "type": "object", + "title": "EntryRelationships", "description": "This model wraps the JSON API Relationships to include type-specific top level keys." }, "EntryResource": { - "title": "EntryResource", - "required": [ - "id", - "type", - "attributes" - ], - "type": "object", "properties": { "id": { - "title": "Id", "type": "string", + "title": "Id", "description": "An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`", "x-optimade-support": "must", "x-optimade-queryable": "must" }, "type": { - "title": "Type", "type": "string", + "title": "Type", "description": "The name of the type of an entry.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type `` and ID `` MUST be returned in response to a request for `//` under the versioned base URL.\n\n- **Example**: `\"structures\"`", "x-optimade-support": "must", "x-optimade-queryable": "must" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ResourceLinks" } ], + "title": "Links", "description": "a links object containing links related to the resource." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/EntryMetadata" } ], + "title": "Meta", "description": "A dictionary, containing entry and property-specific metadata for a given entry." }, "attributes": { - "title": "Attributes", "allOf": [ { "$ref": "#/components/schemas/EntryResourceAttributes" } ], + "title": "Attributes", "description": "A dictionary, containing key-value pairs representing the entry's properties, except for `type` and `id`.\nDatabase-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes)." }, "relationships": { - "title": "Relationships", "allOf": [ { "$ref": "#/components/schemas/EntryRelationships" } ], + "title": "Relationships", "description": "A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object." } }, + "type": "object", + "required": [ + "id", + "type", + "attributes" + ], + "title": "EntryResource", "description": "The base model for an entry resource." }, "EntryResourceAttributes": { - "title": "EntryResourceAttributes", - "required": [ - "last_modified" - ], - "type": "object", "properties": { "immutable_id": { - "title": "Immutable Id", "type": "string", + "title": "Immutable Id", "description": "The entry's immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)", "x-optimade-support": "optional", "x-optimade-queryable": "must" }, "last_modified": { - "title": "Last Modified", "type": "string", - "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "format": "date-time", + "title": "Last Modified", + "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "x-optimade-support": "should", "x-optimade-queryable": "must" } }, + "type": "object", + "required": [ + "last_modified" + ], + "title": "EntryResourceAttributes", "description": "Contains key-value pairs representing the entry's properties." }, "Error": { - "title": "Error", - "type": "object", "properties": { "id": { - "title": "Id", "type": "string", + "title": "Id", "description": "A unique identifier for this particular occurrence of the problem." }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ErrorLinks" } ], + "title": "Links", "description": "A links object storing about" }, "status": { - "title": "Status", "type": "string", + "title": "Status", "description": "the HTTP status code applicable to this problem, expressed as a string value." }, "code": { - "title": "Code", "type": "string", + "title": "Code", "description": "an application-specific error code, expressed as a string value." }, "title": { - "title": "Title", "type": "string", + "title": "Title", "description": "A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization." }, "detail": { - "title": "Detail", "type": "string", + "title": "Detail", "description": "A human-readable explanation specific to this occurrence of the problem." }, "source": { - "title": "Source", "allOf": [ { "$ref": "#/components/schemas/ErrorSource" } ], + "title": "Source", "description": "An object containing references to the source of the error" }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "a meta object containing non-standard meta-information about the error." } }, + "type": "object", + "title": "Error", "description": "An error response" }, "ErrorLinks": { - "title": "ErrorLinks", - "type": "object", "properties": { "about": { - "title": "About", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "About", "description": "A link that leads to further details about this particular occurrence of the problem." } }, + "type": "object", + "title": "ErrorLinks", "description": "A Links object specific to Error objects" }, "ErrorResponse": { - "title": "ErrorResponse", - "required": [ - "meta", - "errors" - ], - "type": "object", "properties": { "data": { - "title": "Data", - "uniqueItems": true, "anyOf": [ { "$ref": "#/components/schemas/Resource" }, { - "type": "array", "items": { "$ref": "#/components/schemas/Resource" - } + }, + "type": "array" } ], + "uniqueItems": true, + "title": "Data", "description": "Outputted Data" }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/ResponseMeta" } ], + "title": "Meta", "description": "A meta object containing non-standard information." }, "errors": { - "title": "Errors", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/OptimadeError" }, + "type": "array", + "uniqueItems": true, + "title": "Errors", "description": "A list of OPTIMADE-specific JSON API error objects, where the field detail MUST be present." }, "included": { - "title": "Included", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/Resource" }, + "type": "array", + "uniqueItems": true, + "title": "Included", "description": "A list of unique included resources" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ToplevelLinks" } ], + "title": "Links", "description": "Links associated with the primary data or errors" }, "jsonapi": { - "title": "Jsonapi", "allOf": [ { "$ref": "#/components/schemas/JsonApi" } ], + "title": "Jsonapi", "description": "Information about the JSON API used" } }, + "type": "object", + "required": [ + "meta", + "errors" + ], + "title": "ErrorResponse", "description": "errors MUST be present and data MUST be skipped" }, "ErrorSource": { - "title": "ErrorSource", - "type": "object", "properties": { "pointer": { - "title": "Pointer", "type": "string", + "title": "Pointer", "description": "a JSON Pointer [RFC6901] to the associated entity in the request document [e.g. \"/data\" for a primary data object, or \"/data/attributes/title\" for a specific attribute]." }, "parameter": { - "title": "Parameter", "type": "string", + "title": "Parameter", "description": "a string indicating which URI query parameter caused the error." } }, + "type": "object", + "title": "ErrorSource", "description": "an object containing references to the source of the error" }, "Implementation": { - "title": "Implementation", - "type": "object", "properties": { "name": { - "title": "Name", "type": "string", + "title": "Name", "description": "name of the implementation" }, "version": { - "title": "Version", "type": "string", + "title": "Version", "description": "version string of the current implementation" }, "homepage": { - "title": "Homepage", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Homepage", "description": "A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the homepage of the implementation." }, "source_url": { - "title": "Source Url", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Source Url", "description": "A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the implementation source, either downloadable archive or version control system." }, "maintainer": { - "title": "Maintainer", "allOf": [ { "$ref": "#/components/schemas/ImplementationMaintainer" } ], + "title": "Maintainer", "description": "A dictionary providing details about the maintainer of the implementation." }, "issue_tracker": { - "title": "Issue Tracker", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Issue Tracker", "description": "A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the implementation's issue tracker." } }, + "type": "object", + "title": "Implementation", "description": "Information on the server implementation" }, "ImplementationMaintainer": { - "title": "ImplementationMaintainer", - "required": [ - "email" - ], - "type": "object", "properties": { "email": { - "title": "Email", "type": "string", - "description": "the maintainer's email address", - "format": "email" + "format": "email", + "title": "Email", + "description": "the maintainer's email address" } }, - "description": "Details about the maintainer of the implementation" - }, - "IndexInfoAttributes": { - "title": "IndexInfoAttributes", + "type": "object", "required": [ - "api_version", - "available_api_versions", - "available_endpoints", - "entry_types_by_format" + "email" ], - "type": "object", + "title": "ImplementationMaintainer", + "description": "Details about the maintainer of the implementation" + }, + "IndexInfoAttributes": { "properties": { "api_version": { - "title": "Api Version", - "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", "type": "string", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", + "title": "Api Version", "description": "Presently used full version of the OPTIMADE API.\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\nExamples: `1.0.0`, `1.0.0-rc.2`.", "example": [ "0.10.1", @@ -870,791 +862,792 @@ ] }, "available_api_versions": { - "title": "Available Api Versions", - "type": "array", "items": { "$ref": "#/components/schemas/AvailableApiVersion" }, + "type": "array", + "title": "Available Api Versions", "description": "A list of dictionaries of available API versions at other base URLs" }, "formats": { - "title": "Formats", - "type": "array", "items": { "type": "string" }, + "type": "array", + "title": "Formats", "description": "List of available output formats.", "default": [ "json" ] }, "available_endpoints": { - "title": "Available Endpoints", - "type": "array", "items": { "type": "string" }, + "type": "array", + "title": "Available Endpoints", "description": "List of available endpoints (i.e., the string to be appended to the versioned base URL)." }, "entry_types_by_format": { - "title": "Entry Types By Format", - "type": "object", "additionalProperties": { - "type": "array", "items": { "type": "string" - } + }, + "type": "array" }, + "type": "object", + "title": "Entry Types By Format", "description": "Available entry endpoints as a function of output formats." }, "is_index": { - "title": "Is Index", "type": "boolean", + "title": "Is Index", "description": "This must be `true` since this is an index meta-database (see section Index Meta-Database).", "default": true } }, + "type": "object", + "required": [ + "api_version", + "available_api_versions", + "available_endpoints", + "entry_types_by_format" + ], + "title": "IndexInfoAttributes", "description": "Attributes for Base URL Info endpoint for an Index Meta-Database" }, "IndexInfoResource": { - "title": "IndexInfoResource", - "required": [ - "id", - "type", - "attributes", - "relationships" - ], - "type": "object", "properties": { "id": { - "title": "Id", - "pattern": "^/$", "type": "string", + "pattern": "^/$", + "title": "Id", "default": "/" }, "type": { - "title": "Type", - "pattern": "^info$", "type": "string", + "pattern": "^info$", + "title": "Type", "default": "info" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ResourceLinks" } ], + "title": "Links", "description": "a links object containing links related to the resource." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship." }, "attributes": { "$ref": "#/components/schemas/IndexInfoAttributes" }, "relationships": { - "title": "Relationships", - "type": "object", "additionalProperties": { "$ref": "#/components/schemas/IndexRelationship" }, + "type": "object", + "title": "Relationships", "description": "Reference to the Links identifier object under the `links` endpoint that the provider has chosen as their 'default' OPTIMADE API database.\nA client SHOULD present this database as the first choice when an end-user chooses this provider." } }, + "type": "object", + "required": [ + "id", + "type", + "attributes", + "relationships" + ], + "title": "IndexInfoResource", "description": "Index Meta-Database Base URL Info endpoint resource" }, "IndexInfoResponse": { - "title": "IndexInfoResponse", - "required": [ - "data", - "meta" - ], - "type": "object", "properties": { "data": { - "title": "Data", "allOf": [ { "$ref": "#/components/schemas/IndexInfoResource" } ], + "title": "Data", "description": "Index meta-database /info data." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/ResponseMeta" } ], + "title": "Meta", "description": "A meta object containing non-standard information" }, "errors": { - "title": "Errors", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/Error" }, + "type": "array", + "uniqueItems": true, + "title": "Errors", "description": "A list of unique errors" }, "included": { - "title": "Included", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/Resource" }, + "type": "array", + "uniqueItems": true, + "title": "Included", "description": "A list of unique included resources" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ToplevelLinks" } ], + "title": "Links", "description": "Links associated with the primary data or errors" }, "jsonapi": { - "title": "Jsonapi", "allOf": [ { "$ref": "#/components/schemas/JsonApi" } ], + "title": "Jsonapi", "description": "Information about the JSON API used" } }, + "type": "object", + "required": [ + "data", + "meta" + ], + "title": "IndexInfoResponse", "description": "errors are not allowed" }, "IndexRelationship": { - "title": "IndexRelationship", - "required": [ - "data" - ], - "type": "object", "properties": { "data": { - "title": "Data", "allOf": [ { "$ref": "#/components/schemas/RelatedLinksResource" } ], + "title": "Data", "description": "[JSON API resource linkage](http://jsonapi.org/format/1.0/#document-links).\nIt MUST be either `null` or contain a single Links identifier object with the fields `id` and `type`" } }, + "type": "object", + "required": [ + "data" + ], + "title": "IndexRelationship", "description": "Index Meta-Database relationship" }, "JsonApi": { - "title": "JsonApi", - "type": "object", "properties": { "version": { - "title": "Version", "type": "string", + "title": "Version", "description": "Version of the json API used", "default": "1.0" }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "Non-standard meta information" } }, + "type": "object", + "title": "JsonApi", "description": "An object describing the server's implementation" }, "Link": { - "title": "Link", - "required": [ - "href" - ], - "type": "object", "properties": { "href": { - "title": "Href", + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", - "description": "a string containing the link\u2019s URL.", - "format": "uri" + "format": "uri", + "title": "Href", + "description": "a string containing the link\u2019s URL." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "a meta object containing non-standard meta-information about the link." } }, + "type": "object", + "required": [ + "href" + ], + "title": "Link", "description": "A link **MUST** be represented as either: a string containing the link's URL or a link object." }, "LinkType": { - "title": "LinkType", "enum": [ "child", "root", "external", "providers" ], + "title": "LinkType", "description": "Enumeration of link_type values" }, "LinksResource": { - "title": "LinksResource", - "required": [ - "id", - "type", - "attributes" - ], - "type": "object", "properties": { "id": { - "title": "Id", "type": "string", + "title": "Id", "description": "An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`", "x-optimade-support": "must", "x-optimade-queryable": "must" }, "type": { - "title": "Type", - "pattern": "^links$", "type": "string", + "pattern": "^links$", + "title": "Type", "description": "These objects are described in detail in the section Links Endpoint", "default": "links" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ResourceLinks" } ], + "title": "Links", "description": "a links object containing links related to the resource." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/EntryMetadata" } ], + "title": "Meta", "description": "A dictionary, containing entry and property-specific metadata for a given entry." }, "attributes": { - "title": "Attributes", "allOf": [ { "$ref": "#/components/schemas/LinksResourceAttributes" } ], + "title": "Attributes", "description": "A dictionary containing key-value pairs representing the Links resource's properties." }, "relationships": { - "title": "Relationships", "allOf": [ { "$ref": "#/components/schemas/EntryRelationships" } ], + "title": "Relationships", "description": "A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object." } }, + "type": "object", + "required": [ + "id", + "type", + "attributes" + ], + "title": "LinksResource", "description": "A Links endpoint resource object" }, "LinksResourceAttributes": { - "title": "LinksResourceAttributes", - "required": [ - "name", - "description", - "base_url", - "homepage", - "link_type" - ], - "type": "object", "properties": { "name": { - "title": "Name", "type": "string", + "title": "Name", "description": "Human-readable name for the OPTIMADE API implementation, e.g., for use in clients to show the name to the end-user." }, "description": { - "title": "Description", "type": "string", + "title": "Description", "description": "Human-readable description for the OPTIMADE API implementation, e.g., for use in clients to show a description to the end-user." }, "base_url": { - "title": "Base Url", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Base Url", "description": "JSON API links object, pointing to the base URL for this implementation" }, "homepage": { - "title": "Homepage", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Homepage", "description": "JSON API links object, pointing to a homepage URL for this implementation" }, "link_type": { - "title": "Link Type", "allOf": [ { "$ref": "#/components/schemas/LinkType" } ], + "title": "Link Type", "description": "The type of the linked relation.\nMUST be one of these values: 'child', 'root', 'external', 'providers'." }, "aggregate": { - "title": "Aggregate", "allOf": [ { "$ref": "#/components/schemas/Aggregate" } ], + "title": "Aggregate", "description": "A string indicating whether a client that is following links to aggregate results from different OPTIMADE implementations should follow this link or not.\nThis flag SHOULD NOT be indicated for links where `link_type` is not `child`.\n\nIf not specified, clients MAY assume that the value is `ok`.\nIf specified, and the value is anything different than `ok`, the client MUST assume that the server is suggesting not to follow the link during aggregation by default (also if the value is not among the known ones, in case a future specification adds new accepted values).\n\nSpecific values indicate the reason why the server is providing the suggestion.\nA client MAY follow the link anyway if it has reason to do so (e.g., if the client is looking for all test databases, it MAY follow the links marked with `aggregate`=`test`).\n\nIf specified, it MUST be one of the values listed in section Link Aggregate Options.", "default": "ok" }, "no_aggregate_reason": { - "title": "No Aggregate Reason", "type": "string", + "title": "No Aggregate Reason", "description": "An OPTIONAL human-readable string indicating the reason for suggesting not to aggregate results following the link.\nIt SHOULD NOT be present if `aggregate`=`ok`." } }, + "type": "object", + "required": [ + "name", + "description", + "base_url", + "homepage", + "link_type" + ], + "title": "LinksResourceAttributes", "description": "Links endpoint resource object attributes" }, "LinksResponse": { - "title": "LinksResponse", - "required": [ - "data", - "meta" - ], - "type": "object", "properties": { "data": { - "title": "Data", - "uniqueItems": true, "anyOf": [ { - "type": "array", "items": { "$ref": "#/components/schemas/LinksResource" - } + }, + "type": "array" }, { - "type": "array", "items": { "type": "object" - } + }, + "type": "array" } ], + "uniqueItems": true, + "title": "Data", "description": "List of unique OPTIMADE links resource objects." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/ResponseMeta" } ], + "title": "Meta", "description": "A meta object containing non-standard information" }, "errors": { - "title": "Errors", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/Error" }, + "type": "array", + "uniqueItems": true, + "title": "Errors", "description": "A list of unique errors" }, "included": { - "title": "Included", - "uniqueItems": true, "anyOf": [ { - "type": "array", "items": { "$ref": "#/components/schemas/EntryResource" - } + }, + "type": "array" }, { - "type": "array", "items": { "type": "object" - } + }, + "type": "array" } - ] + ], + "uniqueItems": true, + "title": "Included" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ToplevelLinks" } ], + "title": "Links", "description": "Links associated with the primary data or errors" }, "jsonapi": { - "title": "Jsonapi", "allOf": [ { "$ref": "#/components/schemas/JsonApi" } ], + "title": "Jsonapi", "description": "Information about the JSON API used" } }, + "type": "object", + "required": [ + "data", + "meta" + ], + "title": "LinksResponse", "description": "errors are not allowed" }, "Meta": { - "title": "Meta", - "type": "object", "properties": {}, + "type": "object", + "title": "Meta", "description": "Non-standard meta-information that can not be represented as an attribute or relationship." }, "OptimadeError": { - "title": "OptimadeError", - "required": [ - "detail" - ], - "type": "object", "properties": { "id": { - "title": "Id", "type": "string", + "title": "Id", "description": "A unique identifier for this particular occurrence of the problem." }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ErrorLinks" } ], + "title": "Links", "description": "A links object storing about" }, "status": { - "title": "Status", "type": "string", + "title": "Status", "description": "the HTTP status code applicable to this problem, expressed as a string value." }, "code": { - "title": "Code", "type": "string", + "title": "Code", "description": "an application-specific error code, expressed as a string value." }, "title": { - "title": "Title", "type": "string", + "title": "Title", "description": "A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization." }, "detail": { - "title": "Detail", "type": "string", + "title": "Detail", "description": "A human-readable explanation specific to this occurrence of the problem." }, "source": { - "title": "Source", "allOf": [ { "$ref": "#/components/schemas/ErrorSource" } ], + "title": "Source", "description": "An object containing references to the source of the error" }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "a meta object containing non-standard meta-information about the error." } }, + "type": "object", + "required": [ + "detail" + ], + "title": "OptimadeError", "description": "detail MUST be present" }, "Provider": { - "title": "Provider", - "required": [ - "name", - "description", - "prefix" - ], - "type": "object", "properties": { "name": { - "title": "Name", "type": "string", + "title": "Name", "description": "a short name for the database provider" }, "description": { - "title": "Description", "type": "string", + "title": "Description", "description": "a longer description of the database provider" }, "prefix": { - "title": "Prefix", - "pattern": "^[a-z]([a-z]|[0-9]|_)*$", "type": "string", + "pattern": "^[a-z]([a-z]|[0-9]|_)*$", + "title": "Prefix", "description": "database-provider-specific prefix as found in section Database-Provider-Specific Namespace Prefixes." }, "homepage": { - "title": "Homepage", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Homepage", "description": "a [JSON API links object](http://jsonapi.org/format/1.0#document-links) pointing to homepage of the database provider, either directly as a string, or as a link object." } }, + "type": "object", + "required": [ + "name", + "description", + "prefix" + ], + "title": "Provider", "description": "Information on the database provider of the implementation." }, "ReferenceRelationship": { - "title": "ReferenceRelationship", - "type": "object", "properties": { "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/RelationshipLinks" } ], + "title": "Links", "description": "a links object containing at least one of the following: self, related" }, "data": { - "title": "Data", - "uniqueItems": true, "anyOf": [ { "$ref": "#/components/schemas/BaseRelationshipResource" }, { - "type": "array", "items": { "$ref": "#/components/schemas/BaseRelationshipResource" - } + }, + "type": "array" } ], + "uniqueItems": true, + "title": "Data", "description": "Resource linkage" }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "a meta object that contains non-standard meta-information about the relationship." } }, + "type": "object", + "title": "ReferenceRelationship", "description": "Similar to normal JSON API relationship, but with addition of OPTIONAL meta field for a resource." }, "RelatedLinksResource": { - "title": "RelatedLinksResource", - "required": [ - "id", - "type" - ], - "type": "object", "properties": { "id": { - "title": "Id", "type": "string", + "title": "Id", "description": "Resource ID" }, "type": { - "title": "Type", - "pattern": "^links$", "type": "string", + "pattern": "^links$", + "title": "Type", "default": "links" } }, + "type": "object", + "required": [ + "id", + "type" + ], + "title": "RelatedLinksResource", "description": "A related Links resource object" }, "RelationshipLinks": { - "title": "RelationshipLinks", - "type": "object", "properties": { "self": { - "title": "Self", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Self", "description": "A link for the relationship itself (a 'relationship link').\nThis link allows the client to directly manipulate the relationship.\nWhen fetched successfully, this link returns the [linkage](https://jsonapi.org/format/1.0/#document-resource-object-linkage) for the related resources as its primary data.\n(See [Fetching Relationships](https://jsonapi.org/format/1.0/#fetching-relationships).)" }, "related": { - "title": "Related", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Related", "description": "A [related resource link](https://jsonapi.org/format/1.0/#document-resource-object-related-resource-links)." } }, + "type": "object", + "title": "RelationshipLinks", "description": "A resource object **MAY** contain references to other resource objects (\"relationships\").\nRelationships may be to-one or to-many.\nRelationships can be specified by including a member in a resource's links object." }, "Relationships": { - "title": "Relationships", - "type": "object", "properties": {}, + "type": "object", + "title": "Relationships", "description": "Members of the relationships object (\"relationships\") represent references from the resource object in which it's defined to other resource objects.\nKeys MUST NOT be:\n type\n id" }, "Resource": { - "title": "Resource", - "required": [ - "id", - "type" - ], - "type": "object", "properties": { "id": { - "title": "Id", "type": "string", + "title": "Id", "description": "Resource ID" }, "type": { - "title": "Type", "type": "string", + "title": "Type", "description": "Resource type" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ResourceLinks" } ], + "title": "Links", "description": "a links object containing links related to the resource." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship." }, "attributes": { - "title": "Attributes", "allOf": [ { "$ref": "#/components/schemas/Attributes" } ], + "title": "Attributes", "description": "an attributes object representing some of the resource\u2019s data." }, "relationships": { - "title": "Relationships", "allOf": [ { "$ref": "#/components/schemas/Relationships" } ], + "title": "Relationships", "description": "[Relationships object](https://jsonapi.org/format/1.0/#document-resource-object-relationships)\ndescribing relationships between the resource and other JSON API resources." } }, + "type": "object", + "required": [ + "id", + "type" + ], + "title": "Resource", "description": "Resource objects appear in a JSON API document to represent resources." }, "ResourceLinks": { - "title": "ResourceLinks", - "type": "object", "properties": { "self": { - "title": "Self", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Self", "description": "A link that identifies the resource represented by the resource object." } }, + "type": "object", + "title": "ResourceLinks", "description": "A Resource Links object" }, "ResponseMeta": { - "title": "ResponseMeta", - "required": [ - "query", - "api_version", - "more_data_available" - ], - "type": "object", "properties": { "query": { - "title": "Query", "allOf": [ { "$ref": "#/components/schemas/ResponseMetaQuery" } ], + "title": "Query", "description": "Information on the Query that was requested" }, "api_version": { - "title": "Api Version", - "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", "type": "string", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", + "title": "Api Version", "description": "Presently used full version of the OPTIMADE API.\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\nExamples: `1.0.0`, `1.0.0-rc.2`.", "example": [ "0.10.1", @@ -1663,298 +1656,305 @@ ] }, "more_data_available": { - "title": "More Data Available", "type": "boolean", + "title": "More Data Available", "description": "`false` if the response contains all data for the request (e.g., a request issued to a single entry endpoint, or a `filter` query at the last page of a paginated response) and `true` if the response is incomplete in the sense that multiple objects match the request, and not all of them have been included in the response (e.g., a query with multiple pages that is not at the last page)." }, "schema": { - "title": "Schema", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Schema", "description": "A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) that points to a schema for the response.\nIf it is a string, or a dictionary containing no `meta` field, the provided URL MUST point at an [OpenAPI](https://swagger.io/specification/) schema.\nIt is possible that future versions of this specification allows for alternative schema types.\nHence, if the `meta` field of the JSON API links object is provided and contains a field `schema_type` that is not equal to the string `OpenAPI` the client MUST not handle failures to parse the schema or to validate the response against the schema as errors." }, "time_stamp": { - "title": "Time Stamp", "type": "string", - "description": "A timestamp containing the date and time at which the query was executed.", - "format": "date-time" + "format": "date-time", + "title": "Time Stamp", + "description": "A timestamp containing the date and time at which the query was executed." }, "data_returned": { - "title": "Data Returned", - "minimum": 0.0, "type": "integer", + "minimum": 0.0, + "title": "Data Returned", "description": "An integer containing the total number of data resource objects returned for the current `filter` query, independent of pagination." }, "provider": { - "title": "Provider", "allOf": [ { "$ref": "#/components/schemas/Provider" } ], + "title": "Provider", "description": "information on the database provider of the implementation." }, "data_available": { - "title": "Data Available", "type": "integer", + "title": "Data Available", "description": "An integer containing the total number of data resource objects available in the database for the endpoint." }, "last_id": { - "title": "Last Id", "type": "string", + "title": "Last Id", "description": "a string containing the last ID returned" }, "response_message": { - "title": "Response Message", "type": "string", + "title": "Response Message", "description": "response string from the server" }, "implementation": { - "title": "Implementation", "allOf": [ { "$ref": "#/components/schemas/Implementation" } ], + "title": "Implementation", "description": "a dictionary describing the server implementation" }, "warnings": { - "title": "Warnings", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/Warnings" }, + "type": "array", + "uniqueItems": true, + "title": "Warnings", "description": "A list of warning resource objects representing non-critical errors or warnings.\nA warning resource object is defined similarly to a [JSON API error object](http://jsonapi.org/format/1.0/#error-objects), but MUST also include the field `type`, which MUST have the value `\"warning\"`.\nThe field `detail` MUST be present and SHOULD contain a non-critical message, e.g., reporting unrecognized search attributes or deprecated features.\nThe field `status`, representing a HTTP response status code, MUST NOT be present for a warning resource object.\nThis is an exclusive field for error resource objects." } }, + "type": "object", + "required": [ + "query", + "api_version", + "more_data_available" + ], + "title": "ResponseMeta", "description": "A [JSON API meta member](https://jsonapi.org/format/1.0#document-meta)\nthat contains JSON API meta objects of non-standard\nmeta-information.\n\nOPTIONAL additional information global to the query that is not\nspecified in this document, MUST start with a\ndatabase-provider-specific prefix." }, "ResponseMetaQuery": { - "title": "ResponseMetaQuery", - "required": [ - "representation" - ], - "type": "object", "properties": { "representation": { - "title": "Representation", "type": "string", + "title": "Representation", "description": "A string with the part of the URL following the versioned or unversioned base URL that serves the API.\nQuery parameters that have not been used in processing the request MAY be omitted.\nIn particular, if no query parameters have been involved in processing the request, the query part of the URL MAY be excluded.\nExample: `/structures?filter=nelements=2`" } }, + "type": "object", + "required": [ + "representation" + ], + "title": "ResponseMetaQuery", "description": "Information on the query that was requested." }, "StructureRelationship": { - "title": "StructureRelationship", - "type": "object", "properties": { "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/RelationshipLinks" } ], + "title": "Links", "description": "a links object containing at least one of the following: self, related" }, "data": { - "title": "Data", - "uniqueItems": true, "anyOf": [ { "$ref": "#/components/schemas/BaseRelationshipResource" }, { - "type": "array", "items": { "$ref": "#/components/schemas/BaseRelationshipResource" - } + }, + "type": "array" } ], + "uniqueItems": true, + "title": "Data", "description": "Resource linkage" }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "a meta object that contains non-standard meta-information about the relationship." } }, + "type": "object", + "title": "StructureRelationship", "description": "Similar to normal JSON API relationship, but with addition of OPTIONAL meta field for a resource." }, "ToplevelLinks": { - "title": "ToplevelLinks", - "type": "object", "properties": { "self": { - "title": "Self", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Self", "description": "A link to itself" }, "related": { - "title": "Related", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Related", "description": "A related resource link" }, "first": { - "title": "First", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "First", "description": "The first page of data" }, "last": { - "title": "Last", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Last", "description": "The last page of data" }, "prev": { - "title": "Prev", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Prev", "description": "The previous page of data" }, "next": { - "title": "Next", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Next", "description": "The next page of data" } }, + "type": "object", + "title": "ToplevelLinks", "description": "A set of Links objects, possibly including pagination" }, "Warnings": { - "title": "Warnings", - "required": [ - "detail", - "type" - ], - "type": "object", "properties": { "id": { - "title": "Id", "type": "string", + "title": "Id", "description": "A unique identifier for this particular occurrence of the problem." }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ErrorLinks" } ], + "title": "Links", "description": "A links object storing about" }, "code": { - "title": "Code", "type": "string", + "title": "Code", "description": "an application-specific error code, expressed as a string value." }, "title": { - "title": "Title", "type": "string", + "title": "Title", "description": "A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization." }, "detail": { - "title": "Detail", "type": "string", + "title": "Detail", "description": "A human-readable explanation specific to this occurrence of the problem." }, "source": { - "title": "Source", "allOf": [ { "$ref": "#/components/schemas/ErrorSource" } ], + "title": "Source", "description": "An object containing references to the source of the error" }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "a meta object containing non-standard meta-information about the error." }, "type": { - "title": "Type", - "pattern": "^warning$", "type": "string", + "pattern": "^warning$", + "title": "Type", "description": "Warnings must be of type \"warning\"", "default": "warning" } }, + "type": "object", + "required": [ + "detail", + "type" + ], + "title": "Warnings", "description": "OPTIMADE-specific warning class based on OPTIMADE-specific JSON API Error.\n\nFrom the specification:\n\nA warning resource object is defined similarly to a JSON API error object, but MUST also include the field type, which MUST have the value \"warning\".\nThe field detail MUST be present and SHOULD contain a non-critical message, e.g., reporting unrecognized search attributes or deprecated features.\n\nNote: Must be named \"Warnings\", since \"Warning\" is a built-in Python class." } } diff --git a/openapi/openapi.json b/openapi/openapi.json index d1d5491e8..3e2a2f51a 100644 --- a/openapi/openapi.json +++ b/openapi/openapi.json @@ -1,5 +1,5 @@ { - "openapi": "3.0.2", + "openapi": "3.1.0", "info": { "title": "OPTIMADE API", "description": "The [Open Databases Integration for Materials Design (OPTIMADE) consortium](https://www.optimade.org/) aims to make materials databases interoperational by developing a common REST API.\n\nThis specification is generated using [`optimade-python-tools`](https://github.com/Materials-Consortia/optimade-python-tools/tree/v0.25.2) v0.25.2.", @@ -108,8 +108,8 @@ { "required": true, "schema": { - "title": "Entry", - "type": "string" + "type": "string", + "title": "Entry" }, "name": "entry", "in": "path" @@ -211,8 +211,8 @@ "description": "A filter string, in the format described in section API Filtering Format Specification of the specification.", "required": false, "schema": { - "title": "Filter", "type": "string", + "title": "Filter", "description": "A filter string, in the format described in section API Filtering Format Specification of the specification.", "default": "" }, @@ -223,8 +223,8 @@ "description": "The output format requested (see section Response Format).\nDefaults to the format string 'json', which specifies the standard output format described in this specification.\nExample: `http://example.com/v1/structures?response_format=xml`", "required": false, "schema": { - "title": "Response Format", "type": "string", + "title": "Response Format", "description": "The output format requested (see section Response Format).\nDefaults to the format string 'json', which specifies the standard output format described in this specification.\nExample: `http://example.com/v1/structures?response_format=xml`", "default": "json" }, @@ -235,10 +235,10 @@ "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "required": false, "schema": { - "title": "Email Address", "type": "string", - "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "format": "email", + "title": "Email Address", + "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "default": "" }, "name": "email_address", @@ -248,9 +248,9 @@ "description": "A comma-delimited set of fields to be provided in the output.\nIf provided, these fields MUST be returned along with the REQUIRED fields.\nOther OPTIONAL fields MUST NOT be returned when this parameter is present.\nExample: `http://example.com/v1/structures?response_fields=last_modified,nsites`", "required": false, "schema": { - "title": "Response Fields", - "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", "type": "string", + "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", + "title": "Response Fields", "description": "A comma-delimited set of fields to be provided in the output.\nIf provided, these fields MUST be returned along with the REQUIRED fields.\nOther OPTIONAL fields MUST NOT be returned when this parameter is present.\nExample: `http://example.com/v1/structures?response_fields=last_modified,nsites`", "default": "" }, @@ -261,9 +261,9 @@ "description": "If supporting sortable queries, an implementation MUST use the `sort` query parameter with format as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-sorting).\n\nAn implementation MAY support multiple sort fields for a single query.\nIf it does, it again MUST conform to the JSON API 1.0 specification.\n\nIf an implementation supports sorting for an entry listing endpoint, then the `/info/` endpoint MUST include, for each field name `` in its `data.properties.` response value that can be used for sorting, the key `sortable` with value `true`.\nIf a field name under an entry listing endpoint supporting sorting cannot be used for sorting, the server MUST either leave out the `sortable` key or set it equal to `false` for the specific field name.\nThe set of field names, with `sortable` equal to `true` are allowed to be used in the \"sort fields\" list according to its definition in the JSON API 1.0 specification.\nThe field `sortable` is in addition to each property description and other OPTIONAL fields.\nAn example is shown in the section Entry Listing Info Endpoints.", "required": false, "schema": { - "title": "Sort", - "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", "type": "string", + "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", + "title": "Sort", "description": "If supporting sortable queries, an implementation MUST use the `sort` query parameter with format as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-sorting).\n\nAn implementation MAY support multiple sort fields for a single query.\nIf it does, it again MUST conform to the JSON API 1.0 specification.\n\nIf an implementation supports sorting for an entry listing endpoint, then the `/info/` endpoint MUST include, for each field name `` in its `data.properties.` response value that can be used for sorting, the key `sortable` with value `true`.\nIf a field name under an entry listing endpoint supporting sorting cannot be used for sorting, the server MUST either leave out the `sortable` key or set it equal to `false` for the specific field name.\nThe set of field names, with `sortable` equal to `true` are allowed to be used in the \"sort fields\" list according to its definition in the JSON API 1.0 specification.\nThe field `sortable` is in addition to each property description and other OPTIONAL fields.\nAn example is shown in the section Entry Listing Info Endpoints.", "default": "" }, @@ -274,9 +274,9 @@ "description": "Sets a numerical limit on the number of entries returned.\nSee [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-pagination).\nThe API implementation MUST return no more than the number specified.\nIt MAY return fewer.\nThe database MAY have a maximum limit and not accept larger numbers (in which case an error code -- 403 Forbidden -- MUST be returned).\nThe default limit value is up to the API implementation to decide.\nExample: `http://example.com/optimade/v1/structures?page_limit=100`", "required": false, "schema": { - "title": "Page Limit", - "minimum": 0.0, "type": "integer", + "minimum": 0.0, + "title": "Page Limit", "description": "Sets a numerical limit on the number of entries returned.\nSee [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-pagination).\nThe API implementation MUST return no more than the number specified.\nIt MAY return fewer.\nThe database MAY have a maximum limit and not accept larger numbers (in which case an error code -- 403 Forbidden -- MUST be returned).\nThe default limit value is up to the API implementation to decide.\nExample: `http://example.com/optimade/v1/structures?page_limit=100`", "default": 20 }, @@ -287,9 +287,9 @@ "description": "RECOMMENDED for use with _offset-based_ pagination: using `page_offset` and `page_limit` is RECOMMENDED.\nExample: Skip 50 structures and fetch up to 100: `/structures?page_offset=50&page_limit=100`.", "required": false, "schema": { - "title": "Page Offset", - "minimum": 0.0, "type": "integer", + "minimum": 0.0, + "title": "Page Offset", "description": "RECOMMENDED for use with _offset-based_ pagination: using `page_offset` and `page_limit` is RECOMMENDED.\nExample: Skip 50 structures and fetch up to 100: `/structures?page_offset=50&page_limit=100`.", "default": 0 }, @@ -300,8 +300,8 @@ "description": "RECOMMENDED for use with _page-based_ pagination: using `page_number` and `page_limit` is RECOMMENDED.\nIt is RECOMMENDED that the first page has number 1, i.e., that `page_number` is 1-based.\nExample: Fetch page 2 of up to 50 structures per page: `/structures?page_number=2&page_limit=50`.", "required": false, "schema": { - "title": "Page Number", "type": "integer", + "title": "Page Number", "description": "RECOMMENDED for use with _page-based_ pagination: using `page_number` and `page_limit` is RECOMMENDED.\nIt is RECOMMENDED that the first page has number 1, i.e., that `page_number` is 1-based.\nExample: Fetch page 2 of up to 50 structures per page: `/structures?page_number=2&page_limit=50`." }, "name": "page_number", @@ -311,9 +311,9 @@ "description": "RECOMMENDED for use with _cursor-based_ pagination: using `page_cursor` and `page_limit` is RECOMMENDED.", "required": false, "schema": { - "title": "Page Cursor", - "minimum": 0.0, "type": "integer", + "minimum": 0.0, + "title": "Page Cursor", "description": "RECOMMENDED for use with _cursor-based_ pagination: using `page_cursor` and `page_limit` is RECOMMENDED.", "default": 0 }, @@ -324,8 +324,8 @@ "description": "RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED.\nExample: Fetch up to 100 structures above sort-field value 4000 (in this example, server chooses to fetch results sorted by increasing `id`, so `page_above` value refers to an `id` value): `/structures?page_above=4000&page_limit=100`.", "required": false, "schema": { - "title": "Page Above", "type": "string", + "title": "Page Above", "description": "RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED.\nExample: Fetch up to 100 structures above sort-field value 4000 (in this example, server chooses to fetch results sorted by increasing `id`, so `page_above` value refers to an `id` value): `/structures?page_above=4000&page_limit=100`." }, "name": "page_above", @@ -335,8 +335,8 @@ "description": "RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED.", "required": false, "schema": { - "title": "Page Below", "type": "string", + "title": "Page Below", "description": "RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED." }, "name": "page_below", @@ -346,8 +346,8 @@ "description": "A server MAY implement the JSON API concept of returning [compound documents](https://jsonapi.org/format/1.0/#document-compound-documents) by utilizing the `include` query parameter as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-includes).\n\nAll related resource objects MUST be returned as part of an array value for the top-level `included` field, see the section JSON Response Schema: Common Fields.\n\nThe value of `include` MUST be a comma-separated list of \"relationship paths\", as defined in the [JSON API](https://jsonapi.org/format/1.0/#fetching-includes).\nIf relationship paths are not supported, or a server is unable to identify a relationship path a `400 Bad Request` response MUST be made.\n\nThe **default value** for `include` is `references`.\nThis means `references` entries MUST always be included under the top-level field `included` as default, since a server assumes if `include` is not specified by a client in the request, it is still specified as `include=references`.\nNote, if a client explicitly specifies `include` and leaves out `references`, `references` resource objects MUST NOT be included under the top-level field `included`, as per the definition of `included`, see section JSON Response Schema: Common Fields.\n\n> **Note**: A query with the parameter `include` set to the empty string means no related resource objects are to be returned under the top-level field `included`.", "required": false, "schema": { - "title": "Include", "type": "string", + "title": "Include", "description": "A server MAY implement the JSON API concept of returning [compound documents](https://jsonapi.org/format/1.0/#document-compound-documents) by utilizing the `include` query parameter as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-includes).\n\nAll related resource objects MUST be returned as part of an array value for the top-level `included` field, see the section JSON Response Schema: Common Fields.\n\nThe value of `include` MUST be a comma-separated list of \"relationship paths\", as defined in the [JSON API](https://jsonapi.org/format/1.0/#fetching-includes).\nIf relationship paths are not supported, or a server is unable to identify a relationship path a `400 Bad Request` response MUST be made.\n\nThe **default value** for `include` is `references`.\nThis means `references` entries MUST always be included under the top-level field `included` as default, since a server assumes if `include` is not specified by a client in the request, it is still specified as `include=references`.\nNote, if a client explicitly specifies `include` and leaves out `references`, `references` resource objects MUST NOT be included under the top-level field `included`, as per the definition of `included`, see section JSON Response Schema: Common Fields.\n\n> **Note**: A query with the parameter `include` set to the empty string means no related resource objects are to be returned under the top-level field `included`.", "default": "references" }, @@ -358,9 +358,9 @@ "description": "If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.", "required": false, "schema": { - "title": "Api Hint", - "pattern": "(v[0-9]+(\\.[0-9]+)?)?", "type": "string", + "pattern": "(v[0-9]+(\\.[0-9]+)?)?", + "title": "Api Hint", "description": "If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.", "default": "" }, @@ -464,8 +464,8 @@ "description": "A filter string, in the format described in section API Filtering Format Specification of the specification.", "required": false, "schema": { - "title": "Filter", "type": "string", + "title": "Filter", "description": "A filter string, in the format described in section API Filtering Format Specification of the specification.", "default": "" }, @@ -476,8 +476,8 @@ "description": "The output format requested (see section Response Format).\nDefaults to the format string 'json', which specifies the standard output format described in this specification.\nExample: `http://example.com/v1/structures?response_format=xml`", "required": false, "schema": { - "title": "Response Format", "type": "string", + "title": "Response Format", "description": "The output format requested (see section Response Format).\nDefaults to the format string 'json', which specifies the standard output format described in this specification.\nExample: `http://example.com/v1/structures?response_format=xml`", "default": "json" }, @@ -488,10 +488,10 @@ "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "required": false, "schema": { - "title": "Email Address", "type": "string", - "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "format": "email", + "title": "Email Address", + "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "default": "" }, "name": "email_address", @@ -501,9 +501,9 @@ "description": "A comma-delimited set of fields to be provided in the output.\nIf provided, these fields MUST be returned along with the REQUIRED fields.\nOther OPTIONAL fields MUST NOT be returned when this parameter is present.\nExample: `http://example.com/v1/structures?response_fields=last_modified,nsites`", "required": false, "schema": { - "title": "Response Fields", - "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", "type": "string", + "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", + "title": "Response Fields", "description": "A comma-delimited set of fields to be provided in the output.\nIf provided, these fields MUST be returned along with the REQUIRED fields.\nOther OPTIONAL fields MUST NOT be returned when this parameter is present.\nExample: `http://example.com/v1/structures?response_fields=last_modified,nsites`", "default": "" }, @@ -514,9 +514,9 @@ "description": "If supporting sortable queries, an implementation MUST use the `sort` query parameter with format as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-sorting).\n\nAn implementation MAY support multiple sort fields for a single query.\nIf it does, it again MUST conform to the JSON API 1.0 specification.\n\nIf an implementation supports sorting for an entry listing endpoint, then the `/info/` endpoint MUST include, for each field name `` in its `data.properties.` response value that can be used for sorting, the key `sortable` with value `true`.\nIf a field name under an entry listing endpoint supporting sorting cannot be used for sorting, the server MUST either leave out the `sortable` key or set it equal to `false` for the specific field name.\nThe set of field names, with `sortable` equal to `true` are allowed to be used in the \"sort fields\" list according to its definition in the JSON API 1.0 specification.\nThe field `sortable` is in addition to each property description and other OPTIONAL fields.\nAn example is shown in the section Entry Listing Info Endpoints.", "required": false, "schema": { - "title": "Sort", - "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", "type": "string", + "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", + "title": "Sort", "description": "If supporting sortable queries, an implementation MUST use the `sort` query parameter with format as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-sorting).\n\nAn implementation MAY support multiple sort fields for a single query.\nIf it does, it again MUST conform to the JSON API 1.0 specification.\n\nIf an implementation supports sorting for an entry listing endpoint, then the `/info/` endpoint MUST include, for each field name `` in its `data.properties.` response value that can be used for sorting, the key `sortable` with value `true`.\nIf a field name under an entry listing endpoint supporting sorting cannot be used for sorting, the server MUST either leave out the `sortable` key or set it equal to `false` for the specific field name.\nThe set of field names, with `sortable` equal to `true` are allowed to be used in the \"sort fields\" list according to its definition in the JSON API 1.0 specification.\nThe field `sortable` is in addition to each property description and other OPTIONAL fields.\nAn example is shown in the section Entry Listing Info Endpoints.", "default": "" }, @@ -527,9 +527,9 @@ "description": "Sets a numerical limit on the number of entries returned.\nSee [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-pagination).\nThe API implementation MUST return no more than the number specified.\nIt MAY return fewer.\nThe database MAY have a maximum limit and not accept larger numbers (in which case an error code -- 403 Forbidden -- MUST be returned).\nThe default limit value is up to the API implementation to decide.\nExample: `http://example.com/optimade/v1/structures?page_limit=100`", "required": false, "schema": { - "title": "Page Limit", - "minimum": 0.0, "type": "integer", + "minimum": 0.0, + "title": "Page Limit", "description": "Sets a numerical limit on the number of entries returned.\nSee [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-pagination).\nThe API implementation MUST return no more than the number specified.\nIt MAY return fewer.\nThe database MAY have a maximum limit and not accept larger numbers (in which case an error code -- 403 Forbidden -- MUST be returned).\nThe default limit value is up to the API implementation to decide.\nExample: `http://example.com/optimade/v1/structures?page_limit=100`", "default": 20 }, @@ -540,9 +540,9 @@ "description": "RECOMMENDED for use with _offset-based_ pagination: using `page_offset` and `page_limit` is RECOMMENDED.\nExample: Skip 50 structures and fetch up to 100: `/structures?page_offset=50&page_limit=100`.", "required": false, "schema": { - "title": "Page Offset", - "minimum": 0.0, "type": "integer", + "minimum": 0.0, + "title": "Page Offset", "description": "RECOMMENDED for use with _offset-based_ pagination: using `page_offset` and `page_limit` is RECOMMENDED.\nExample: Skip 50 structures and fetch up to 100: `/structures?page_offset=50&page_limit=100`.", "default": 0 }, @@ -553,8 +553,8 @@ "description": "RECOMMENDED for use with _page-based_ pagination: using `page_number` and `page_limit` is RECOMMENDED.\nIt is RECOMMENDED that the first page has number 1, i.e., that `page_number` is 1-based.\nExample: Fetch page 2 of up to 50 structures per page: `/structures?page_number=2&page_limit=50`.", "required": false, "schema": { - "title": "Page Number", "type": "integer", + "title": "Page Number", "description": "RECOMMENDED for use with _page-based_ pagination: using `page_number` and `page_limit` is RECOMMENDED.\nIt is RECOMMENDED that the first page has number 1, i.e., that `page_number` is 1-based.\nExample: Fetch page 2 of up to 50 structures per page: `/structures?page_number=2&page_limit=50`." }, "name": "page_number", @@ -564,9 +564,9 @@ "description": "RECOMMENDED for use with _cursor-based_ pagination: using `page_cursor` and `page_limit` is RECOMMENDED.", "required": false, "schema": { - "title": "Page Cursor", - "minimum": 0.0, "type": "integer", + "minimum": 0.0, + "title": "Page Cursor", "description": "RECOMMENDED for use with _cursor-based_ pagination: using `page_cursor` and `page_limit` is RECOMMENDED.", "default": 0 }, @@ -577,8 +577,8 @@ "description": "RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED.\nExample: Fetch up to 100 structures above sort-field value 4000 (in this example, server chooses to fetch results sorted by increasing `id`, so `page_above` value refers to an `id` value): `/structures?page_above=4000&page_limit=100`.", "required": false, "schema": { - "title": "Page Above", "type": "string", + "title": "Page Above", "description": "RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED.\nExample: Fetch up to 100 structures above sort-field value 4000 (in this example, server chooses to fetch results sorted by increasing `id`, so `page_above` value refers to an `id` value): `/structures?page_above=4000&page_limit=100`." }, "name": "page_above", @@ -588,8 +588,8 @@ "description": "RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED.", "required": false, "schema": { - "title": "Page Below", "type": "string", + "title": "Page Below", "description": "RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED." }, "name": "page_below", @@ -599,8 +599,8 @@ "description": "A server MAY implement the JSON API concept of returning [compound documents](https://jsonapi.org/format/1.0/#document-compound-documents) by utilizing the `include` query parameter as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-includes).\n\nAll related resource objects MUST be returned as part of an array value for the top-level `included` field, see the section JSON Response Schema: Common Fields.\n\nThe value of `include` MUST be a comma-separated list of \"relationship paths\", as defined in the [JSON API](https://jsonapi.org/format/1.0/#fetching-includes).\nIf relationship paths are not supported, or a server is unable to identify a relationship path a `400 Bad Request` response MUST be made.\n\nThe **default value** for `include` is `references`.\nThis means `references` entries MUST always be included under the top-level field `included` as default, since a server assumes if `include` is not specified by a client in the request, it is still specified as `include=references`.\nNote, if a client explicitly specifies `include` and leaves out `references`, `references` resource objects MUST NOT be included under the top-level field `included`, as per the definition of `included`, see section JSON Response Schema: Common Fields.\n\n> **Note**: A query with the parameter `include` set to the empty string means no related resource objects are to be returned under the top-level field `included`.", "required": false, "schema": { - "title": "Include", "type": "string", + "title": "Include", "description": "A server MAY implement the JSON API concept of returning [compound documents](https://jsonapi.org/format/1.0/#document-compound-documents) by utilizing the `include` query parameter as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-includes).\n\nAll related resource objects MUST be returned as part of an array value for the top-level `included` field, see the section JSON Response Schema: Common Fields.\n\nThe value of `include` MUST be a comma-separated list of \"relationship paths\", as defined in the [JSON API](https://jsonapi.org/format/1.0/#fetching-includes).\nIf relationship paths are not supported, or a server is unable to identify a relationship path a `400 Bad Request` response MUST be made.\n\nThe **default value** for `include` is `references`.\nThis means `references` entries MUST always be included under the top-level field `included` as default, since a server assumes if `include` is not specified by a client in the request, it is still specified as `include=references`.\nNote, if a client explicitly specifies `include` and leaves out `references`, `references` resource objects MUST NOT be included under the top-level field `included`, as per the definition of `included`, see section JSON Response Schema: Common Fields.\n\n> **Note**: A query with the parameter `include` set to the empty string means no related resource objects are to be returned under the top-level field `included`.", "default": "references" }, @@ -611,9 +611,9 @@ "description": "If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.", "required": false, "schema": { - "title": "Api Hint", - "pattern": "(v[0-9]+(\\.[0-9]+)?)?", "type": "string", + "pattern": "(v[0-9]+(\\.[0-9]+)?)?", + "title": "Api Hint", "description": "If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.", "default": "" }, @@ -716,8 +716,8 @@ { "required": true, "schema": { - "title": "Entry Id", - "type": "string" + "type": "string", + "title": "Entry Id" }, "name": "entry_id", "in": "path" @@ -726,8 +726,8 @@ "description": "The output format requested (see section Response Format).\nDefaults to the format string 'json', which specifies the standard output format described in this specification.\nExample: `http://example.com/v1/structures?response_format=xml`", "required": false, "schema": { - "title": "Response Format", "type": "string", + "title": "Response Format", "description": "The output format requested (see section Response Format).\nDefaults to the format string 'json', which specifies the standard output format described in this specification.\nExample: `http://example.com/v1/structures?response_format=xml`", "default": "json" }, @@ -738,10 +738,10 @@ "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "required": false, "schema": { - "title": "Email Address", "type": "string", - "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "format": "email", + "title": "Email Address", + "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "default": "" }, "name": "email_address", @@ -751,9 +751,9 @@ "description": "A comma-delimited set of fields to be provided in the output.\nIf provided, these fields MUST be returned along with the REQUIRED fields.\nOther OPTIONAL fields MUST NOT be returned when this parameter is present.\nExample: `http://example.com/v1/structures?response_fields=last_modified,nsites`", "required": false, "schema": { - "title": "Response Fields", - "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", "type": "string", + "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", + "title": "Response Fields", "description": "A comma-delimited set of fields to be provided in the output.\nIf provided, these fields MUST be returned along with the REQUIRED fields.\nOther OPTIONAL fields MUST NOT be returned when this parameter is present.\nExample: `http://example.com/v1/structures?response_fields=last_modified,nsites`", "default": "" }, @@ -764,8 +764,8 @@ "description": "A server MAY implement the JSON API concept of returning [compound documents](https://jsonapi.org/format/1.0/#document-compound-documents) by utilizing the `include` query parameter as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-includes).\n\nAll related resource objects MUST be returned as part of an array value for the top-level `included` field, see the section JSON Response Schema: Common Fields.\n\nThe value of `include` MUST be a comma-separated list of \"relationship paths\", as defined in the [JSON API](https://jsonapi.org/format/1.0/#fetching-includes).\nIf relationship paths are not supported, or a server is unable to identify a relationship path a `400 Bad Request` response MUST be made.\n\nThe **default value** for `include` is `references`.\nThis means `references` entries MUST always be included under the top-level field `included` as default, since a server assumes if `include` is not specified by a client in the request, it is still specified as `include=references`.\nNote, if a client explicitly specifies `include` and leaves out `references`, `references` resource objects MUST NOT be included under the top-level field `included`, as per the definition of `included`, see section JSON Response Schema: Common Fields.\n\n> **Note**: A query with the parameter `include` set to the empty string means no related resource objects are to be returned under the top-level field `included`.", "required": false, "schema": { - "title": "Include", "type": "string", + "title": "Include", "description": "A server MAY implement the JSON API concept of returning [compound documents](https://jsonapi.org/format/1.0/#document-compound-documents) by utilizing the `include` query parameter as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-includes).\n\nAll related resource objects MUST be returned as part of an array value for the top-level `included` field, see the section JSON Response Schema: Common Fields.\n\nThe value of `include` MUST be a comma-separated list of \"relationship paths\", as defined in the [JSON API](https://jsonapi.org/format/1.0/#fetching-includes).\nIf relationship paths are not supported, or a server is unable to identify a relationship path a `400 Bad Request` response MUST be made.\n\nThe **default value** for `include` is `references`.\nThis means `references` entries MUST always be included under the top-level field `included` as default, since a server assumes if `include` is not specified by a client in the request, it is still specified as `include=references`.\nNote, if a client explicitly specifies `include` and leaves out `references`, `references` resource objects MUST NOT be included under the top-level field `included`, as per the definition of `included`, see section JSON Response Schema: Common Fields.\n\n> **Note**: A query with the parameter `include` set to the empty string means no related resource objects are to be returned under the top-level field `included`.", "default": "references" }, @@ -776,9 +776,9 @@ "description": "If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.", "required": false, "schema": { - "title": "Api Hint", - "pattern": "(v[0-9]+(\\.[0-9]+)?)?", "type": "string", + "pattern": "(v[0-9]+(\\.[0-9]+)?)?", + "title": "Api Hint", "description": "If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.", "default": "" }, @@ -882,8 +882,8 @@ "description": "A filter string, in the format described in section API Filtering Format Specification of the specification.", "required": false, "schema": { - "title": "Filter", "type": "string", + "title": "Filter", "description": "A filter string, in the format described in section API Filtering Format Specification of the specification.", "default": "" }, @@ -894,8 +894,8 @@ "description": "The output format requested (see section Response Format).\nDefaults to the format string 'json', which specifies the standard output format described in this specification.\nExample: `http://example.com/v1/structures?response_format=xml`", "required": false, "schema": { - "title": "Response Format", "type": "string", + "title": "Response Format", "description": "The output format requested (see section Response Format).\nDefaults to the format string 'json', which specifies the standard output format described in this specification.\nExample: `http://example.com/v1/structures?response_format=xml`", "default": "json" }, @@ -906,10 +906,10 @@ "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "required": false, "schema": { - "title": "Email Address", "type": "string", - "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "format": "email", + "title": "Email Address", + "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "default": "" }, "name": "email_address", @@ -919,9 +919,9 @@ "description": "A comma-delimited set of fields to be provided in the output.\nIf provided, these fields MUST be returned along with the REQUIRED fields.\nOther OPTIONAL fields MUST NOT be returned when this parameter is present.\nExample: `http://example.com/v1/structures?response_fields=last_modified,nsites`", "required": false, "schema": { - "title": "Response Fields", - "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", "type": "string", + "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", + "title": "Response Fields", "description": "A comma-delimited set of fields to be provided in the output.\nIf provided, these fields MUST be returned along with the REQUIRED fields.\nOther OPTIONAL fields MUST NOT be returned when this parameter is present.\nExample: `http://example.com/v1/structures?response_fields=last_modified,nsites`", "default": "" }, @@ -932,9 +932,9 @@ "description": "If supporting sortable queries, an implementation MUST use the `sort` query parameter with format as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-sorting).\n\nAn implementation MAY support multiple sort fields for a single query.\nIf it does, it again MUST conform to the JSON API 1.0 specification.\n\nIf an implementation supports sorting for an entry listing endpoint, then the `/info/` endpoint MUST include, for each field name `` in its `data.properties.` response value that can be used for sorting, the key `sortable` with value `true`.\nIf a field name under an entry listing endpoint supporting sorting cannot be used for sorting, the server MUST either leave out the `sortable` key or set it equal to `false` for the specific field name.\nThe set of field names, with `sortable` equal to `true` are allowed to be used in the \"sort fields\" list according to its definition in the JSON API 1.0 specification.\nThe field `sortable` is in addition to each property description and other OPTIONAL fields.\nAn example is shown in the section Entry Listing Info Endpoints.", "required": false, "schema": { - "title": "Sort", - "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", "type": "string", + "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", + "title": "Sort", "description": "If supporting sortable queries, an implementation MUST use the `sort` query parameter with format as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-sorting).\n\nAn implementation MAY support multiple sort fields for a single query.\nIf it does, it again MUST conform to the JSON API 1.0 specification.\n\nIf an implementation supports sorting for an entry listing endpoint, then the `/info/` endpoint MUST include, for each field name `` in its `data.properties.` response value that can be used for sorting, the key `sortable` with value `true`.\nIf a field name under an entry listing endpoint supporting sorting cannot be used for sorting, the server MUST either leave out the `sortable` key or set it equal to `false` for the specific field name.\nThe set of field names, with `sortable` equal to `true` are allowed to be used in the \"sort fields\" list according to its definition in the JSON API 1.0 specification.\nThe field `sortable` is in addition to each property description and other OPTIONAL fields.\nAn example is shown in the section Entry Listing Info Endpoints.", "default": "" }, @@ -945,9 +945,9 @@ "description": "Sets a numerical limit on the number of entries returned.\nSee [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-pagination).\nThe API implementation MUST return no more than the number specified.\nIt MAY return fewer.\nThe database MAY have a maximum limit and not accept larger numbers (in which case an error code -- 403 Forbidden -- MUST be returned).\nThe default limit value is up to the API implementation to decide.\nExample: `http://example.com/optimade/v1/structures?page_limit=100`", "required": false, "schema": { - "title": "Page Limit", - "minimum": 0.0, "type": "integer", + "minimum": 0.0, + "title": "Page Limit", "description": "Sets a numerical limit on the number of entries returned.\nSee [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-pagination).\nThe API implementation MUST return no more than the number specified.\nIt MAY return fewer.\nThe database MAY have a maximum limit and not accept larger numbers (in which case an error code -- 403 Forbidden -- MUST be returned).\nThe default limit value is up to the API implementation to decide.\nExample: `http://example.com/optimade/v1/structures?page_limit=100`", "default": 20 }, @@ -958,9 +958,9 @@ "description": "RECOMMENDED for use with _offset-based_ pagination: using `page_offset` and `page_limit` is RECOMMENDED.\nExample: Skip 50 structures and fetch up to 100: `/structures?page_offset=50&page_limit=100`.", "required": false, "schema": { - "title": "Page Offset", - "minimum": 0.0, "type": "integer", + "minimum": 0.0, + "title": "Page Offset", "description": "RECOMMENDED for use with _offset-based_ pagination: using `page_offset` and `page_limit` is RECOMMENDED.\nExample: Skip 50 structures and fetch up to 100: `/structures?page_offset=50&page_limit=100`.", "default": 0 }, @@ -971,8 +971,8 @@ "description": "RECOMMENDED for use with _page-based_ pagination: using `page_number` and `page_limit` is RECOMMENDED.\nIt is RECOMMENDED that the first page has number 1, i.e., that `page_number` is 1-based.\nExample: Fetch page 2 of up to 50 structures per page: `/structures?page_number=2&page_limit=50`.", "required": false, "schema": { - "title": "Page Number", "type": "integer", + "title": "Page Number", "description": "RECOMMENDED for use with _page-based_ pagination: using `page_number` and `page_limit` is RECOMMENDED.\nIt is RECOMMENDED that the first page has number 1, i.e., that `page_number` is 1-based.\nExample: Fetch page 2 of up to 50 structures per page: `/structures?page_number=2&page_limit=50`." }, "name": "page_number", @@ -982,9 +982,9 @@ "description": "RECOMMENDED for use with _cursor-based_ pagination: using `page_cursor` and `page_limit` is RECOMMENDED.", "required": false, "schema": { - "title": "Page Cursor", - "minimum": 0.0, "type": "integer", + "minimum": 0.0, + "title": "Page Cursor", "description": "RECOMMENDED for use with _cursor-based_ pagination: using `page_cursor` and `page_limit` is RECOMMENDED.", "default": 0 }, @@ -995,8 +995,8 @@ "description": "RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED.\nExample: Fetch up to 100 structures above sort-field value 4000 (in this example, server chooses to fetch results sorted by increasing `id`, so `page_above` value refers to an `id` value): `/structures?page_above=4000&page_limit=100`.", "required": false, "schema": { - "title": "Page Above", "type": "string", + "title": "Page Above", "description": "RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED.\nExample: Fetch up to 100 structures above sort-field value 4000 (in this example, server chooses to fetch results sorted by increasing `id`, so `page_above` value refers to an `id` value): `/structures?page_above=4000&page_limit=100`." }, "name": "page_above", @@ -1006,8 +1006,8 @@ "description": "RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED.", "required": false, "schema": { - "title": "Page Below", "type": "string", + "title": "Page Below", "description": "RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED." }, "name": "page_below", @@ -1017,8 +1017,8 @@ "description": "A server MAY implement the JSON API concept of returning [compound documents](https://jsonapi.org/format/1.0/#document-compound-documents) by utilizing the `include` query parameter as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-includes).\n\nAll related resource objects MUST be returned as part of an array value for the top-level `included` field, see the section JSON Response Schema: Common Fields.\n\nThe value of `include` MUST be a comma-separated list of \"relationship paths\", as defined in the [JSON API](https://jsonapi.org/format/1.0/#fetching-includes).\nIf relationship paths are not supported, or a server is unable to identify a relationship path a `400 Bad Request` response MUST be made.\n\nThe **default value** for `include` is `references`.\nThis means `references` entries MUST always be included under the top-level field `included` as default, since a server assumes if `include` is not specified by a client in the request, it is still specified as `include=references`.\nNote, if a client explicitly specifies `include` and leaves out `references`, `references` resource objects MUST NOT be included under the top-level field `included`, as per the definition of `included`, see section JSON Response Schema: Common Fields.\n\n> **Note**: A query with the parameter `include` set to the empty string means no related resource objects are to be returned under the top-level field `included`.", "required": false, "schema": { - "title": "Include", "type": "string", + "title": "Include", "description": "A server MAY implement the JSON API concept of returning [compound documents](https://jsonapi.org/format/1.0/#document-compound-documents) by utilizing the `include` query parameter as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-includes).\n\nAll related resource objects MUST be returned as part of an array value for the top-level `included` field, see the section JSON Response Schema: Common Fields.\n\nThe value of `include` MUST be a comma-separated list of \"relationship paths\", as defined in the [JSON API](https://jsonapi.org/format/1.0/#fetching-includes).\nIf relationship paths are not supported, or a server is unable to identify a relationship path a `400 Bad Request` response MUST be made.\n\nThe **default value** for `include` is `references`.\nThis means `references` entries MUST always be included under the top-level field `included` as default, since a server assumes if `include` is not specified by a client in the request, it is still specified as `include=references`.\nNote, if a client explicitly specifies `include` and leaves out `references`, `references` resource objects MUST NOT be included under the top-level field `included`, as per the definition of `included`, see section JSON Response Schema: Common Fields.\n\n> **Note**: A query with the parameter `include` set to the empty string means no related resource objects are to be returned under the top-level field `included`.", "default": "references" }, @@ -1029,9 +1029,9 @@ "description": "If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.", "required": false, "schema": { - "title": "Api Hint", - "pattern": "(v[0-9]+(\\.[0-9]+)?)?", "type": "string", + "pattern": "(v[0-9]+(\\.[0-9]+)?)?", + "title": "Api Hint", "description": "If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.", "default": "" }, @@ -1134,8 +1134,8 @@ { "required": true, "schema": { - "title": "Entry Id", - "type": "string" + "type": "string", + "title": "Entry Id" }, "name": "entry_id", "in": "path" @@ -1144,8 +1144,8 @@ "description": "The output format requested (see section Response Format).\nDefaults to the format string 'json', which specifies the standard output format described in this specification.\nExample: `http://example.com/v1/structures?response_format=xml`", "required": false, "schema": { - "title": "Response Format", "type": "string", + "title": "Response Format", "description": "The output format requested (see section Response Format).\nDefaults to the format string 'json', which specifies the standard output format described in this specification.\nExample: `http://example.com/v1/structures?response_format=xml`", "default": "json" }, @@ -1156,10 +1156,10 @@ "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "required": false, "schema": { - "title": "Email Address", "type": "string", - "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "format": "email", + "title": "Email Address", + "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "default": "" }, "name": "email_address", @@ -1169,9 +1169,9 @@ "description": "A comma-delimited set of fields to be provided in the output.\nIf provided, these fields MUST be returned along with the REQUIRED fields.\nOther OPTIONAL fields MUST NOT be returned when this parameter is present.\nExample: `http://example.com/v1/structures?response_fields=last_modified,nsites`", "required": false, "schema": { - "title": "Response Fields", - "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", "type": "string", + "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", + "title": "Response Fields", "description": "A comma-delimited set of fields to be provided in the output.\nIf provided, these fields MUST be returned along with the REQUIRED fields.\nOther OPTIONAL fields MUST NOT be returned when this parameter is present.\nExample: `http://example.com/v1/structures?response_fields=last_modified,nsites`", "default": "" }, @@ -1182,8 +1182,8 @@ "description": "A server MAY implement the JSON API concept of returning [compound documents](https://jsonapi.org/format/1.0/#document-compound-documents) by utilizing the `include` query parameter as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-includes).\n\nAll related resource objects MUST be returned as part of an array value for the top-level `included` field, see the section JSON Response Schema: Common Fields.\n\nThe value of `include` MUST be a comma-separated list of \"relationship paths\", as defined in the [JSON API](https://jsonapi.org/format/1.0/#fetching-includes).\nIf relationship paths are not supported, or a server is unable to identify a relationship path a `400 Bad Request` response MUST be made.\n\nThe **default value** for `include` is `references`.\nThis means `references` entries MUST always be included under the top-level field `included` as default, since a server assumes if `include` is not specified by a client in the request, it is still specified as `include=references`.\nNote, if a client explicitly specifies `include` and leaves out `references`, `references` resource objects MUST NOT be included under the top-level field `included`, as per the definition of `included`, see section JSON Response Schema: Common Fields.\n\n> **Note**: A query with the parameter `include` set to the empty string means no related resource objects are to be returned under the top-level field `included`.", "required": false, "schema": { - "title": "Include", "type": "string", + "title": "Include", "description": "A server MAY implement the JSON API concept of returning [compound documents](https://jsonapi.org/format/1.0/#document-compound-documents) by utilizing the `include` query parameter as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-includes).\n\nAll related resource objects MUST be returned as part of an array value for the top-level `included` field, see the section JSON Response Schema: Common Fields.\n\nThe value of `include` MUST be a comma-separated list of \"relationship paths\", as defined in the [JSON API](https://jsonapi.org/format/1.0/#fetching-includes).\nIf relationship paths are not supported, or a server is unable to identify a relationship path a `400 Bad Request` response MUST be made.\n\nThe **default value** for `include` is `references`.\nThis means `references` entries MUST always be included under the top-level field `included` as default, since a server assumes if `include` is not specified by a client in the request, it is still specified as `include=references`.\nNote, if a client explicitly specifies `include` and leaves out `references`, `references` resource objects MUST NOT be included under the top-level field `included`, as per the definition of `included`, see section JSON Response Schema: Common Fields.\n\n> **Note**: A query with the parameter `include` set to the empty string means no related resource objects are to be returned under the top-level field `included`.", "default": "references" }, @@ -1194,9 +1194,9 @@ "description": "If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.", "required": false, "schema": { - "title": "Api Hint", - "pattern": "(v[0-9]+(\\.[0-9]+)?)?", "type": "string", + "pattern": "(v[0-9]+(\\.[0-9]+)?)?", + "title": "Api Hint", "description": "If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.", "default": "" }, @@ -1314,76 +1314,70 @@ "components": { "schemas": { "Aggregate": { - "title": "Aggregate", "enum": [ "ok", "test", "staging", "no" ], + "title": "Aggregate", "description": "Enumeration of aggregate values" }, "Assembly": { - "title": "Assembly", - "required": [ - "sites_in_groups", - "group_probabilities" - ], - "type": "object", "properties": { "sites_in_groups": { - "title": "Sites In Groups", - "type": "array", "items": { - "type": "array", "items": { "type": "integer" - } + }, + "type": "array" }, + "type": "array", + "title": "Sites In Groups", "description": "Index of the sites (0-based) that belong to each group for each assembly.\n\n- **Examples**:\n - `[[1], [2]]`: two groups, one with the second site, one with the third.\n - `[[1,2], [3]]`: one group with the second and third site, one with the fourth.", "x-optimade-support": "must", "x-optimade-queryable": "optional" }, "group_probabilities": { - "title": "Group Probabilities", - "type": "array", "items": { "type": "number" }, + "type": "array", + "title": "Group Probabilities", "description": "Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\nIt SHOULD sum to one.\nSee below for examples of how to specify the probability of the occurrence of a vacancy.\nThe possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.", "x-optimade-support": "must", "x-optimade-queryable": "optional" } }, + "type": "object", + "required": [ + "sites_in_groups", + "group_probabilities" + ], + "title": "Assembly", "description": "A description of groups of sites that are statistically correlated.\n\n- **Examples** (for each entry of the assemblies list):\n - `{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}`: the first site and the second site never occur at the same time in the unit cell.\n Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.\n - `{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}`: the second and third site are either present together or not present; they form the first group of atoms for this assembly.\n The second group is formed by the fourth site. Sites of the first group (the second and the third) are never present at the same time as the fourth site.\n 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent)." }, "Attributes": { - "title": "Attributes", - "type": "object", "properties": {}, + "type": "object", + "title": "Attributes", "description": "Members of the attributes object (\"attributes\") represent information about the resource object in which it's defined.\nThe keys for Attributes MUST NOT be:\n relationships\n links\n id\n type" }, "AvailableApiVersion": { - "title": "AvailableApiVersion", - "required": [ - "url", - "version" - ], - "type": "object", "properties": { "url": { - "title": "Url", + "type": "string", "maxLength": 65536, "minLength": 1, "pattern": ".+/v[0-1](\\.[0-9]+)*/?$", - "type": "string", - "description": "A string specifying a versioned base URL that MUST adhere to the rules in section Base URL", - "format": "uri" + "format": "uri", + "title": "Url", + "description": "A string specifying a versioned base URL that MUST adhere to the rules in section Base URL" }, "version": { - "title": "Version", - "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", "type": "string", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", + "title": "Version", "description": "A string containing the full version number of the API served at that versioned base URL.\nThe version number string MUST NOT be prefixed by, e.g., 'v'.\nExamples: `1.0.0`, `1.0.0-rc.2`.", "example": [ "0.10.1", @@ -1392,22 +1386,20 @@ ] } }, + "type": "object", + "required": [ + "url", + "version" + ], + "title": "AvailableApiVersion", "description": "A JSON object containing information about an available API version" }, "BaseInfoAttributes": { - "title": "BaseInfoAttributes", - "required": [ - "api_version", - "available_api_versions", - "available_endpoints", - "entry_types_by_format" - ], - "type": "object", "properties": { "api_version": { - "title": "Api Version", - "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", "type": "string", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", + "title": "Api Version", "description": "Presently used full version of the OPTIMADE API.\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\nExamples: `1.0.0`, `1.0.0-rc.2`.", "example": [ "0.10.1", @@ -1416,153 +1408,160 @@ ] }, "available_api_versions": { - "title": "Available Api Versions", - "type": "array", "items": { "$ref": "#/components/schemas/AvailableApiVersion" }, + "type": "array", + "title": "Available Api Versions", "description": "A list of dictionaries of available API versions at other base URLs" }, "formats": { - "title": "Formats", - "type": "array", "items": { "type": "string" }, + "type": "array", + "title": "Formats", "description": "List of available output formats.", "default": [ "json" ] }, "available_endpoints": { - "title": "Available Endpoints", - "type": "array", "items": { "type": "string" }, + "type": "array", + "title": "Available Endpoints", "description": "List of available endpoints (i.e., the string to be appended to the versioned base URL)." }, "entry_types_by_format": { - "title": "Entry Types By Format", - "type": "object", "additionalProperties": { - "type": "array", "items": { "type": "string" - } + }, + "type": "array" }, + "type": "object", + "title": "Entry Types By Format", "description": "Available entry endpoints as a function of output formats." }, "is_index": { - "title": "Is Index", "type": "boolean", + "title": "Is Index", "description": "If true, this is an index meta-database base URL (see section Index Meta-Database). If this member is not provided, the client MUST assume this is not an index meta-database base URL (i.e., the default is for `is_index` to be `false`).", "default": false } }, + "type": "object", + "required": [ + "api_version", + "available_api_versions", + "available_endpoints", + "entry_types_by_format" + ], + "title": "BaseInfoAttributes", "description": "Attributes for Base URL Info endpoint" }, "BaseInfoResource": { - "title": "BaseInfoResource", - "required": [ - "id", - "type", - "attributes" - ], - "type": "object", "properties": { "id": { - "title": "Id", - "pattern": "^/$", "type": "string", + "pattern": "^/$", + "title": "Id", "default": "/" }, "type": { - "title": "Type", - "pattern": "^info$", "type": "string", + "pattern": "^info$", + "title": "Type", "default": "info" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ResourceLinks" } ], + "title": "Links", "description": "a links object containing links related to the resource." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship." }, "attributes": { "$ref": "#/components/schemas/BaseInfoAttributes" }, "relationships": { - "title": "Relationships", "allOf": [ { "$ref": "#/components/schemas/Relationships" } ], + "title": "Relationships", "description": "[Relationships object](https://jsonapi.org/format/1.0/#document-resource-object-relationships)\ndescribing relationships between the resource and other JSON API resources." } }, + "type": "object", + "required": [ + "id", + "type", + "attributes" + ], + "title": "BaseInfoResource", "description": "Resource objects appear in a JSON API document to represent resources." }, "BaseRelationshipMeta": { - "title": "BaseRelationshipMeta", - "required": [ - "description" - ], - "type": "object", "properties": { "description": { - "title": "Description", "type": "string", + "title": "Description", "description": "OPTIONAL human-readable description of the relationship." } }, + "type": "object", + "required": [ + "description" + ], + "title": "BaseRelationshipMeta", "description": "Specific meta field for base relationship resource" }, "BaseRelationshipResource": { - "title": "BaseRelationshipResource", - "required": [ - "id", - "type" - ], - "type": "object", "properties": { "id": { - "title": "Id", "type": "string", + "title": "Id", "description": "Resource ID" }, "type": { - "title": "Type", "type": "string", + "title": "Type", "description": "Resource type" }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/BaseRelationshipMeta" } ], + "title": "Meta", "description": "Relationship meta field. MUST contain 'description' if supplied." } }, + "type": "object", + "required": [ + "id", + "type" + ], + "title": "BaseRelationshipResource", "description": "Minimum requirements to represent a relationship resource" }, "DataType": { - "title": "DataType", "enum": [ "string", "integer", @@ -1573,1439 +1572,1433 @@ "dictionary", "unknown" ], + "title": "DataType", "description": "Optimade Data Types\n\nSee the section \"Data types\" in the OPTIMADE API specification for more information." }, "EntryInfoProperty": { - "title": "EntryInfoProperty", - "required": [ - "description" - ], - "type": "object", "properties": { "description": { - "title": "Description", "type": "string", + "title": "Description", "description": "A human-readable description of the entry property" }, "unit": { - "title": "Unit", "type": "string", + "title": "Unit", "description": "The physical unit of the entry property.\nThis MUST be a valid representation of units according to version 2.1 of [The Unified Code for Units of Measure](https://unitsofmeasure.org/ucum.html).\nIt is RECOMMENDED that non-standard (non-SI) units are described in the description for the property." }, "sortable": { - "title": "Sortable", "type": "boolean", + "title": "Sortable", "description": "Defines whether the entry property can be used for sorting with the \"sort\" parameter.\nIf the entry listing endpoint supports sorting, this key MUST be present for sortable properties with value `true`." }, "type": { - "title": "Type", "allOf": [ { "$ref": "#/components/schemas/DataType" } ], + "title": "Type", "description": "The type of the property's value.\nThis MUST be any of the types defined in the Data types section.\nFor the purpose of compatibility with future versions of this specification, a client MUST accept values that are not `string` values specifying any of the OPTIMADE Data types, but MUST then also disregard the `type` field.\nNote, if the value is a nested type, only the outermost type should be reported.\nE.g., for the entry resource `structures`, the `species` property is defined as a list of dictionaries, hence its `type` value would be `list`." } - } - }, - "EntryInfoResource": { - "title": "EntryInfoResource", + }, + "type": "object", "required": [ - "formats", - "description", - "properties", - "output_fields_by_format" + "description" ], - "type": "object", + "title": "EntryInfoProperty" + }, + "EntryInfoResource": { "properties": { "formats": { - "title": "Formats", - "type": "array", "items": { "type": "string" }, + "type": "array", + "title": "Formats", "description": "List of output formats available for this type of entry." }, "description": { - "title": "Description", "type": "string", + "title": "Description", "description": "Description of the entry." }, "properties": { - "title": "Properties", - "type": "object", "additionalProperties": { "$ref": "#/components/schemas/EntryInfoProperty" }, + "type": "object", + "title": "Properties", "description": "A dictionary describing queryable properties for this entry type, where each key is a property name." }, "output_fields_by_format": { - "title": "Output Fields By Format", - "type": "object", "additionalProperties": { - "type": "array", "items": { "type": "string" - } + }, + "type": "array" }, + "type": "object", + "title": "Output Fields By Format", "description": "Dictionary of available output fields for this entry type, where the keys are the values of the `formats` list and the values are the keys of the `properties` dictionary." } - } - }, - "EntryInfoResponse": { - "title": "EntryInfoResponse", + }, + "type": "object", "required": [ - "data", - "meta" + "formats", + "description", + "properties", + "output_fields_by_format" ], - "type": "object", + "title": "EntryInfoResource" + }, + "EntryInfoResponse": { "properties": { "data": { - "title": "Data", "allOf": [ { "$ref": "#/components/schemas/EntryInfoResource" } ], + "title": "Data", "description": "OPTIMADE information for an entry endpoint." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/ResponseMeta" } ], + "title": "Meta", "description": "A meta object containing non-standard information" }, "errors": { - "title": "Errors", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/Error" }, + "type": "array", + "uniqueItems": true, + "title": "Errors", "description": "A list of unique errors" }, "included": { - "title": "Included", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/Resource" }, + "type": "array", + "uniqueItems": true, + "title": "Included", "description": "A list of unique included resources" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ToplevelLinks" } ], + "title": "Links", "description": "Links associated with the primary data or errors" }, "jsonapi": { - "title": "Jsonapi", "allOf": [ { "$ref": "#/components/schemas/JsonApi" } ], + "title": "Jsonapi", "description": "Information about the JSON API used" } }, + "type": "object", + "required": [ + "data", + "meta" + ], + "title": "EntryInfoResponse", "description": "errors are not allowed" }, "EntryMetadata": { - "title": "EntryMetadata", - "type": "object", "properties": { "property_metadata": { - "title": "Property Metadata", "type": "object", + "title": "Property Metadata", "description": "A dictionary, where the keys are the names of the properties in the attributes field and the value is a dictionary containing the metadata for that property.\nDatabase-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes)." } }, + "type": "object", + "title": "EntryMetadata", "description": "Contains the metadata for the attributes of an entry" }, "EntryRelationships": { - "title": "EntryRelationships", - "type": "object", "properties": { "references": { - "title": "References", "allOf": [ { "$ref": "#/components/schemas/ReferenceRelationship" } ], + "title": "References", "description": "Object containing links to relationships with entries of the `references` type." }, "structures": { - "title": "Structures", "allOf": [ { "$ref": "#/components/schemas/StructureRelationship" } ], + "title": "Structures", "description": "Object containing links to relationships with entries of the `structures` type." } }, + "type": "object", + "title": "EntryRelationships", "description": "This model wraps the JSON API Relationships to include type-specific top level keys." }, "EntryResource": { - "title": "EntryResource", - "required": [ - "id", - "type", - "attributes" - ], - "type": "object", "properties": { "id": { - "title": "Id", "type": "string", + "title": "Id", "description": "An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`", "x-optimade-support": "must", "x-optimade-queryable": "must" }, "type": { - "title": "Type", "type": "string", + "title": "Type", "description": "The name of the type of an entry.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type `` and ID `` MUST be returned in response to a request for `//` under the versioned base URL.\n\n- **Example**: `\"structures\"`", "x-optimade-support": "must", "x-optimade-queryable": "must" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ResourceLinks" } ], + "title": "Links", "description": "a links object containing links related to the resource." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/EntryMetadata" } ], + "title": "Meta", "description": "A dictionary, containing entry and property-specific metadata for a given entry." }, "attributes": { - "title": "Attributes", "allOf": [ { "$ref": "#/components/schemas/EntryResourceAttributes" } ], + "title": "Attributes", "description": "A dictionary, containing key-value pairs representing the entry's properties, except for `type` and `id`.\nDatabase-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes)." }, "relationships": { - "title": "Relationships", "allOf": [ { "$ref": "#/components/schemas/EntryRelationships" } ], + "title": "Relationships", "description": "A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object." } }, + "type": "object", + "required": [ + "id", + "type", + "attributes" + ], + "title": "EntryResource", "description": "The base model for an entry resource." }, "EntryResourceAttributes": { - "title": "EntryResourceAttributes", - "required": [ - "last_modified" - ], - "type": "object", "properties": { "immutable_id": { - "title": "Immutable Id", "type": "string", + "title": "Immutable Id", "description": "The entry's immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)", "x-optimade-support": "optional", "x-optimade-queryable": "must" }, "last_modified": { - "title": "Last Modified", "type": "string", - "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "format": "date-time", + "title": "Last Modified", + "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "x-optimade-support": "should", "x-optimade-queryable": "must" } }, + "type": "object", + "required": [ + "last_modified" + ], + "title": "EntryResourceAttributes", "description": "Contains key-value pairs representing the entry's properties." }, "Error": { - "title": "Error", - "type": "object", "properties": { "id": { - "title": "Id", "type": "string", + "title": "Id", "description": "A unique identifier for this particular occurrence of the problem." }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ErrorLinks" } ], + "title": "Links", "description": "A links object storing about" }, "status": { - "title": "Status", "type": "string", + "title": "Status", "description": "the HTTP status code applicable to this problem, expressed as a string value." }, "code": { - "title": "Code", "type": "string", + "title": "Code", "description": "an application-specific error code, expressed as a string value." }, "title": { - "title": "Title", "type": "string", + "title": "Title", "description": "A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization." }, "detail": { - "title": "Detail", "type": "string", + "title": "Detail", "description": "A human-readable explanation specific to this occurrence of the problem." }, "source": { - "title": "Source", "allOf": [ { "$ref": "#/components/schemas/ErrorSource" } ], + "title": "Source", "description": "An object containing references to the source of the error" }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "a meta object containing non-standard meta-information about the error." } }, + "type": "object", + "title": "Error", "description": "An error response" }, "ErrorLinks": { - "title": "ErrorLinks", - "type": "object", "properties": { "about": { - "title": "About", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "About", "description": "A link that leads to further details about this particular occurrence of the problem." } }, + "type": "object", + "title": "ErrorLinks", "description": "A Links object specific to Error objects" }, "ErrorResponse": { - "title": "ErrorResponse", - "required": [ - "meta", - "errors" - ], - "type": "object", "properties": { "data": { - "title": "Data", - "uniqueItems": true, "anyOf": [ { "$ref": "#/components/schemas/Resource" }, { - "type": "array", "items": { "$ref": "#/components/schemas/Resource" - } + }, + "type": "array" } ], + "uniqueItems": true, + "title": "Data", "description": "Outputted Data" }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/ResponseMeta" } ], + "title": "Meta", "description": "A meta object containing non-standard information." }, "errors": { - "title": "Errors", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/OptimadeError" }, + "type": "array", + "uniqueItems": true, + "title": "Errors", "description": "A list of OPTIMADE-specific JSON API error objects, where the field detail MUST be present." }, "included": { - "title": "Included", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/Resource" }, + "type": "array", + "uniqueItems": true, + "title": "Included", "description": "A list of unique included resources" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ToplevelLinks" } ], + "title": "Links", "description": "Links associated with the primary data or errors" }, "jsonapi": { - "title": "Jsonapi", "allOf": [ { "$ref": "#/components/schemas/JsonApi" } ], + "title": "Jsonapi", "description": "Information about the JSON API used" } }, + "type": "object", + "required": [ + "meta", + "errors" + ], + "title": "ErrorResponse", "description": "errors MUST be present and data MUST be skipped" }, "ErrorSource": { - "title": "ErrorSource", - "type": "object", "properties": { "pointer": { - "title": "Pointer", "type": "string", + "title": "Pointer", "description": "a JSON Pointer [RFC6901] to the associated entity in the request document [e.g. \"/data\" for a primary data object, or \"/data/attributes/title\" for a specific attribute]." }, "parameter": { - "title": "Parameter", "type": "string", + "title": "Parameter", "description": "a string indicating which URI query parameter caused the error." } }, + "type": "object", + "title": "ErrorSource", "description": "an object containing references to the source of the error" }, "Implementation": { - "title": "Implementation", - "type": "object", "properties": { "name": { - "title": "Name", "type": "string", + "title": "Name", "description": "name of the implementation" }, "version": { - "title": "Version", "type": "string", + "title": "Version", "description": "version string of the current implementation" }, "homepage": { - "title": "Homepage", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Homepage", "description": "A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the homepage of the implementation." }, "source_url": { - "title": "Source Url", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Source Url", "description": "A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the implementation source, either downloadable archive or version control system." }, "maintainer": { - "title": "Maintainer", "allOf": [ { "$ref": "#/components/schemas/ImplementationMaintainer" } ], + "title": "Maintainer", "description": "A dictionary providing details about the maintainer of the implementation." }, "issue_tracker": { - "title": "Issue Tracker", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Issue Tracker", "description": "A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the implementation's issue tracker." } }, + "type": "object", + "title": "Implementation", "description": "Information on the server implementation" }, "ImplementationMaintainer": { - "title": "ImplementationMaintainer", - "required": [ - "email" - ], - "type": "object", "properties": { "email": { - "title": "Email", "type": "string", - "description": "the maintainer's email address", - "format": "email" + "format": "email", + "title": "Email", + "description": "the maintainer's email address" } }, + "type": "object", + "required": [ + "email" + ], + "title": "ImplementationMaintainer", "description": "Details about the maintainer of the implementation" }, "InfoResponse": { - "title": "InfoResponse", - "required": [ - "data", - "meta" - ], - "type": "object", "properties": { "data": { - "title": "Data", "allOf": [ { "$ref": "#/components/schemas/BaseInfoResource" } ], + "title": "Data", "description": "The implementations /info data." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/ResponseMeta" } ], + "title": "Meta", "description": "A meta object containing non-standard information" }, "errors": { - "title": "Errors", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/Error" }, + "type": "array", + "uniqueItems": true, + "title": "Errors", "description": "A list of unique errors" }, "included": { - "title": "Included", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/Resource" }, + "type": "array", + "uniqueItems": true, + "title": "Included", "description": "A list of unique included resources" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ToplevelLinks" } ], + "title": "Links", "description": "Links associated with the primary data or errors" }, "jsonapi": { - "title": "Jsonapi", "allOf": [ { "$ref": "#/components/schemas/JsonApi" } ], + "title": "Jsonapi", "description": "Information about the JSON API used" } }, + "type": "object", + "required": [ + "data", + "meta" + ], + "title": "InfoResponse", "description": "errors are not allowed" }, "JsonApi": { - "title": "JsonApi", - "type": "object", "properties": { "version": { - "title": "Version", "type": "string", + "title": "Version", "description": "Version of the json API used", "default": "1.0" }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "Non-standard meta information" } }, + "type": "object", + "title": "JsonApi", "description": "An object describing the server's implementation" }, "Link": { - "title": "Link", - "required": [ - "href" - ], - "type": "object", "properties": { "href": { - "title": "Href", + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", - "description": "a string containing the link\u2019s URL.", - "format": "uri" + "format": "uri", + "title": "Href", + "description": "a string containing the link\u2019s URL." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "a meta object containing non-standard meta-information about the link." } }, + "type": "object", + "required": [ + "href" + ], + "title": "Link", "description": "A link **MUST** be represented as either: a string containing the link's URL or a link object." }, "LinkType": { - "title": "LinkType", "enum": [ "child", "root", "external", "providers" ], + "title": "LinkType", "description": "Enumeration of link_type values" }, "LinksResource": { - "title": "LinksResource", - "required": [ - "id", - "type", - "attributes" - ], - "type": "object", "properties": { "id": { - "title": "Id", "type": "string", + "title": "Id", "description": "An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`", "x-optimade-support": "must", "x-optimade-queryable": "must" }, "type": { - "title": "Type", - "pattern": "^links$", "type": "string", + "pattern": "^links$", + "title": "Type", "description": "These objects are described in detail in the section Links Endpoint", "default": "links" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ResourceLinks" } ], + "title": "Links", "description": "a links object containing links related to the resource." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/EntryMetadata" } ], + "title": "Meta", "description": "A dictionary, containing entry and property-specific metadata for a given entry." }, "attributes": { - "title": "Attributes", "allOf": [ { "$ref": "#/components/schemas/LinksResourceAttributes" } ], + "title": "Attributes", "description": "A dictionary containing key-value pairs representing the Links resource's properties." }, "relationships": { - "title": "Relationships", "allOf": [ { "$ref": "#/components/schemas/EntryRelationships" } ], + "title": "Relationships", "description": "A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object." } }, + "type": "object", + "required": [ + "id", + "type", + "attributes" + ], + "title": "LinksResource", "description": "A Links endpoint resource object" }, "LinksResourceAttributes": { - "title": "LinksResourceAttributes", - "required": [ - "name", - "description", - "base_url", - "homepage", - "link_type" - ], - "type": "object", "properties": { "name": { - "title": "Name", "type": "string", + "title": "Name", "description": "Human-readable name for the OPTIMADE API implementation, e.g., for use in clients to show the name to the end-user." }, "description": { - "title": "Description", "type": "string", + "title": "Description", "description": "Human-readable description for the OPTIMADE API implementation, e.g., for use in clients to show a description to the end-user." }, "base_url": { - "title": "Base Url", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Base Url", "description": "JSON API links object, pointing to the base URL for this implementation" }, "homepage": { - "title": "Homepage", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Homepage", "description": "JSON API links object, pointing to a homepage URL for this implementation" }, "link_type": { - "title": "Link Type", "allOf": [ { "$ref": "#/components/schemas/LinkType" } ], + "title": "Link Type", "description": "The type of the linked relation.\nMUST be one of these values: 'child', 'root', 'external', 'providers'." }, "aggregate": { - "title": "Aggregate", "allOf": [ { "$ref": "#/components/schemas/Aggregate" } ], + "title": "Aggregate", "description": "A string indicating whether a client that is following links to aggregate results from different OPTIMADE implementations should follow this link or not.\nThis flag SHOULD NOT be indicated for links where `link_type` is not `child`.\n\nIf not specified, clients MAY assume that the value is `ok`.\nIf specified, and the value is anything different than `ok`, the client MUST assume that the server is suggesting not to follow the link during aggregation by default (also if the value is not among the known ones, in case a future specification adds new accepted values).\n\nSpecific values indicate the reason why the server is providing the suggestion.\nA client MAY follow the link anyway if it has reason to do so (e.g., if the client is looking for all test databases, it MAY follow the links marked with `aggregate`=`test`).\n\nIf specified, it MUST be one of the values listed in section Link Aggregate Options.", "default": "ok" }, "no_aggregate_reason": { - "title": "No Aggregate Reason", "type": "string", + "title": "No Aggregate Reason", "description": "An OPTIONAL human-readable string indicating the reason for suggesting not to aggregate results following the link.\nIt SHOULD NOT be present if `aggregate`=`ok`." } }, + "type": "object", + "required": [ + "name", + "description", + "base_url", + "homepage", + "link_type" + ], + "title": "LinksResourceAttributes", "description": "Links endpoint resource object attributes" }, "LinksResponse": { - "title": "LinksResponse", - "required": [ - "data", - "meta" - ], - "type": "object", "properties": { "data": { - "title": "Data", - "uniqueItems": true, "anyOf": [ { - "type": "array", "items": { "$ref": "#/components/schemas/LinksResource" - } + }, + "type": "array" }, { - "type": "array", "items": { "type": "object" - } + }, + "type": "array" } ], + "uniqueItems": true, + "title": "Data", "description": "List of unique OPTIMADE links resource objects." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/ResponseMeta" } ], + "title": "Meta", "description": "A meta object containing non-standard information" }, "errors": { - "title": "Errors", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/Error" }, + "type": "array", + "uniqueItems": true, + "title": "Errors", "description": "A list of unique errors" }, "included": { - "title": "Included", - "uniqueItems": true, "anyOf": [ { - "type": "array", "items": { "$ref": "#/components/schemas/EntryResource" - } + }, + "type": "array" }, { - "type": "array", "items": { "type": "object" - } + }, + "type": "array" } - ] + ], + "uniqueItems": true, + "title": "Included" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ToplevelLinks" } ], + "title": "Links", "description": "Links associated with the primary data or errors" }, "jsonapi": { - "title": "Jsonapi", "allOf": [ { "$ref": "#/components/schemas/JsonApi" } ], + "title": "Jsonapi", "description": "Information about the JSON API used" } }, + "type": "object", + "required": [ + "data", + "meta" + ], + "title": "LinksResponse", "description": "errors are not allowed" }, "Meta": { - "title": "Meta", - "type": "object", "properties": {}, + "type": "object", + "title": "Meta", "description": "Non-standard meta-information that can not be represented as an attribute or relationship." }, "OptimadeError": { - "title": "OptimadeError", - "required": [ - "detail" - ], - "type": "object", "properties": { "id": { - "title": "Id", "type": "string", + "title": "Id", "description": "A unique identifier for this particular occurrence of the problem." }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ErrorLinks" } ], + "title": "Links", "description": "A links object storing about" }, "status": { - "title": "Status", "type": "string", + "title": "Status", "description": "the HTTP status code applicable to this problem, expressed as a string value." }, "code": { - "title": "Code", "type": "string", + "title": "Code", "description": "an application-specific error code, expressed as a string value." }, "title": { - "title": "Title", "type": "string", + "title": "Title", "description": "A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization." }, "detail": { - "title": "Detail", "type": "string", + "title": "Detail", "description": "A human-readable explanation specific to this occurrence of the problem." }, "source": { - "title": "Source", "allOf": [ { "$ref": "#/components/schemas/ErrorSource" } ], + "title": "Source", "description": "An object containing references to the source of the error" }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "a meta object containing non-standard meta-information about the error." } }, + "type": "object", + "required": [ + "detail" + ], + "title": "OptimadeError", "description": "detail MUST be present" }, "Periodicity": { - "title": "Periodicity", + "type": "integer", "enum": [ 0, 1 ], - "type": "integer", + "title": "Periodicity", "description": "Integer enumeration of dimension_types values" }, "Person": { - "title": "Person", - "required": [ - "name" - ], - "type": "object", "properties": { "name": { - "title": "Name", "type": "string", + "title": "Name", "description": "Full name of the person, REQUIRED.", "x-optimade-support": "must", "x-optimade-queryable": "optional" }, "firstname": { - "title": "Firstname", "type": "string", + "title": "Firstname", "description": "First name of the person.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "lastname": { - "title": "Lastname", "type": "string", + "title": "Lastname", "description": "Last name of the person.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" } }, + "type": "object", + "required": [ + "name" + ], + "title": "Person", "description": "A person, i.e., an author, editor or other." }, "Provider": { - "title": "Provider", - "required": [ - "name", - "description", - "prefix" - ], - "type": "object", "properties": { "name": { - "title": "Name", "type": "string", + "title": "Name", "description": "a short name for the database provider" }, "description": { - "title": "Description", "type": "string", + "title": "Description", "description": "a longer description of the database provider" }, "prefix": { - "title": "Prefix", - "pattern": "^[a-z]([a-z]|[0-9]|_)*$", "type": "string", + "pattern": "^[a-z]([a-z]|[0-9]|_)*$", + "title": "Prefix", "description": "database-provider-specific prefix as found in section Database-Provider-Specific Namespace Prefixes." }, "homepage": { - "title": "Homepage", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Homepage", "description": "a [JSON API links object](http://jsonapi.org/format/1.0#document-links) pointing to homepage of the database provider, either directly as a string, or as a link object." } }, + "type": "object", + "required": [ + "name", + "description", + "prefix" + ], + "title": "Provider", "description": "Information on the database provider of the implementation." }, "ReferenceRelationship": { - "title": "ReferenceRelationship", - "type": "object", "properties": { "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/RelationshipLinks" } ], + "title": "Links", "description": "a links object containing at least one of the following: self, related" }, "data": { - "title": "Data", - "uniqueItems": true, "anyOf": [ { "$ref": "#/components/schemas/BaseRelationshipResource" }, { - "type": "array", "items": { "$ref": "#/components/schemas/BaseRelationshipResource" - } + }, + "type": "array" } ], + "uniqueItems": true, + "title": "Data", "description": "Resource linkage" }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "a meta object that contains non-standard meta-information about the relationship." } }, + "type": "object", + "title": "ReferenceRelationship", "description": "Similar to normal JSON API relationship, but with addition of OPTIONAL meta field for a resource." }, "ReferenceResource": { - "title": "ReferenceResource", - "required": [ - "id", - "type", - "attributes" - ], - "type": "object", "properties": { "id": { - "title": "Id", "type": "string", + "title": "Id", "description": "An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`", "x-optimade-support": "must", "x-optimade-queryable": "must" }, "type": { - "title": "Type", - "pattern": "^references$", "type": "string", + "pattern": "^references$", + "title": "Type", "description": "The name of the type of an entry.\n- **Type**: string.\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type and ID MUST be returned in response to a request for `//` under the versioned base URL.\n- **Example**: `\"structures\"`", "default": "references", "x-optimade-support": "must", "x-optimade-queryable": "must" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ResourceLinks" } ], + "title": "Links", "description": "a links object containing links related to the resource." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/EntryMetadata" } ], + "title": "Meta", "description": "A dictionary, containing entry and property-specific metadata for a given entry." }, "attributes": { "$ref": "#/components/schemas/ReferenceResourceAttributes" }, "relationships": { - "title": "Relationships", "allOf": [ { "$ref": "#/components/schemas/EntryRelationships" } ], + "title": "Relationships", "description": "A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object." } }, + "type": "object", + "required": [ + "id", + "type", + "attributes" + ], + "title": "ReferenceResource", "description": "The `references` entries describe bibliographic references.\n\nThe following properties are used to provide the bibliographic details:\n\n- **address**, **annote**, **booktitle**, **chapter**, **crossref**, **edition**, **howpublished**, **institution**, **journal**, **key**, **month**, **note**, **number**, **organization**, **pages**, **publisher**, **school**, **series**, **title**, **volume**, **year**: meanings of these properties match the [BibTeX specification](http://bibtexml.sourceforge.net/btxdoc.pdf), values are strings;\n- **bib_type**: type of the reference, corresponding to **type** property in the BibTeX specification, value is string;\n- **authors** and **editors**: lists of *person objects* which are dictionaries with the following keys:\n - **name**: Full name of the person, REQUIRED.\n - **firstname**, **lastname**: Parts of the person's name, OPTIONAL.\n- **doi** and **url**: values are strings.\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., any of the properties MAY be `null`.\n - **Query**: Support for queries on any of these properties is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - Every references entry MUST contain at least one of the properties." }, "ReferenceResourceAttributes": { - "title": "ReferenceResourceAttributes", - "required": [ - "last_modified" - ], - "type": "object", "properties": { "immutable_id": { - "title": "Immutable Id", "type": "string", + "title": "Immutable Id", "description": "The entry's immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)", "x-optimade-support": "optional", "x-optimade-queryable": "must" }, "last_modified": { - "title": "Last Modified", "type": "string", - "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "format": "date-time", + "title": "Last Modified", + "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "x-optimade-support": "should", "x-optimade-queryable": "must" }, "authors": { - "title": "Authors", - "type": "array", "items": { "$ref": "#/components/schemas/Person" }, + "type": "array", + "title": "Authors", "description": "List of person objects containing the authors of the reference.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "editors": { - "title": "Editors", - "type": "array", "items": { "$ref": "#/components/schemas/Person" }, + "type": "array", + "title": "Editors", "description": "List of person objects containing the editors of the reference.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "doi": { - "title": "Doi", "type": "string", + "title": "Doi", "description": "The digital object identifier of the reference.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "url": { - "title": "Url", + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", - "description": "The URL of the reference.", "format": "uri", + "title": "Url", + "description": "The URL of the reference.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "address": { - "title": "Address", "type": "string", + "title": "Address", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "annote": { - "title": "Annote", "type": "string", + "title": "Annote", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "booktitle": { - "title": "Booktitle", "type": "string", + "title": "Booktitle", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "chapter": { - "title": "Chapter", "type": "string", + "title": "Chapter", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "crossref": { - "title": "Crossref", "type": "string", + "title": "Crossref", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "edition": { - "title": "Edition", "type": "string", + "title": "Edition", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "howpublished": { - "title": "Howpublished", "type": "string", + "title": "Howpublished", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "institution": { - "title": "Institution", "type": "string", + "title": "Institution", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "journal": { - "title": "Journal", "type": "string", + "title": "Journal", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "key": { - "title": "Key", "type": "string", + "title": "Key", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "month": { - "title": "Month", "type": "string", + "title": "Month", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "note": { - "title": "Note", "type": "string", + "title": "Note", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "number": { - "title": "Number", "type": "string", + "title": "Number", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "organization": { - "title": "Organization", "type": "string", + "title": "Organization", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "pages": { - "title": "Pages", "type": "string", + "title": "Pages", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "publisher": { - "title": "Publisher", "type": "string", + "title": "Publisher", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "school": { - "title": "School", "type": "string", + "title": "School", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "series": { - "title": "Series", "type": "string", + "title": "Series", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "title": { - "title": "Title", "type": "string", + "title": "Title", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "bib_type": { - "title": "Bib Type", "type": "string", + "title": "Bib Type", "description": "Type of the reference, corresponding to the **type** property in the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "volume": { - "title": "Volume", "type": "string", + "title": "Volume", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "year": { - "title": "Year", "type": "string", + "title": "Year", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" } }, + "type": "object", + "required": [ + "last_modified" + ], + "title": "ReferenceResourceAttributes", "description": "Model that stores the attributes of a reference.\n\nMany properties match the meaning described in the\n[BibTeX specification](http://bibtexml.sourceforge.net/btxdoc.pdf)." }, "ReferenceResponseMany": { - "title": "ReferenceResponseMany", - "required": [ - "data", - "meta" - ], - "type": "object", "properties": { "data": { - "title": "Data", - "uniqueItems": true, "anyOf": [ { - "type": "array", "items": { "$ref": "#/components/schemas/ReferenceResource" - } + }, + "type": "array" }, { - "type": "array", "items": { "type": "object" - } + }, + "type": "array" } ], + "uniqueItems": true, + "title": "Data", "description": "List of unique OPTIMADE references entry resource objects." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/ResponseMeta" } ], + "title": "Meta", "description": "A meta object containing non-standard information" }, "errors": { - "title": "Errors", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/Error" }, + "type": "array", + "uniqueItems": true, + "title": "Errors", "description": "A list of unique errors" }, "included": { - "title": "Included", - "uniqueItems": true, "anyOf": [ { - "type": "array", "items": { "$ref": "#/components/schemas/EntryResource" - } + }, + "type": "array" }, { - "type": "array", "items": { "type": "object" - } + }, + "type": "array" } - ] + ], + "uniqueItems": true, + "title": "Included" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ToplevelLinks" } ], + "title": "Links", "description": "Links associated with the primary data or errors" }, "jsonapi": { - "title": "Jsonapi", "allOf": [ { "$ref": "#/components/schemas/JsonApi" } ], + "title": "Jsonapi", "description": "Information about the JSON API used" } }, - "description": "errors are not allowed" - }, - "ReferenceResponseOne": { - "title": "ReferenceResponseOne", + "type": "object", "required": [ "data", "meta" ], - "type": "object", + "title": "ReferenceResponseMany", + "description": "errors are not allowed" + }, + "ReferenceResponseOne": { "properties": { "data": { - "title": "Data", "anyOf": [ { "$ref": "#/components/schemas/ReferenceResource" @@ -3014,209 +3007,209 @@ "type": "object" } ], + "title": "Data", "description": "A single references entry resource." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/ResponseMeta" } ], + "title": "Meta", "description": "A meta object containing non-standard information" }, "errors": { - "title": "Errors", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/Error" }, + "type": "array", + "uniqueItems": true, + "title": "Errors", "description": "A list of unique errors" }, "included": { - "title": "Included", - "uniqueItems": true, "anyOf": [ { - "type": "array", "items": { "$ref": "#/components/schemas/EntryResource" - } + }, + "type": "array" }, { - "type": "array", "items": { "type": "object" - } + }, + "type": "array" } - ] + ], + "uniqueItems": true, + "title": "Included" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ToplevelLinks" } ], + "title": "Links", "description": "Links associated with the primary data or errors" }, "jsonapi": { - "title": "Jsonapi", "allOf": [ { "$ref": "#/components/schemas/JsonApi" } ], + "title": "Jsonapi", "description": "Information about the JSON API used" } }, + "type": "object", + "required": [ + "data", + "meta" + ], + "title": "ReferenceResponseOne", "description": "errors are not allowed" }, "RelationshipLinks": { - "title": "RelationshipLinks", - "type": "object", "properties": { "self": { - "title": "Self", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Self", "description": "A link for the relationship itself (a 'relationship link').\nThis link allows the client to directly manipulate the relationship.\nWhen fetched successfully, this link returns the [linkage](https://jsonapi.org/format/1.0/#document-resource-object-linkage) for the related resources as its primary data.\n(See [Fetching Relationships](https://jsonapi.org/format/1.0/#fetching-relationships).)" }, "related": { - "title": "Related", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Related", "description": "A [related resource link](https://jsonapi.org/format/1.0/#document-resource-object-related-resource-links)." } }, + "type": "object", + "title": "RelationshipLinks", "description": "A resource object **MAY** contain references to other resource objects (\"relationships\").\nRelationships may be to-one or to-many.\nRelationships can be specified by including a member in a resource's links object." }, "Relationships": { - "title": "Relationships", - "type": "object", "properties": {}, + "type": "object", + "title": "Relationships", "description": "Members of the relationships object (\"relationships\") represent references from the resource object in which it's defined to other resource objects.\nKeys MUST NOT be:\n type\n id" }, "Resource": { - "title": "Resource", - "required": [ - "id", - "type" - ], - "type": "object", "properties": { "id": { - "title": "Id", "type": "string", + "title": "Id", "description": "Resource ID" }, "type": { - "title": "Type", "type": "string", + "title": "Type", "description": "Resource type" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ResourceLinks" } ], + "title": "Links", "description": "a links object containing links related to the resource." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship." }, "attributes": { - "title": "Attributes", "allOf": [ { "$ref": "#/components/schemas/Attributes" } ], + "title": "Attributes", "description": "an attributes object representing some of the resource\u2019s data." }, "relationships": { - "title": "Relationships", "allOf": [ { "$ref": "#/components/schemas/Relationships" } ], + "title": "Relationships", "description": "[Relationships object](https://jsonapi.org/format/1.0/#document-resource-object-relationships)\ndescribing relationships between the resource and other JSON API resources." } }, + "type": "object", + "required": [ + "id", + "type" + ], + "title": "Resource", "description": "Resource objects appear in a JSON API document to represent resources." }, "ResourceLinks": { - "title": "ResourceLinks", - "type": "object", "properties": { "self": { - "title": "Self", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Self", "description": "A link that identifies the resource represented by the resource object." } }, + "type": "object", + "title": "ResourceLinks", "description": "A Resource Links object" }, "ResponseMeta": { - "title": "ResponseMeta", - "required": [ - "query", - "api_version", - "more_data_available" - ], - "type": "object", "properties": { "query": { - "title": "Query", "allOf": [ { "$ref": "#/components/schemas/ResponseMetaQuery" } ], + "title": "Query", "description": "Information on the Query that was requested" }, "api_version": { - "title": "Api Version", - "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", "type": "string", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", + "title": "Api Version", "description": "Presently used full version of the OPTIMADE API.\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\nExamples: `1.0.0`, `1.0.0-rc.2`.", "example": [ "0.10.1", @@ -3225,588 +3218,588 @@ ] }, "more_data_available": { - "title": "More Data Available", "type": "boolean", + "title": "More Data Available", "description": "`false` if the response contains all data for the request (e.g., a request issued to a single entry endpoint, or a `filter` query at the last page of a paginated response) and `true` if the response is incomplete in the sense that multiple objects match the request, and not all of them have been included in the response (e.g., a query with multiple pages that is not at the last page)." }, "schema": { - "title": "Schema", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Schema", "description": "A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) that points to a schema for the response.\nIf it is a string, or a dictionary containing no `meta` field, the provided URL MUST point at an [OpenAPI](https://swagger.io/specification/) schema.\nIt is possible that future versions of this specification allows for alternative schema types.\nHence, if the `meta` field of the JSON API links object is provided and contains a field `schema_type` that is not equal to the string `OpenAPI` the client MUST not handle failures to parse the schema or to validate the response against the schema as errors." }, "time_stamp": { - "title": "Time Stamp", "type": "string", - "description": "A timestamp containing the date and time at which the query was executed.", - "format": "date-time" + "format": "date-time", + "title": "Time Stamp", + "description": "A timestamp containing the date and time at which the query was executed." }, "data_returned": { - "title": "Data Returned", - "minimum": 0.0, "type": "integer", + "minimum": 0.0, + "title": "Data Returned", "description": "An integer containing the total number of data resource objects returned for the current `filter` query, independent of pagination." }, "provider": { - "title": "Provider", "allOf": [ { "$ref": "#/components/schemas/Provider" } ], + "title": "Provider", "description": "information on the database provider of the implementation." }, "data_available": { - "title": "Data Available", "type": "integer", + "title": "Data Available", "description": "An integer containing the total number of data resource objects available in the database for the endpoint." }, "last_id": { - "title": "Last Id", "type": "string", + "title": "Last Id", "description": "a string containing the last ID returned" }, "response_message": { - "title": "Response Message", "type": "string", + "title": "Response Message", "description": "response string from the server" }, "implementation": { - "title": "Implementation", "allOf": [ { "$ref": "#/components/schemas/Implementation" } ], + "title": "Implementation", "description": "a dictionary describing the server implementation" }, "warnings": { - "title": "Warnings", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/Warnings" }, + "type": "array", + "uniqueItems": true, + "title": "Warnings", "description": "A list of warning resource objects representing non-critical errors or warnings.\nA warning resource object is defined similarly to a [JSON API error object](http://jsonapi.org/format/1.0/#error-objects), but MUST also include the field `type`, which MUST have the value `\"warning\"`.\nThe field `detail` MUST be present and SHOULD contain a non-critical message, e.g., reporting unrecognized search attributes or deprecated features.\nThe field `status`, representing a HTTP response status code, MUST NOT be present for a warning resource object.\nThis is an exclusive field for error resource objects." } }, + "type": "object", + "required": [ + "query", + "api_version", + "more_data_available" + ], + "title": "ResponseMeta", "description": "A [JSON API meta member](https://jsonapi.org/format/1.0#document-meta)\nthat contains JSON API meta objects of non-standard\nmeta-information.\n\nOPTIONAL additional information global to the query that is not\nspecified in this document, MUST start with a\ndatabase-provider-specific prefix." }, "ResponseMetaQuery": { - "title": "ResponseMetaQuery", - "required": [ - "representation" - ], - "type": "object", "properties": { "representation": { - "title": "Representation", "type": "string", + "title": "Representation", "description": "A string with the part of the URL following the versioned or unversioned base URL that serves the API.\nQuery parameters that have not been used in processing the request MAY be omitted.\nIn particular, if no query parameters have been involved in processing the request, the query part of the URL MAY be excluded.\nExample: `/structures?filter=nelements=2`" } }, + "type": "object", + "required": [ + "representation" + ], + "title": "ResponseMetaQuery", "description": "Information on the query that was requested." }, "Species": { - "title": "Species", - "required": [ - "name", - "chemical_symbols", - "concentration" - ], - "type": "object", "properties": { "name": { - "title": "Name", "type": "string", + "title": "Name", "description": "Gives the name of the species; the **name** value MUST be unique in the `species` list.", "x-optimade-support": "must", "x-optimade-queryable": "optional" }, "chemical_symbols": { - "title": "Chemical Symbols", - "type": "array", "items": { "type": "string" }, + "type": "array", + "title": "Chemical Symbols", "description": "MUST be a list of strings of all chemical elements composing this species. Each item of the list MUST be one of the following:\n\n- a valid chemical-element symbol, or\n- the special value `\"X\"` to represent a non-chemical element, or\n- the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\n\nIf any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.", "x-optimade-support": "must", "x-optimade-queryable": "optional" }, "concentration": { - "title": "Concentration", - "type": "array", "items": { "type": "number" }, + "type": "array", + "title": "Concentration", "description": "MUST be a list of floats, with same length as `chemical_symbols`. The numbers represent the relative concentration of the corresponding chemical symbol in this species. The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\n\n- Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\n- Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\n\nNote that concentrations are uncorrelated between different site (even of the same species).", "x-optimade-support": "must", "x-optimade-queryable": "optional" }, "mass": { - "title": "Mass", - "type": "array", "items": { "type": "number" }, + "type": "array", + "title": "Mass", "description": "If present MUST be a list of floats expressed in a.m.u.\nElements denoting vacancies MUST have masses equal to 0.", + "x-optimade-unit": "a.m.u.", "x-optimade-support": "optional", - "x-optimade-queryable": "optional", - "x-optimade-unit": "a.m.u." + "x-optimade-queryable": "optional" }, "original_name": { - "title": "Original Name", "type": "string", + "title": "Original Name", "description": "Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\n\nNote: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "attached": { - "title": "Attached", - "type": "array", "items": { "type": "string" }, + "type": "array", + "title": "Attached", "description": "If provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "nattached": { - "title": "Nattached", - "type": "array", "items": { "type": "integer" }, + "type": "array", + "title": "Nattached", "description": "If provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the :field:`attached` key.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" } }, + "type": "object", + "required": [ + "name", + "chemical_symbols", + "concentration" + ], + "title": "Species", "description": "A list describing the species of the sites of this structure.\n\nSpecies can represent pure chemical elements, virtual-crystal atoms representing a\nstatistical occupation of a given site by multiple chemical elements, and/or a\nlocation to which there are attached atoms, i.e., atoms whose precise location are\nunknown beyond that they are attached to that position (frequently used to indicate\nhydrogen atoms attached to another element, e.g., a carbon with three attached\nhydrogens might represent a methyl group, -CH3).\n\n- **Examples**:\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]`: any site with this species is occupied by a Ti atom.\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]`: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.\n - `[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]`: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.\n - `[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]`: any site with this species is occupied by a carbon isotope with mass 12.\n - `[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]`: any site with this species is occupied by a carbon isotope with mass 13.\n - `[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]`: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms." }, "StructureFeatures": { - "title": "StructureFeatures", "enum": [ "disorder", "implicit_atoms", "site_attachments", "assemblies" ], + "title": "StructureFeatures", "description": "Enumeration of structure_features values" }, "StructureRelationship": { - "title": "StructureRelationship", - "type": "object", "properties": { "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/RelationshipLinks" } ], + "title": "Links", "description": "a links object containing at least one of the following: self, related" }, "data": { - "title": "Data", - "uniqueItems": true, "anyOf": [ { "$ref": "#/components/schemas/BaseRelationshipResource" }, { - "type": "array", "items": { "$ref": "#/components/schemas/BaseRelationshipResource" - } + }, + "type": "array" } ], + "uniqueItems": true, + "title": "Data", "description": "Resource linkage" }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "a meta object that contains non-standard meta-information about the relationship." } }, + "type": "object", + "title": "StructureRelationship", "description": "Similar to normal JSON API relationship, but with addition of OPTIONAL meta field for a resource." }, "StructureResource": { - "title": "StructureResource", - "required": [ - "id", - "type", - "attributes" - ], - "type": "object", "properties": { "id": { - "title": "Id", "type": "string", + "title": "Id", "description": "An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`", "x-optimade-support": "must", "x-optimade-queryable": "must" }, "type": { - "title": "Type", - "pattern": "^structures$", "type": "string", + "pattern": "^structures$", + "title": "Type", "description": "The name of the type of an entry.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type `` and ID `` MUST be returned in response to a request for `//` under the versioned base URL.\n\n- **Examples**:\n - `\"structures\"`", "default": "structures", "x-optimade-support": "must", "x-optimade-queryable": "must" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ResourceLinks" } ], + "title": "Links", "description": "a links object containing links related to the resource." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/EntryMetadata" } ], + "title": "Meta", "description": "A dictionary, containing entry and property-specific metadata for a given entry." }, "attributes": { "$ref": "#/components/schemas/StructureResourceAttributes" }, "relationships": { - "title": "Relationships", "allOf": [ { "$ref": "#/components/schemas/EntryRelationships" } ], + "title": "Relationships", "description": "A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object." } }, + "type": "object", + "required": [ + "id", + "type", + "attributes" + ], + "title": "StructureResource", "description": "Representing a structure." }, "StructureResourceAttributes": { - "title": "StructureResourceAttributes", - "required": [ - "last_modified", - "elements", - "nelements", - "elements_ratios", - "chemical_formula_descriptive", - "chemical_formula_reduced", - "chemical_formula_anonymous", - "dimension_types", - "nperiodic_dimensions", - "lattice_vectors", - "cartesian_site_positions", - "nsites", - "species", - "species_at_sites", - "structure_features" - ], - "type": "object", "properties": { "immutable_id": { - "title": "Immutable Id", "type": "string", + "title": "Immutable Id", "description": "The entry's immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)", "x-optimade-support": "optional", "x-optimade-queryable": "must" }, "last_modified": { - "title": "Last Modified", "type": "string", - "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "format": "date-time", - "nullable": true, + "title": "Last Modified", + "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "x-optimade-support": "should", + "nullable": true, "x-optimade-queryable": "must" }, "elements": { - "title": "Elements", - "type": "array", "items": { "type": "string" }, + "type": "array", + "title": "Elements", "description": "The chemical symbols of the different elements present in the structure.\n\n- **Type**: list of strings.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The strings are the chemical symbols, i.e., either a single uppercase letter or an uppercase letter followed by a number of lowercase letters.\n - The order MUST be alphabetical.\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements_ratios`, if the latter is provided.\n - Note: This property SHOULD NOT contain the string \"X\" to indicate non-chemical elements or \"vacancy\" to indicate vacancies (in contrast to the field `chemical_symbols` for the `species` property).\n\n- **Examples**:\n - `[\"Si\"]`\n - `[\"Al\",\"O\",\"Si\"]`\n\n- **Query examples**:\n - A filter that matches all records of structures that contain Si, Al **and** O, and possibly other elements: `elements HAS ALL \"Si\", \"Al\", \"O\"`.\n - To match structures with exactly these three elements, use `elements HAS ALL \"Si\", \"Al\", \"O\" AND elements LENGTH 3`.\n - Note: length queries on this property can be equivalently formulated by filtering on the `nelements`_ property directly.", - "nullable": true, "x-optimade-support": "should", + "nullable": true, "x-optimade-queryable": "must" }, "nelements": { - "title": "Nelements", "type": "integer", + "title": "Nelements", "description": "Number of different elements in the structure as an integer.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - MUST be equal to the lengths of the list properties `elements` and `elements_ratios`, if they are provided.\n\n- **Examples**:\n - `3`\n\n- **Querying**:\n - Note: queries on this property can equivalently be formulated using `elements LENGTH`.\n - A filter that matches structures that have exactly 4 elements: `nelements=4`.\n - A filter that matches structures that have between 2 and 7 elements: `nelements>=2 AND nelements<=7`.", - "nullable": true, "x-optimade-support": "should", + "nullable": true, "x-optimade-queryable": "must" }, "elements_ratios": { - "title": "Elements Ratios", - "type": "array", "items": { "type": "number" }, + "type": "array", + "title": "Elements Ratios", "description": "Relative proportions of different elements in the structure.\n\n- **Type**: list of floats\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - Composed by the proportions of elements in the structure as a list of floating point numbers.\n - The sum of the numbers MUST be 1.0 (within floating point accuracy)\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements`, if the latter is provided.\n\n- **Examples**:\n - `[1.0]`\n - `[0.3333333333333333, 0.2222222222222222, 0.4444444444444444]`\n\n- **Query examples**:\n - Note: Useful filters can be formulated using the set operator syntax for correlated values.\n However, since the values are floating point values, the use of equality comparisons is generally inadvisable.\n - OPTIONAL: a filter that matches structures where approximately 1/3 of the atoms in the structure are the element Al is: `elements:elements_ratios HAS ALL \"Al\":>0.3333, \"Al\":<0.3334`.", - "nullable": true, "x-optimade-support": "should", + "nullable": true, "x-optimade-queryable": "must" }, "chemical_formula_descriptive": { - "title": "Chemical Formula Descriptive", "type": "string", + "title": "Chemical Formula Descriptive", "description": "The chemical formula for a structure as a string in a form chosen by the API implementation.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The chemical formula is given as a string consisting of properly capitalized element symbols followed by integers or decimal numbers, balanced parentheses, square, and curly brackets `(`,`)`, `[`,`]`, `{`, `}`, commas, the `+`, `-`, `:` and `=` symbols. The parentheses are allowed to be followed by a number. Spaces are allowed anywhere except within chemical symbols. The order of elements and any groupings indicated by parentheses or brackets are chosen freely by the API implementation.\n - The string SHOULD be arithmetically consistent with the element ratios in the `chemical_formula_reduced` property.\n - It is RECOMMENDED, but not mandatory, that symbols, parentheses and brackets, if used, are used with the meanings prescribed by [IUPAC's Nomenclature of Organic Chemistry](https://www.qmul.ac.uk/sbcs/iupac/bibliog/blue.html).\n\n- **Examples**:\n - `\"(H2O)2 Na\"`\n - `\"NaCl\"`\n - `\"CaCO3\"`\n - `\"CCaO3\"`\n - `\"(CH3)3N+ - [CH2]2-OH = Me3N+ - CH2 - CH2OH\"`\n\n- **Query examples**:\n - Note: the free-form nature of this property is likely to make queries on it across different databases inconsistent.\n - A filter that matches an exactly given formula: `chemical_formula_descriptive=\"(H2O)2 Na\"`.\n - A filter that does a partial match: `chemical_formula_descriptive CONTAINS \"H2O\"`.", - "nullable": true, "x-optimade-support": "should", + "nullable": true, "x-optimade-queryable": "must" }, "chemical_formula_reduced": { - "title": "Chemical Formula Reduced", - "pattern": "(^$)|^([A-Z][a-z]?([2-9]|[1-9]\\d+)?)+$", "type": "string", + "pattern": "(^$)|^([A-Z][a-z]?([2-9]|[1-9]\\d+)?)+$", + "title": "Chemical Formula Reduced", "description": "The reduced chemical formula for a structure as a string with element symbols and integer chemical proportion numbers.\nThe proportion number MUST be omitted if it is 1.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property.\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\n Intricate queries on formula components are instead suggested to be formulated using set-type filter operators on the multi valued `elements` and `elements_ratios` properties.\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\n - Elements MUST be placed in alphabetical order, followed by their integer chemical proportion number.\n - For structures with no partial occupation, the chemical proportion numbers are the smallest integers for which the chemical proportion is exactly correct.\n - For structures with partial occupation, the chemical proportion numbers are integers that within reasonable approximation indicate the correct chemical proportions. The precise details of how to perform the rounding is chosen by the API implementation.\n - No spaces or separators are allowed.\n\n- **Examples**:\n - `\"H2NaO\"`\n - `\"ClNa\"`\n - `\"CCaO3\"`\n\n- **Query examples**:\n - A filter that matches an exactly given formula is `chemical_formula_reduced=\"H2NaO\"`.", - "nullable": true, "x-optimade-support": "should", + "nullable": true, "x-optimade-queryable": "must" }, "chemical_formula_hill": { - "title": "Chemical Formula Hill", - "pattern": "(^$)|^([A-Z][a-z]?([2-9]|[1-9]\\d+)?)+$", "type": "string", + "pattern": "(^$)|^([A-Z][a-z]?([2-9]|[1-9]\\d+)?)+$", + "title": "Chemical Formula Hill", "description": "The chemical formula for a structure in [Hill form](https://dx.doi.org/10.1021/ja02046a005) with element symbols followed by integer chemical proportion numbers. The proportion number MUST be omitted if it is 1.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, only a subset of the filter features MAY be supported.\n - The overall scale factor of the chemical proportions is chosen such that the resulting values are integers that indicate the most chemically relevant unit of which the system is composed.\n For example, if the structure is a repeating unit cell with four hydrogens and four oxygens that represents two hydroperoxide molecules, `chemical_formula_hill` is `\"H2O2\"` (i.e., not `\"HO\"`, nor `\"H4O4\"`).\n - If the chemical insight needed to ascribe a Hill formula to the system is not present, the property MUST be handled as unset.\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\n - Elements MUST be placed in [Hill order](https://dx.doi.org/10.1021/ja02046a005), followed by their integer chemical proportion number.\n Hill order means: if carbon is present, it is placed first, and if also present, hydrogen is placed second.\n After that, all other elements are ordered alphabetically.\n If carbon is not present, all elements are ordered alphabetically.\n - If the system has sites with partial occupation and the total occupations of each element do not all sum up to integers, then the Hill formula SHOULD be handled as unset.\n - No spaces or separators are allowed.\n\n- **Examples**:\n - `\"H2O2\"`\n\n- **Query examples**:\n - A filter that matches an exactly given formula is `chemical_formula_hill=\"H2O2\"`.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "chemical_formula_anonymous": { - "title": "Chemical Formula Anonymous", - "pattern": "(^$)|^([A-Z][a-z]?([2-9]|[1-9]\\d+)?)+$", "type": "string", + "pattern": "(^$)|^([A-Z][a-z]?([2-9]|[1-9]\\d+)?)+$", + "title": "Chemical Formula Anonymous", "description": "The anonymous formula is the `chemical_formula_reduced`, but where the elements are instead first ordered by their chemical proportion number, and then, in order left to right, replaced by anonymous symbols A, B, C, ..., Z, Aa, Ba, ..., Za, Ab, Bb, ... and so on.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property.\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\n\n- **Examples**:\n - `\"A2B\"`\n - `\"A42B42C16D12E10F9G5\"`\n\n- **Querying**:\n - A filter that matches an exactly given formula is `chemical_formula_anonymous=\"A2B\"`.", - "nullable": true, "x-optimade-support": "should", + "nullable": true, "x-optimade-queryable": "must" }, "dimension_types": { - "title": "Dimension Types", - "maxItems": 3, - "minItems": 3, - "type": "array", "items": { "$ref": "#/components/schemas/Periodicity" }, + "type": "array", + "maxItems": 3, + "minItems": 3, + "title": "Dimension Types", "description": "List of three integers.\nFor each of the three directions indicated by the three lattice vectors (see property `lattice_vectors`), this list indicates if the direction is periodic (value `1`) or non-periodic (value `0`).\nNote: the elements in this list each refer to the direction of the corresponding entry in `lattice_vectors` and *not* the Cartesian x, y, z directions.\n\n- **Type**: list of integers.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n - MUST be a list of length 3.\n - Each integer element MUST assume only the value 0 or 1.\n\n- **Examples**:\n - For a molecule: `[0, 0, 0]`\n - For a wire along the direction specified by the third lattice vector: `[0, 0, 1]`\n - For a 2D surface/slab, periodic on the plane defined by the first and third lattice vectors: `[1, 0, 1]`\n - For a bulk 3D system: `[1, 1, 1]`", - "nullable": true, "x-optimade-support": "should", + "nullable": true, "x-optimade-queryable": "optional" }, "nperiodic_dimensions": { - "title": "Nperiodic Dimensions", "type": "integer", + "title": "Nperiodic Dimensions", "description": "An integer specifying the number of periodic dimensions in the structure, equivalent to the number of non-zero entries in `dimension_types`.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The integer value MUST be between 0 and 3 inclusive and MUST be equal to the sum of the items in the `dimension_types` property.\n - This property only reflects the treatment of the lattice vectors provided for the structure, and not any physical interpretation of the dimensionality of its contents.\n\n- **Examples**:\n - `2` should be indicated in cases where `dimension_types` is any of `[1, 1, 0]`, `[1, 0, 1]`, `[0, 1, 1]`.\n\n- **Query examples**:\n - Match only structures with exactly 3 periodic dimensions: `nperiodic_dimensions=3`\n - Match all structures with 2 or fewer periodic dimensions: `nperiodic_dimensions<=2`", - "nullable": true, "x-optimade-support": "should", + "nullable": true, "x-optimade-queryable": "must" }, "lattice_vectors": { - "title": "Lattice Vectors", - "maxItems": 3, - "minItems": 3, - "type": "array", "items": { - "maxItems": 3, - "minItems": 3, - "type": "array", "items": { "type": "number" - } + }, + "type": "array", + "maxItems": 3, + "minItems": 3 }, + "type": "array", + "maxItems": 3, + "minItems": 3, + "title": "Lattice Vectors", "description": "The three lattice vectors in Cartesian coordinates, in \u00e5ngstr\u00f6m (\u00c5).\n\n- **Type**: list of list of floats or unknown values.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - MUST be a list of three vectors *a*, *b*, and *c*, where each of the vectors MUST BE a list of the vector's coordinates along the x, y, and z Cartesian coordinates.\n (Therefore, the first index runs over the three lattice vectors and the second index runs over the x, y, z Cartesian coordinates).\n - For databases that do not define an absolute Cartesian system (e.g., only defining the length and angles between vectors), the first lattice vector SHOULD be set along *x* and the second on the *xy*-plane.\n - MUST always contain three vectors of three coordinates each, independently of the elements of property `dimension_types`.\n The vectors SHOULD by convention be chosen so the determinant of the `lattice_vectors` matrix is different from zero.\n The vectors in the non-periodic directions have no significance beyond fulfilling these requirements.\n - The coordinates of the lattice vectors of non-periodic dimensions (i.e., those dimensions for which `dimension_types` is `0`) MAY be given as a list of all `null` values.\n If a lattice vector contains the value `null`, all coordinates of that lattice vector MUST be `null`.\n\n- **Examples**:\n - `[[4.0,0.0,0.0],[0.0,4.0,0.0],[0.0,1.0,4.0]]` represents a cell, where the first vector is `(4, 0, 0)`, i.e., a vector aligned along the `x` axis of length 4 \u00c5; the second vector is `(0, 4, 0)`; and the third vector is `(0, 1, 4)`.", - "nullable": true, "x-optimade-unit": "\u00c5", "x-optimade-support": "should", + "nullable": true, "x-optimade-queryable": "optional" }, "cartesian_site_positions": { - "title": "Cartesian Site Positions", - "type": "array", "items": { - "maxItems": 3, - "minItems": 3, - "type": "array", "items": { "type": "number" - } + }, + "type": "array", + "maxItems": 3, + "minItems": 3 }, + "type": "array", + "title": "Cartesian Site Positions", "description": "Cartesian positions of each site in the structure.\nA site is usually used to describe positions of atoms; what atoms can be encountered at a given site is conveyed by the `species_at_sites` property, and the species themselves are described in the `species` property.\n\n- **Type**: list of list of floats\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - It MUST be a list of length equal to the number of sites in the structure, where every element is a list of the three Cartesian coordinates of a site expressed as float values in the unit angstrom (\u00c5).\n - An entry MAY have multiple sites at the same Cartesian position (for a relevant use of this, see e.g., the property `assemblies`).\n\n- **Examples**:\n - `[[0,0,0],[0,0,2]]` indicates a structure with two sites, one sitting at the origin and one along the (positive) *z*-axis, 2 \u00c5 away from the origin.", - "nullable": true, "x-optimade-unit": "\u00c5", "x-optimade-support": "should", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "nullable": true }, "nsites": { - "title": "Nsites", "type": "integer", + "title": "Nsites", "description": "An integer specifying the length of the `cartesian_site_positions` property.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `42`\n\n- **Query examples**:\n - Match only structures with exactly 4 sites: `nsites=4`\n - Match structures that have between 2 and 7 sites: `nsites>=2 AND nsites<=7`", - "nullable": true, "x-optimade-support": "should", + "nullable": true, "x-optimade-queryable": "must" }, "species": { - "title": "Species", - "type": "array", "items": { "$ref": "#/components/schemas/Species" }, + "type": "array", + "title": "Species", "description": "A list describing the species of the sites of this structure.\nSpecies can represent pure chemical elements, virtual-crystal atoms representing a statistical occupation of a given site by multiple chemical elements, and/or a location to which there are attached atoms, i.e., atoms whose precise location are unknown beyond that they are attached to that position (frequently used to indicate hydrogen atoms attached to another element, e.g., a carbon with three attached hydrogens might represent a methyl group, -CH3).\n\n- **Type**: list of dictionary with keys:\n - `name`: string (REQUIRED)\n - `chemical_symbols`: list of strings (REQUIRED)\n - `concentration`: list of float (REQUIRED)\n - `attached`: list of strings (REQUIRED)\n - `nattached`: list of integers (OPTIONAL)\n - `mass`: list of floats (OPTIONAL)\n - `original_name`: string (OPTIONAL).\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - Each list member MUST be a dictionary with the following keys:\n - **name**: REQUIRED; gives the name of the species; the **name** value MUST be unique in the `species` list;\n - **chemical_symbols**: REQUIRED; MUST be a list of strings of all chemical elements composing this species.\n Each item of the list MUST be one of the following:\n - a valid chemical-element symbol, or\n - the special value `\"X\"` to represent a non-chemical element, or\n - the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\n\n If any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.\n\n - **concentration**: REQUIRED; MUST be a list of floats, with same length as `chemical_symbols`.\n The numbers represent the relative concentration of the corresponding chemical symbol in this species.\n The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\n\n - Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\n - Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\n\n Note that concentrations are uncorrelated between different sites (even of the same species).\n\n - **attached**: OPTIONAL; if provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.\n\n - **nattached**: OPTIONAL; if provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the `attached` key.\n\n The implementation MUST include either both or none of the `attached` and `nattached` keys, and if they are provided, they MUST be of the same length.\n Furthermore, if they are provided, the `structure_features` property MUST include the string `site_attachments`.\n\n - **mass**: OPTIONAL. If present MUST be a list of floats, with the same length as `chemical_symbols`, providing element masses expressed in a.m.u.\n Elements denoting vacancies MUST have masses equal to 0.\n\n - **original_name**: OPTIONAL. Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\n\n Note: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.\n\n The main use of this field is for source databases that use species names, containing characters that are not allowed (see description of the list property `species_at_sites`).\n\n - For systems that have only species formed by a single chemical symbol, and that have at most one species per chemical symbol, SHOULD use the chemical symbol as species name (e.g., `\"Ti\"` for titanium, `\"O\"` for oxygen, etc.)\n However, note that this is OPTIONAL, and client implementations MUST NOT assume that the key corresponds to a chemical symbol, nor assume that if the species name is a valid chemical symbol, that it represents a species with that chemical symbol.\n This means that a species `{\"name\": \"C\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]}` is valid and represents a titanium species (and *not* a carbon species).\n - It is NOT RECOMMENDED that a structure includes species that do not have at least one corresponding site.\n\n- **Examples**:\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]`: any site with this species is occupied by a Ti atom.\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]`: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.\n - `[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]`: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.\n - `[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]`: any site with this species is occupied by a carbon isotope with mass 12.\n - `[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]`: any site with this species is occupied by a carbon isotope with mass 13.\n - `[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]`: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms.", - "nullable": true, "x-optimade-support": "should", + "nullable": true, "x-optimade-queryable": "optional" }, "species_at_sites": { - "title": "Species At Sites", - "type": "array", "items": { "type": "string" }, + "type": "array", + "title": "Species At Sites", "description": "Name of the species at each site (where values for sites are specified with the same order of the property `cartesian_site_positions`).\nThe properties of the species are found in the property `species`.\n\n- **Type**: list of strings.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - MUST have length equal to the number of sites in the structure (first dimension of the list property `cartesian_site_positions`).\n - Each species name mentioned in the `species_at_sites` list MUST be described in the list property `species` (i.e. for each value in the `species_at_sites` list there MUST exist exactly one dictionary in the `species` list with the `name` attribute equal to the corresponding `species_at_sites` value).\n - Each site MUST be associated only to a single species.\n **Note**: However, species can represent mixtures of atoms, and multiple species MAY be defined for the same chemical element.\n This latter case is useful when different atoms of the same type need to be grouped or distinguished, for instance in simulation codes to assign different initial spin states.\n\n- **Examples**:\n - `[\"Ti\",\"O2\"]` indicates that the first site is hosting a species labeled `\"Ti\"` and the second a species labeled `\"O2\"`.\n - `[\"Ac\", \"Ac\", \"Ag\", \"Ir\"]` indicating the first two sites contains the `\"Ac\"` species, while the third and fourth sites contain the `\"Ag\"` and `\"Ir\"` species, respectively.", - "nullable": true, "x-optimade-support": "should", + "nullable": true, "x-optimade-queryable": "optional" }, "assemblies": { - "title": "Assemblies", - "type": "array", "items": { "$ref": "#/components/schemas/Assembly" }, + "type": "array", + "title": "Assemblies", "description": "A description of groups of sites that are statistically correlated.\n\n- **Type**: list of dictionary with keys:\n - `sites_in_groups`: list of list of integers (REQUIRED)\n - `group_probabilities`: list of floats (REQUIRED)\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - The property SHOULD be `null` for entries that have no partial occupancies.\n - If present, the correct flag MUST be set in the list `structure_features`.\n - Client implementations MUST check its presence (as its presence changes the interpretation of the structure).\n - If present, it MUST be a list of dictionaries, each of which represents an assembly and MUST have the following two keys:\n - **sites_in_groups**: Index of the sites (0-based) that belong to each group for each assembly.\n\n Example: `[[1], [2]]`: two groups, one with the second site, one with the third.\n Example: `[[1,2], [3]]`: one group with the second and third site, one with the fourth.\n\n - **group_probabilities**: Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\n It SHOULD sum to one.\n See below for examples of how to specify the probability of the occurrence of a vacancy.\n The possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.\n\n - If a site is not present in any group, it means that it is present with 100 % probability (as if no assembly was specified).\n - A site MUST NOT appear in more than one group.\n\n- **Examples** (for each entry of the assemblies list):\n - `{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}`: the first site and the second site never occur at the same time in the unit cell.\n Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.\n - `{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}`: the second and third site are either present together or not present; they form the first group of atoms for this assembly.\n The second group is formed by the fourth site.\n Sites of the first group (the second and the third) are never present at the same time as the fourth site.\n 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent).\n\n- **Notes**:\n - Assemblies are essential to represent, for instance, the situation where an atom can statistically occupy two different positions (sites).\n\n - By defining groups, it is possible to represent, e.g., the case where a functional molecule (and not just one atom) is either present or absent (or the case where it it is present in two conformations)\n\n - Considerations on virtual alloys and on vacancies: In the special case of a virtual alloy, these specifications allow two different, equivalent ways of specifying them.\n For instance, for a site at the origin with 30 % probability of being occupied by Si, 50 % probability of being occupied by Ge, and 20 % of being a vacancy, the following two representations are possible:\n\n - Using a single species:\n ```json\n {\n \"cartesian_site_positions\": [[0,0,0]],\n \"species_at_sites\": [\"SiGe-vac\"],\n \"species\": [\n {\n \"name\": \"SiGe-vac\",\n \"chemical_symbols\": [\"Si\", \"Ge\", \"vacancy\"],\n \"concentration\": [0.3, 0.5, 0.2]\n }\n ]\n // ...\n }\n ```\n\n - Using multiple species and the assemblies:\n ```json\n {\n \"cartesian_site_positions\": [ [0,0,0], [0,0,0], [0,0,0] ],\n \"species_at_sites\": [\"Si\", \"Ge\", \"vac\"],\n \"species\": [\n { \"name\": \"Si\", \"chemical_symbols\": [\"Si\"], \"concentration\": [1.0] },\n { \"name\": \"Ge\", \"chemical_symbols\": [\"Ge\"], \"concentration\": [1.0] },\n { \"name\": \"vac\", \"chemical_symbols\": [\"vacancy\"], \"concentration\": [1.0] }\n ],\n \"assemblies\": [\n {\n \"sites_in_groups\": [ [0], [1], [2] ],\n \"group_probabilities\": [0.3, 0.5, 0.2]\n }\n ]\n // ...\n }\n ```\n\n - It is up to the database provider to decide which representation to use, typically depending on the internal format in which the structure is stored.\n However, given a structure identified by a unique ID, the API implementation MUST always provide the same representation for it.\n\n - The probabilities of occurrence of different assemblies are uncorrelated.\n So, for instance in the following case with two assemblies:\n ```json\n {\n \"assemblies\": [\n {\n \"sites_in_groups\": [ [0], [1] ],\n \"group_probabilities\": [0.2, 0.8],\n },\n {\n \"sites_in_groups\": [ [2], [3] ],\n \"group_probabilities\": [0.3, 0.7]\n }\n ]\n }\n ```\n\n Site 0 is present with a probability of 20 % and site 1 with a probability of 80 %. These two sites are correlated (either site 0 or 1 is present). Similarly, site 2 is present with a probability of 30 % and site 3 with a probability of 70 %.\n These two sites are correlated (either site 2 or 3 is present).\n However, the presence or absence of sites 0 and 1 is not correlated with the presence or absence of sites 2 and 3 (in the specific example, the pair of sites (0, 2) can occur with 0.2*0.3 = 6 % probability; the pair (0, 3) with 0.2*0.7 = 14 % probability; the pair (1, 2) with 0.8*0.3 = 24 % probability; and the pair (1, 3) with 0.8*0.7 = 56 % probability).", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "structure_features": { - "title": "Structure Features", - "type": "array", "items": { "$ref": "#/components/schemas/StructureFeatures" }, + "type": "array", + "title": "Structure Features", "description": "A list of strings that flag which special features are used by the structure.\n\n- **Type**: list of strings\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property.\n Filters on the list MUST support all mandatory HAS-type queries.\n Filter operators for comparisons on the string components MUST support equality, support for other comparison operators are OPTIONAL.\n - MUST be an empty list if no special features are used.\n - MUST be sorted alphabetically.\n - If a special feature listed below is used, the list MUST contain the corresponding string.\n - If a special feature listed below is not used, the list MUST NOT contain the corresponding string.\n - **List of strings used to indicate special structure features**:\n - `disorder`: this flag MUST be present if any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element.\n - `implicit_atoms`: this flag MUST be present if the structure contains atoms that are not assigned to sites via the property `species_at_sites` (e.g., because their positions are unknown).\n When this flag is present, the properties related to the chemical formula will likely not match the type and count of atoms represented by the `species_at_sites`, `species` and `assemblies` properties.\n - `site_attachments`: this flag MUST be present if any one entry in the `species` list includes `attached` and `nattached`.\n - `assemblies`: this flag MUST be present if the property `assemblies` is present.\n\n- **Examples**: A structure having implicit atoms and using assemblies: `[\"assemblies\", \"implicit_atoms\"]`", "x-optimade-support": "must", "x-optimade-queryable": "must" } }, + "type": "object", + "required": [ + "last_modified", + "elements", + "nelements", + "elements_ratios", + "chemical_formula_descriptive", + "chemical_formula_reduced", + "chemical_formula_anonymous", + "dimension_types", + "nperiodic_dimensions", + "lattice_vectors", + "cartesian_site_positions", + "nsites", + "species", + "species_at_sites", + "structure_features" + ], + "title": "StructureResourceAttributes", "description": "This class contains the Field for the attributes used to represent a structure, e.g. unit cell, atoms, positions." }, "StructureResponseMany": { - "title": "StructureResponseMany", - "required": [ - "data", - "meta" - ], - "type": "object", "properties": { "data": { - "title": "Data", - "uniqueItems": true, "anyOf": [ { - "type": "array", "items": { "$ref": "#/components/schemas/StructureResource" - } + }, + "type": "array" }, { - "type": "array", "items": { "type": "object" - } + }, + "type": "array" } ], + "uniqueItems": true, + "title": "Data", "description": "List of unique OPTIMADE structures entry resource objects." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/ResponseMeta" } ], + "title": "Meta", "description": "A meta object containing non-standard information" }, "errors": { - "title": "Errors", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/Error" }, + "type": "array", + "uniqueItems": true, + "title": "Errors", "description": "A list of unique errors" }, "included": { - "title": "Included", - "uniqueItems": true, "anyOf": [ { - "type": "array", "items": { "$ref": "#/components/schemas/EntryResource" - } + }, + "type": "array" }, { - "type": "array", "items": { "type": "object" - } + }, + "type": "array" } - ] + ], + "uniqueItems": true, + "title": "Included" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ToplevelLinks" } ], + "title": "Links", "description": "Links associated with the primary data or errors" }, "jsonapi": { - "title": "Jsonapi", "allOf": [ { "$ref": "#/components/schemas/JsonApi" } ], + "title": "Jsonapi", "description": "Information about the JSON API used" } }, - "description": "errors are not allowed" - }, - "StructureResponseOne": { - "title": "StructureResponseOne", + "type": "object", "required": [ "data", "meta" ], - "type": "object", + "title": "StructureResponseMany", + "description": "errors are not allowed" + }, + "StructureResponseOne": { "properties": { "data": { - "title": "Data", "anyOf": [ { "$ref": "#/components/schemas/StructureResource" @@ -3815,225 +3808,232 @@ "type": "object" } ], + "title": "Data", "description": "A single structures entry resource." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/ResponseMeta" } ], + "title": "Meta", "description": "A meta object containing non-standard information" }, "errors": { - "title": "Errors", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/Error" }, + "type": "array", + "uniqueItems": true, + "title": "Errors", "description": "A list of unique errors" }, "included": { - "title": "Included", - "uniqueItems": true, "anyOf": [ { - "type": "array", "items": { "$ref": "#/components/schemas/EntryResource" - } + }, + "type": "array" }, { - "type": "array", "items": { "type": "object" - } + }, + "type": "array" } - ] + ], + "uniqueItems": true, + "title": "Included" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ToplevelLinks" } ], + "title": "Links", "description": "Links associated with the primary data or errors" }, "jsonapi": { - "title": "Jsonapi", "allOf": [ { "$ref": "#/components/schemas/JsonApi" } ], + "title": "Jsonapi", "description": "Information about the JSON API used" } }, + "type": "object", + "required": [ + "data", + "meta" + ], + "title": "StructureResponseOne", "description": "errors are not allowed" }, "ToplevelLinks": { - "title": "ToplevelLinks", - "type": "object", "properties": { "self": { - "title": "Self", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Self", "description": "A link to itself" }, "related": { - "title": "Related", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Related", "description": "A related resource link" }, "first": { - "title": "First", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "First", "description": "The first page of data" }, "last": { - "title": "Last", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Last", "description": "The last page of data" }, "prev": { - "title": "Prev", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Prev", "description": "The previous page of data" }, "next": { - "title": "Next", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Next", "description": "The next page of data" } }, + "type": "object", + "title": "ToplevelLinks", "description": "A set of Links objects, possibly including pagination" }, "Warnings": { - "title": "Warnings", - "required": [ - "detail", - "type" - ], - "type": "object", "properties": { "id": { - "title": "Id", "type": "string", + "title": "Id", "description": "A unique identifier for this particular occurrence of the problem." }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ErrorLinks" } ], + "title": "Links", "description": "A links object storing about" }, "code": { - "title": "Code", "type": "string", + "title": "Code", "description": "an application-specific error code, expressed as a string value." }, "title": { - "title": "Title", "type": "string", + "title": "Title", "description": "A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization." }, "detail": { - "title": "Detail", "type": "string", + "title": "Detail", "description": "A human-readable explanation specific to this occurrence of the problem." }, "source": { - "title": "Source", "allOf": [ { "$ref": "#/components/schemas/ErrorSource" } ], + "title": "Source", "description": "An object containing references to the source of the error" }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "a meta object containing non-standard meta-information about the error." }, "type": { - "title": "Type", - "pattern": "^warning$", "type": "string", + "pattern": "^warning$", + "title": "Type", "description": "Warnings must be of type \"warning\"", "default": "warning" } }, + "type": "object", + "required": [ + "detail", + "type" + ], + "title": "Warnings", "description": "OPTIMADE-specific warning class based on OPTIMADE-specific JSON API Error.\n\nFrom the specification:\n\nA warning resource object is defined similarly to a JSON API error object, but MUST also include the field type, which MUST have the value \"warning\".\nThe field detail MUST be present and SHOULD contain a non-critical message, e.g., reporting unrecognized search attributes or deprecated features.\n\nNote: Must be named \"Warnings\", since \"Warning\" is a built-in Python class." } } From 7a75366befacb06356c7f9cab16368765969b84f Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Thu, 14 Sep 2023 12:56:22 +0200 Subject: [PATCH 64/86] changed 'regex' to 'pattern'. --- openapi/index_openapi.json | 840 ++++++++-------- openapi/openapi.json | 1592 +++++++++++++++---------------- optimade/server/query_params.py | 4 +- 3 files changed, 1218 insertions(+), 1218 deletions(-) diff --git a/openapi/index_openapi.json b/openapi/index_openapi.json index 4c7dea0af..45b32228c 100644 --- a/openapi/index_openapi.json +++ b/openapi/index_openapi.json @@ -1,5 +1,5 @@ { - "openapi": "3.0.2", + "openapi": "3.1.0", "info": { "title": "OPTIMADE API - Index meta-database", "description": "The [Open Databases Integration for Materials Design (OPTIMADE) consortium](https://www.optimade.org/) aims to make materials databases interoperational by developing a common REST API.\nThis is the \"special\" index meta-database.\n\nThis specification is generated using [`optimade-python-tools`](https://github.com/Materials-Consortia/optimade-python-tools/tree/v0.25.2) v0.25.2.", @@ -109,8 +109,8 @@ "description": "A filter string, in the format described in section API Filtering Format Specification of the specification.", "required": false, "schema": { - "title": "Filter", "type": "string", + "title": "Filter", "description": "A filter string, in the format described in section API Filtering Format Specification of the specification.", "default": "" }, @@ -121,8 +121,8 @@ "description": "The output format requested (see section Response Format).\nDefaults to the format string 'json', which specifies the standard output format described in this specification.\nExample: `http://example.com/v1/structures?response_format=xml`", "required": false, "schema": { - "title": "Response Format", "type": "string", + "title": "Response Format", "description": "The output format requested (see section Response Format).\nDefaults to the format string 'json', which specifies the standard output format described in this specification.\nExample: `http://example.com/v1/structures?response_format=xml`", "default": "json" }, @@ -133,10 +133,10 @@ "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "required": false, "schema": { - "title": "Email Address", "type": "string", - "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "format": "email", + "title": "Email Address", + "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "default": "" }, "name": "email_address", @@ -146,9 +146,9 @@ "description": "A comma-delimited set of fields to be provided in the output.\nIf provided, these fields MUST be returned along with the REQUIRED fields.\nOther OPTIONAL fields MUST NOT be returned when this parameter is present.\nExample: `http://example.com/v1/structures?response_fields=last_modified,nsites`", "required": false, "schema": { - "title": "Response Fields", - "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", "type": "string", + "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", + "title": "Response Fields", "description": "A comma-delimited set of fields to be provided in the output.\nIf provided, these fields MUST be returned along with the REQUIRED fields.\nOther OPTIONAL fields MUST NOT be returned when this parameter is present.\nExample: `http://example.com/v1/structures?response_fields=last_modified,nsites`", "default": "" }, @@ -159,9 +159,9 @@ "description": "If supporting sortable queries, an implementation MUST use the `sort` query parameter with format as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-sorting).\n\nAn implementation MAY support multiple sort fields for a single query.\nIf it does, it again MUST conform to the JSON API 1.0 specification.\n\nIf an implementation supports sorting for an entry listing endpoint, then the `/info/` endpoint MUST include, for each field name `` in its `data.properties.` response value that can be used for sorting, the key `sortable` with value `true`.\nIf a field name under an entry listing endpoint supporting sorting cannot be used for sorting, the server MUST either leave out the `sortable` key or set it equal to `false` for the specific field name.\nThe set of field names, with `sortable` equal to `true` are allowed to be used in the \"sort fields\" list according to its definition in the JSON API 1.0 specification.\nThe field `sortable` is in addition to each property description and other OPTIONAL fields.\nAn example is shown in the section Entry Listing Info Endpoints.", "required": false, "schema": { - "title": "Sort", - "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", "type": "string", + "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", + "title": "Sort", "description": "If supporting sortable queries, an implementation MUST use the `sort` query parameter with format as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-sorting).\n\nAn implementation MAY support multiple sort fields for a single query.\nIf it does, it again MUST conform to the JSON API 1.0 specification.\n\nIf an implementation supports sorting for an entry listing endpoint, then the `/info/` endpoint MUST include, for each field name `` in its `data.properties.` response value that can be used for sorting, the key `sortable` with value `true`.\nIf a field name under an entry listing endpoint supporting sorting cannot be used for sorting, the server MUST either leave out the `sortable` key or set it equal to `false` for the specific field name.\nThe set of field names, with `sortable` equal to `true` are allowed to be used in the \"sort fields\" list according to its definition in the JSON API 1.0 specification.\nThe field `sortable` is in addition to each property description and other OPTIONAL fields.\nAn example is shown in the section Entry Listing Info Endpoints.", "default": "" }, @@ -172,9 +172,9 @@ "description": "Sets a numerical limit on the number of entries returned.\nSee [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-pagination).\nThe API implementation MUST return no more than the number specified.\nIt MAY return fewer.\nThe database MAY have a maximum limit and not accept larger numbers (in which case an error code -- 403 Forbidden -- MUST be returned).\nThe default limit value is up to the API implementation to decide.\nExample: `http://example.com/optimade/v1/structures?page_limit=100`", "required": false, "schema": { - "title": "Page Limit", - "minimum": 0.0, "type": "integer", + "minimum": 0.0, + "title": "Page Limit", "description": "Sets a numerical limit on the number of entries returned.\nSee [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-pagination).\nThe API implementation MUST return no more than the number specified.\nIt MAY return fewer.\nThe database MAY have a maximum limit and not accept larger numbers (in which case an error code -- 403 Forbidden -- MUST be returned).\nThe default limit value is up to the API implementation to decide.\nExample: `http://example.com/optimade/v1/structures?page_limit=100`", "default": 20 }, @@ -185,9 +185,9 @@ "description": "RECOMMENDED for use with _offset-based_ pagination: using `page_offset` and `page_limit` is RECOMMENDED.\nExample: Skip 50 structures and fetch up to 100: `/structures?page_offset=50&page_limit=100`.", "required": false, "schema": { - "title": "Page Offset", - "minimum": 0.0, "type": "integer", + "minimum": 0.0, + "title": "Page Offset", "description": "RECOMMENDED for use with _offset-based_ pagination: using `page_offset` and `page_limit` is RECOMMENDED.\nExample: Skip 50 structures and fetch up to 100: `/structures?page_offset=50&page_limit=100`.", "default": 0 }, @@ -198,8 +198,8 @@ "description": "RECOMMENDED for use with _page-based_ pagination: using `page_number` and `page_limit` is RECOMMENDED.\nIt is RECOMMENDED that the first page has number 1, i.e., that `page_number` is 1-based.\nExample: Fetch page 2 of up to 50 structures per page: `/structures?page_number=2&page_limit=50`.", "required": false, "schema": { - "title": "Page Number", "type": "integer", + "title": "Page Number", "description": "RECOMMENDED for use with _page-based_ pagination: using `page_number` and `page_limit` is RECOMMENDED.\nIt is RECOMMENDED that the first page has number 1, i.e., that `page_number` is 1-based.\nExample: Fetch page 2 of up to 50 structures per page: `/structures?page_number=2&page_limit=50`." }, "name": "page_number", @@ -209,9 +209,9 @@ "description": "RECOMMENDED for use with _cursor-based_ pagination: using `page_cursor` and `page_limit` is RECOMMENDED.", "required": false, "schema": { - "title": "Page Cursor", - "minimum": 0.0, "type": "integer", + "minimum": 0.0, + "title": "Page Cursor", "description": "RECOMMENDED for use with _cursor-based_ pagination: using `page_cursor` and `page_limit` is RECOMMENDED.", "default": 0 }, @@ -222,8 +222,8 @@ "description": "RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED.\nExample: Fetch up to 100 structures above sort-field value 4000 (in this example, server chooses to fetch results sorted by increasing `id`, so `page_above` value refers to an `id` value): `/structures?page_above=4000&page_limit=100`.", "required": false, "schema": { - "title": "Page Above", "type": "string", + "title": "Page Above", "description": "RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED.\nExample: Fetch up to 100 structures above sort-field value 4000 (in this example, server chooses to fetch results sorted by increasing `id`, so `page_above` value refers to an `id` value): `/structures?page_above=4000&page_limit=100`." }, "name": "page_above", @@ -233,8 +233,8 @@ "description": "RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED.", "required": false, "schema": { - "title": "Page Below", "type": "string", + "title": "Page Below", "description": "RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED." }, "name": "page_below", @@ -244,8 +244,8 @@ "description": "A server MAY implement the JSON API concept of returning [compound documents](https://jsonapi.org/format/1.0/#document-compound-documents) by utilizing the `include` query parameter as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-includes).\n\nAll related resource objects MUST be returned as part of an array value for the top-level `included` field, see the section JSON Response Schema: Common Fields.\n\nThe value of `include` MUST be a comma-separated list of \"relationship paths\", as defined in the [JSON API](https://jsonapi.org/format/1.0/#fetching-includes).\nIf relationship paths are not supported, or a server is unable to identify a relationship path a `400 Bad Request` response MUST be made.\n\nThe **default value** for `include` is `references`.\nThis means `references` entries MUST always be included under the top-level field `included` as default, since a server assumes if `include` is not specified by a client in the request, it is still specified as `include=references`.\nNote, if a client explicitly specifies `include` and leaves out `references`, `references` resource objects MUST NOT be included under the top-level field `included`, as per the definition of `included`, see section JSON Response Schema: Common Fields.\n\n> **Note**: A query with the parameter `include` set to the empty string means no related resource objects are to be returned under the top-level field `included`.", "required": false, "schema": { - "title": "Include", "type": "string", + "title": "Include", "description": "A server MAY implement the JSON API concept of returning [compound documents](https://jsonapi.org/format/1.0/#document-compound-documents) by utilizing the `include` query parameter as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-includes).\n\nAll related resource objects MUST be returned as part of an array value for the top-level `included` field, see the section JSON Response Schema: Common Fields.\n\nThe value of `include` MUST be a comma-separated list of \"relationship paths\", as defined in the [JSON API](https://jsonapi.org/format/1.0/#fetching-includes).\nIf relationship paths are not supported, or a server is unable to identify a relationship path a `400 Bad Request` response MUST be made.\n\nThe **default value** for `include` is `references`.\nThis means `references` entries MUST always be included under the top-level field `included` as default, since a server assumes if `include` is not specified by a client in the request, it is still specified as `include=references`.\nNote, if a client explicitly specifies `include` and leaves out `references`, `references` resource objects MUST NOT be included under the top-level field `included`, as per the definition of `included`, see section JSON Response Schema: Common Fields.\n\n> **Note**: A query with the parameter `include` set to the empty string means no related resource objects are to be returned under the top-level field `included`.", "default": "references" }, @@ -256,9 +256,9 @@ "description": "If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.", "required": false, "schema": { - "title": "Api Hint", - "pattern": "(v[0-9]+(\\.[0-9]+)?)?", "type": "string", + "pattern": "(v[0-9]+(\\.[0-9]+)?)?", + "title": "Api Hint", "description": "If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.", "default": "" }, @@ -376,42 +376,36 @@ "components": { "schemas": { "Aggregate": { - "title": "Aggregate", "enum": [ "ok", "test", "staging", "no" ], + "title": "Aggregate", "description": "Enumeration of aggregate values" }, "Attributes": { - "title": "Attributes", - "type": "object", "properties": {}, + "type": "object", + "title": "Attributes", "description": "Members of the attributes object (\"attributes\") represent information about the resource object in which it's defined.\nThe keys for Attributes MUST NOT be:\n relationships\n links\n id\n type" }, "AvailableApiVersion": { - "title": "AvailableApiVersion", - "required": [ - "url", - "version" - ], - "type": "object", "properties": { "url": { - "title": "Url", + "type": "string", "maxLength": 65536, "minLength": 1, "pattern": ".+/v[0-1](\\.[0-9]+)*/?$", - "type": "string", - "description": "A string specifying a versioned base URL that MUST adhere to the rules in section Base URL", - "format": "uri" + "format": "uri", + "title": "Url", + "description": "A string specifying a versioned base URL that MUST adhere to the rules in section Base URL" }, "version": { - "title": "Version", - "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", "type": "string", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", + "title": "Version", "description": "A string containing the full version number of the API served at that versioned base URL.\nThe version number string MUST NOT be prefixed by, e.g., 'v'.\nExamples: `1.0.0`, `1.0.0-rc.2`.", "example": [ "0.10.1", @@ -420,459 +414,457 @@ ] } }, + "type": "object", + "required": [ + "url", + "version" + ], + "title": "AvailableApiVersion", "description": "A JSON object containing information about an available API version" }, "BaseRelationshipMeta": { - "title": "BaseRelationshipMeta", - "required": [ - "description" - ], - "type": "object", "properties": { "description": { - "title": "Description", "type": "string", + "title": "Description", "description": "OPTIONAL human-readable description of the relationship." } }, + "type": "object", + "required": [ + "description" + ], + "title": "BaseRelationshipMeta", "description": "Specific meta field for base relationship resource" }, "BaseRelationshipResource": { - "title": "BaseRelationshipResource", - "required": [ - "id", - "type" - ], - "type": "object", "properties": { "id": { - "title": "Id", "type": "string", + "title": "Id", "description": "Resource ID" }, "type": { - "title": "Type", "type": "string", + "title": "Type", "description": "Resource type" }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/BaseRelationshipMeta" } ], + "title": "Meta", "description": "Relationship meta field. MUST contain 'description' if supplied." } }, + "type": "object", + "required": [ + "id", + "type" + ], + "title": "BaseRelationshipResource", "description": "Minimum requirements to represent a relationship resource" }, "EntryMetadata": { - "title": "EntryMetadata", - "type": "object", "properties": { "property_metadata": { - "title": "Property Metadata", "type": "object", + "title": "Property Metadata", "description": "A dictionary, where the keys are the names of the properties in the attributes field and the value is a dictionary containing the metadata for that property.\nDatabase-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes)." }, "partial_data_links": { - "title": "Partial Data Links", - "type": "object", "additionalProperties": { - "type": "array", "items": { "$ref": "#/components/schemas/PartialDataLink" - } + }, + "type": "array" }, + "type": "object", + "title": "Partial Data Links", "description": "A dictionary, where the keys are the names of the properties in the attributes field for which the value is too large to be shared by default.\n For each property one or more links are provided from which the value of the attribute can be retrieved." } }, + "type": "object", + "title": "EntryMetadata", "description": "Contains the metadata for the attributes of an entry" }, "EntryRelationships": { - "title": "EntryRelationships", - "type": "object", "properties": { "references": { - "title": "References", "allOf": [ { "$ref": "#/components/schemas/ReferenceRelationship" } ], + "title": "References", "description": "Object containing links to relationships with entries of the `references` type." }, "structures": { - "title": "Structures", "allOf": [ { "$ref": "#/components/schemas/StructureRelationship" } ], + "title": "Structures", "description": "Object containing links to relationships with entries of the `structures` type." } }, + "type": "object", + "title": "EntryRelationships", "description": "This model wraps the JSON API Relationships to include type-specific top level keys." }, "EntryResource": { - "title": "EntryResource", - "required": [ - "id", - "type", - "attributes" - ], - "type": "object", "properties": { "id": { - "title": "Id", "type": "string", + "title": "Id", "description": "An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`", "x-optimade-support": "must", "x-optimade-queryable": "must" }, "type": { - "title": "Type", "type": "string", + "title": "Type", "description": "The name of the type of an entry.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type `` and ID `` MUST be returned in response to a request for `//` under the versioned base URL.\n\n- **Example**: `\"structures\"`", "x-optimade-support": "must", "x-optimade-queryable": "must" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ResourceLinks" } ], + "title": "Links", "description": "a links object containing links related to the resource." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/EntryMetadata" } ], + "title": "Meta", "description": "A dictionary, containing entry and property-specific metadata for a given entry." }, "attributes": { - "title": "Attributes", "allOf": [ { "$ref": "#/components/schemas/EntryResourceAttributes" } ], + "title": "Attributes", "description": "A dictionary, containing key-value pairs representing the entry's properties, except for `type` and `id`.\nDatabase-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes)." }, "relationships": { - "title": "Relationships", "allOf": [ { "$ref": "#/components/schemas/EntryRelationships" } ], + "title": "Relationships", "description": "A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object." } }, + "type": "object", + "required": [ + "id", + "type", + "attributes" + ], + "title": "EntryResource", "description": "The base model for an entry resource." }, "EntryResourceAttributes": { - "title": "EntryResourceAttributes", - "required": [ - "last_modified" - ], - "type": "object", "properties": { "immutable_id": { - "title": "Immutable Id", "type": "string", + "title": "Immutable Id", "description": "The entry's immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)", "x-optimade-support": "optional", "x-optimade-queryable": "must" }, "last_modified": { - "title": "Last Modified", "type": "string", - "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "format": "date-time", + "title": "Last Modified", + "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "x-optimade-support": "should", "x-optimade-queryable": "must" } }, + "type": "object", + "required": [ + "last_modified" + ], + "title": "EntryResourceAttributes", "description": "Contains key-value pairs representing the entry's properties." }, "Error": { - "title": "Error", - "type": "object", "properties": { "id": { - "title": "Id", "type": "string", + "title": "Id", "description": "A unique identifier for this particular occurrence of the problem." }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ErrorLinks" } ], + "title": "Links", "description": "A links object storing about" }, "status": { - "title": "Status", "type": "string", + "title": "Status", "description": "the HTTP status code applicable to this problem, expressed as a string value." }, "code": { - "title": "Code", "type": "string", + "title": "Code", "description": "an application-specific error code, expressed as a string value." }, "title": { - "title": "Title", "type": "string", + "title": "Title", "description": "A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization." }, "detail": { - "title": "Detail", "type": "string", + "title": "Detail", "description": "A human-readable explanation specific to this occurrence of the problem." }, "source": { - "title": "Source", "allOf": [ { "$ref": "#/components/schemas/ErrorSource" } ], + "title": "Source", "description": "An object containing references to the source of the error" }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "a meta object containing non-standard meta-information about the error." } }, + "type": "object", + "title": "Error", "description": "An error response" }, "ErrorLinks": { - "title": "ErrorLinks", - "type": "object", "properties": { "about": { - "title": "About", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "About", "description": "A link that leads to further details about this particular occurrence of the problem." } }, + "type": "object", + "title": "ErrorLinks", "description": "A Links object specific to Error objects" }, "ErrorResponse": { - "title": "ErrorResponse", - "required": [ - "meta", - "errors" - ], - "type": "object", "properties": { "data": { - "title": "Data", - "uniqueItems": true, "anyOf": [ { "$ref": "#/components/schemas/Resource" }, { - "type": "array", "items": { "$ref": "#/components/schemas/Resource" - } + }, + "type": "array" } ], + "uniqueItems": true, + "title": "Data", "description": "Outputted Data" }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/ResponseMeta" } ], + "title": "Meta", "description": "A meta object containing non-standard information." }, "errors": { - "title": "Errors", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/OptimadeError" }, + "type": "array", + "uniqueItems": true, + "title": "Errors", "description": "A list of OPTIMADE-specific JSON API error objects, where the field detail MUST be present." }, "included": { - "title": "Included", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/Resource" }, + "type": "array", + "uniqueItems": true, + "title": "Included", "description": "A list of unique included resources" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ToplevelLinks" } ], + "title": "Links", "description": "Links associated with the primary data or errors" }, "jsonapi": { - "title": "Jsonapi", "allOf": [ { "$ref": "#/components/schemas/JsonApi" } ], + "title": "Jsonapi", "description": "Information about the JSON API used" } }, + "type": "object", + "required": [ + "meta", + "errors" + ], + "title": "ErrorResponse", "description": "errors MUST be present and data MUST be skipped" }, "ErrorSource": { - "title": "ErrorSource", - "type": "object", "properties": { "pointer": { - "title": "Pointer", "type": "string", + "title": "Pointer", "description": "a JSON Pointer [RFC6901] to the associated entity in the request document [e.g. \"/data\" for a primary data object, or \"/data/attributes/title\" for a specific attribute]." }, "parameter": { - "title": "Parameter", "type": "string", + "title": "Parameter", "description": "a string indicating which URI query parameter caused the error." } }, + "type": "object", + "title": "ErrorSource", "description": "an object containing references to the source of the error" }, "Implementation": { - "title": "Implementation", - "type": "object", "properties": { "name": { - "title": "Name", "type": "string", + "title": "Name", "description": "name of the implementation" }, "version": { - "title": "Version", "type": "string", + "title": "Version", "description": "version string of the current implementation" }, "homepage": { - "title": "Homepage", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Homepage", "description": "A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the homepage of the implementation." }, "source_url": { - "title": "Source Url", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Source Url", "description": "A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the implementation source, either downloadable archive or version control system." }, "maintainer": { - "title": "Maintainer", "allOf": [ { "$ref": "#/components/schemas/ImplementationMaintainer" } ], + "title": "Maintainer", "description": "A dictionary providing details about the maintainer of the implementation." }, "issue_tracker": { - "title": "Issue Tracker", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Issue Tracker", "description": "A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the implementation's issue tracker." } }, + "type": "object", + "title": "Implementation", "description": "Information on the server implementation" }, "ImplementationMaintainer": { - "title": "ImplementationMaintainer", - "required": [ - "email" - ], - "type": "object", "properties": { "email": { - "title": "Email", "type": "string", - "description": "the maintainer's email address", - "format": "email" + "format": "email", + "title": "Email", + "description": "the maintainer's email address" } }, + "type": "object", + "required": [ + "email" + ], + "title": "ImplementationMaintainer", "description": "Details about the maintainer of the implementation" }, "IndexInfoAttributes": { - "title": "IndexInfoAttributes", - "required": [ - "api_version", - "available_api_versions", - "available_endpoints", - "entry_types_by_format" - ], - "type": "object", "properties": { "api_version": { - "title": "Api Version", - "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", "type": "string", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", + "title": "Api Version", "description": "Presently used full version of the OPTIMADE API.\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\nExamples: `1.0.0`, `1.0.0-rc.2`.", "example": [ "0.10.1", @@ -881,814 +873,815 @@ ] }, "available_api_versions": { - "title": "Available Api Versions", - "type": "array", "items": { "$ref": "#/components/schemas/AvailableApiVersion" }, + "type": "array", + "title": "Available Api Versions", "description": "A list of dictionaries of available API versions at other base URLs" }, "formats": { - "title": "Formats", - "type": "array", "items": { "type": "string" }, + "type": "array", + "title": "Formats", "description": "List of available output formats.", "default": [ "json" ] }, "available_endpoints": { - "title": "Available Endpoints", - "type": "array", "items": { "type": "string" }, + "type": "array", + "title": "Available Endpoints", "description": "List of available endpoints (i.e., the string to be appended to the versioned base URL)." }, "entry_types_by_format": { - "title": "Entry Types By Format", - "type": "object", "additionalProperties": { - "type": "array", "items": { "type": "string" - } + }, + "type": "array" }, + "type": "object", + "title": "Entry Types By Format", "description": "Available entry endpoints as a function of output formats." }, "is_index": { - "title": "Is Index", "type": "boolean", + "title": "Is Index", "description": "This must be `true` since this is an index meta-database (see section Index Meta-Database).", "default": true } }, + "type": "object", + "required": [ + "api_version", + "available_api_versions", + "available_endpoints", + "entry_types_by_format" + ], + "title": "IndexInfoAttributes", "description": "Attributes for Base URL Info endpoint for an Index Meta-Database" }, "IndexInfoResource": { - "title": "IndexInfoResource", - "required": [ - "id", - "type", - "attributes", - "relationships" - ], - "type": "object", "properties": { "id": { - "title": "Id", - "pattern": "^/$", "type": "string", + "pattern": "^/$", + "title": "Id", "default": "/" }, "type": { - "title": "Type", - "pattern": "^info$", "type": "string", + "pattern": "^info$", + "title": "Type", "default": "info" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ResourceLinks" } ], + "title": "Links", "description": "a links object containing links related to the resource." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship." }, "attributes": { "$ref": "#/components/schemas/IndexInfoAttributes" }, "relationships": { - "title": "Relationships", - "type": "object", "additionalProperties": { "$ref": "#/components/schemas/IndexRelationship" }, + "type": "object", + "title": "Relationships", "description": "Reference to the Links identifier object under the `links` endpoint that the provider has chosen as their 'default' OPTIMADE API database.\nA client SHOULD present this database as the first choice when an end-user chooses this provider." } }, + "type": "object", + "required": [ + "id", + "type", + "attributes", + "relationships" + ], + "title": "IndexInfoResource", "description": "Index Meta-Database Base URL Info endpoint resource" }, "IndexInfoResponse": { - "title": "IndexInfoResponse", - "required": [ - "data", - "meta" - ], - "type": "object", "properties": { "data": { - "title": "Data", "allOf": [ { "$ref": "#/components/schemas/IndexInfoResource" } ], + "title": "Data", "description": "Index meta-database /info data." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/ResponseMeta" } ], + "title": "Meta", "description": "A meta object containing non-standard information" }, "errors": { - "title": "Errors", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/Error" }, + "type": "array", + "uniqueItems": true, + "title": "Errors", "description": "A list of unique errors" }, "included": { - "title": "Included", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/Resource" }, + "type": "array", + "uniqueItems": true, + "title": "Included", "description": "A list of unique included resources" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ToplevelLinks" } ], + "title": "Links", "description": "Links associated with the primary data or errors" }, "jsonapi": { - "title": "Jsonapi", "allOf": [ { "$ref": "#/components/schemas/JsonApi" } ], + "title": "Jsonapi", "description": "Information about the JSON API used" } }, + "type": "object", + "required": [ + "data", + "meta" + ], + "title": "IndexInfoResponse", "description": "errors are not allowed" }, "IndexRelationship": { - "title": "IndexRelationship", - "required": [ - "data" - ], - "type": "object", "properties": { "data": { - "title": "Data", "allOf": [ { "$ref": "#/components/schemas/RelatedLinksResource" } ], + "title": "Data", "description": "[JSON API resource linkage](http://jsonapi.org/format/1.0/#document-links).\nIt MUST be either `null` or contain a single Links identifier object with the fields `id` and `type`" } }, + "type": "object", + "required": [ + "data" + ], + "title": "IndexRelationship", "description": "Index Meta-Database relationship" }, "JsonApi": { - "title": "JsonApi", - "type": "object", "properties": { "version": { - "title": "Version", "type": "string", + "title": "Version", "description": "Version of the json API used", "default": "1.0" }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "Non-standard meta information" } }, + "type": "object", + "title": "JsonApi", "description": "An object describing the server's implementation" }, "Link": { - "title": "Link", - "required": [ - "href" - ], - "type": "object", "properties": { "href": { - "title": "Href", + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", - "description": "a string containing the link\u2019s URL.", - "format": "uri" + "format": "uri", + "title": "Href", + "description": "a string containing the link\u2019s URL." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "a meta object containing non-standard meta-information about the link." } }, + "type": "object", + "required": [ + "href" + ], + "title": "Link", "description": "A link **MUST** be represented as either: a string containing the link's URL or a link object." }, "LinkType": { - "title": "LinkType", "enum": [ "child", "root", "external", "providers" ], + "title": "LinkType", "description": "Enumeration of link_type values" }, "LinksResource": { - "title": "LinksResource", - "required": [ - "id", - "type", - "attributes" - ], - "type": "object", "properties": { "id": { - "title": "Id", "type": "string", + "title": "Id", "description": "An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`", "x-optimade-support": "must", "x-optimade-queryable": "must" }, "type": { - "title": "Type", - "pattern": "^links$", "type": "string", + "pattern": "^links$", + "title": "Type", "description": "These objects are described in detail in the section Links Endpoint", "default": "links" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ResourceLinks" } ], + "title": "Links", "description": "a links object containing links related to the resource." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/EntryMetadata" } ], + "title": "Meta", "description": "A dictionary, containing entry and property-specific metadata for a given entry." }, "attributes": { - "title": "Attributes", "allOf": [ { "$ref": "#/components/schemas/LinksResourceAttributes" } ], + "title": "Attributes", "description": "A dictionary containing key-value pairs representing the Links resource's properties." }, "relationships": { - "title": "Relationships", "allOf": [ { "$ref": "#/components/schemas/EntryRelationships" } ], + "title": "Relationships", "description": "A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object." } }, + "type": "object", + "required": [ + "id", + "type", + "attributes" + ], + "title": "LinksResource", "description": "A Links endpoint resource object" }, "LinksResourceAttributes": { - "title": "LinksResourceAttributes", - "required": [ - "name", - "description", - "base_url", - "homepage", - "link_type" - ], - "type": "object", "properties": { "name": { - "title": "Name", "type": "string", + "title": "Name", "description": "Human-readable name for the OPTIMADE API implementation, e.g., for use in clients to show the name to the end-user." }, "description": { - "title": "Description", "type": "string", + "title": "Description", "description": "Human-readable description for the OPTIMADE API implementation, e.g., for use in clients to show a description to the end-user." }, "base_url": { - "title": "Base Url", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Base Url", "description": "JSON API links object, pointing to the base URL for this implementation" }, "homepage": { - "title": "Homepage", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Homepage", "description": "JSON API links object, pointing to a homepage URL for this implementation" }, "link_type": { - "title": "Link Type", "allOf": [ { "$ref": "#/components/schemas/LinkType" } ], + "title": "Link Type", "description": "The type of the linked relation.\nMUST be one of these values: 'child', 'root', 'external', 'providers'." }, "aggregate": { - "title": "Aggregate", "allOf": [ { "$ref": "#/components/schemas/Aggregate" } ], + "title": "Aggregate", "description": "A string indicating whether a client that is following links to aggregate results from different OPTIMADE implementations should follow this link or not.\nThis flag SHOULD NOT be indicated for links where `link_type` is not `child`.\n\nIf not specified, clients MAY assume that the value is `ok`.\nIf specified, and the value is anything different than `ok`, the client MUST assume that the server is suggesting not to follow the link during aggregation by default (also if the value is not among the known ones, in case a future specification adds new accepted values).\n\nSpecific values indicate the reason why the server is providing the suggestion.\nA client MAY follow the link anyway if it has reason to do so (e.g., if the client is looking for all test databases, it MAY follow the links marked with `aggregate`=`test`).\n\nIf specified, it MUST be one of the values listed in section Link Aggregate Options.", "default": "ok" }, "no_aggregate_reason": { - "title": "No Aggregate Reason", "type": "string", + "title": "No Aggregate Reason", "description": "An OPTIONAL human-readable string indicating the reason for suggesting not to aggregate results following the link.\nIt SHOULD NOT be present if `aggregate`=`ok`." } }, + "type": "object", + "required": [ + "name", + "description", + "base_url", + "homepage", + "link_type" + ], + "title": "LinksResourceAttributes", "description": "Links endpoint resource object attributes" }, "LinksResponse": { - "title": "LinksResponse", - "required": [ - "data", - "meta" - ], - "type": "object", "properties": { "data": { - "title": "Data", - "uniqueItems": true, "anyOf": [ { - "type": "array", "items": { "$ref": "#/components/schemas/LinksResource" - } + }, + "type": "array" }, { - "type": "array", "items": { "type": "object" - } + }, + "type": "array" } ], + "uniqueItems": true, + "title": "Data", "description": "List of unique OPTIMADE links resource objects." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/ResponseMeta" } ], + "title": "Meta", "description": "A meta object containing non-standard information" }, "errors": { - "title": "Errors", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/Error" }, + "type": "array", + "uniqueItems": true, + "title": "Errors", "description": "A list of unique errors" }, "included": { - "title": "Included", - "uniqueItems": true, "anyOf": [ { - "type": "array", "items": { "$ref": "#/components/schemas/EntryResource" - } + }, + "type": "array" }, { - "type": "array", "items": { "type": "object" - } + }, + "type": "array" } - ] + ], + "uniqueItems": true, + "title": "Included" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ToplevelLinks" } ], + "title": "Links", "description": "Links associated with the primary data or errors" }, "jsonapi": { - "title": "Jsonapi", "allOf": [ { "$ref": "#/components/schemas/JsonApi" } ], + "title": "Jsonapi", "description": "Information about the JSON API used" } }, + "type": "object", + "required": [ + "data", + "meta" + ], + "title": "LinksResponse", "description": "errors are not allowed" }, "Meta": { - "title": "Meta", - "type": "object", "properties": {}, + "type": "object", + "title": "Meta", "description": "Non-standard meta-information that can not be represented as an attribute or relationship." }, "OptimadeError": { - "title": "OptimadeError", - "required": [ - "detail" - ], - "type": "object", "properties": { "id": { - "title": "Id", "type": "string", + "title": "Id", "description": "A unique identifier for this particular occurrence of the problem." }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ErrorLinks" } ], + "title": "Links", "description": "A links object storing about" }, "status": { - "title": "Status", "type": "string", + "title": "Status", "description": "the HTTP status code applicable to this problem, expressed as a string value." }, "code": { - "title": "Code", "type": "string", + "title": "Code", "description": "an application-specific error code, expressed as a string value." }, "title": { - "title": "Title", "type": "string", + "title": "Title", "description": "A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization." }, "detail": { - "title": "Detail", "type": "string", + "title": "Detail", "description": "A human-readable explanation specific to this occurrence of the problem." }, "source": { - "title": "Source", "allOf": [ { "$ref": "#/components/schemas/ErrorSource" } ], + "title": "Source", "description": "An object containing references to the source of the error" }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "a meta object containing non-standard meta-information about the error." } }, + "type": "object", + "required": [ + "detail" + ], + "title": "OptimadeError", "description": "detail MUST be present" }, "PartialDataLink": { - "title": "PartialDataLink", - "required": [ - "link", - "format" - ], - "type": "object", "properties": { "link": { - "title": "Link", + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", - "description": "String. A JSON API link that points to a location from which the omitted data can be fetched. There is no requirement on the syntax or format for the link URL.", - "format": "uri" + "format": "uri", + "title": "Link", + "description": "String. A JSON API link that points to a location from which the omitted data can be fetched. There is no requirement on the syntax or format for the link URL." }, "format": { - "title": "Format", "type": "string", + "title": "Format", "description": "String. The name of the format provided via this link. For one of the objects this format field SHOULD have the value \"jsonlines\", which refers to the format in OPTIMADE JSON lines partial data format." } - } - }, - "Provider": { - "title": "Provider", + }, + "type": "object", "required": [ - "name", - "description", - "prefix" + "link", + "format" ], - "type": "object", + "title": "PartialDataLink" + }, + "Provider": { "properties": { "name": { - "title": "Name", "type": "string", + "title": "Name", "description": "a short name for the database provider" }, "description": { - "title": "Description", "type": "string", + "title": "Description", "description": "a longer description of the database provider" }, "prefix": { - "title": "Prefix", - "pattern": "^[a-z]([a-z]|[0-9]|_)*$", "type": "string", + "pattern": "^[a-z]([a-z]|[0-9]|_)*$", + "title": "Prefix", "description": "database-provider-specific prefix as found in section Database-Provider-Specific Namespace Prefixes." }, "homepage": { - "title": "Homepage", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Homepage", "description": "a [JSON API links object](http://jsonapi.org/format/1.0#document-links) pointing to homepage of the database provider, either directly as a string, or as a link object." } }, + "type": "object", + "required": [ + "name", + "description", + "prefix" + ], + "title": "Provider", "description": "Information on the database provider of the implementation." }, "ReferenceRelationship": { - "title": "ReferenceRelationship", - "type": "object", "properties": { "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/RelationshipLinks" } ], + "title": "Links", "description": "a links object containing at least one of the following: self, related" }, "data": { - "title": "Data", - "uniqueItems": true, "anyOf": [ { "$ref": "#/components/schemas/BaseRelationshipResource" }, { - "type": "array", "items": { "$ref": "#/components/schemas/BaseRelationshipResource" - } + }, + "type": "array" } ], + "uniqueItems": true, + "title": "Data", "description": "Resource linkage" }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "a meta object that contains non-standard meta-information about the relationship." } }, + "type": "object", + "title": "ReferenceRelationship", "description": "Similar to normal JSON API relationship, but with addition of OPTIONAL meta field for a resource." }, "RelatedLinksResource": { - "title": "RelatedLinksResource", - "required": [ - "id", - "type" - ], - "type": "object", "properties": { "id": { - "title": "Id", "type": "string", + "title": "Id", "description": "Resource ID" }, "type": { - "title": "Type", - "pattern": "^links$", "type": "string", + "pattern": "^links$", + "title": "Type", "default": "links" } }, + "type": "object", + "required": [ + "id", + "type" + ], + "title": "RelatedLinksResource", "description": "A related Links resource object" }, "RelationshipLinks": { - "title": "RelationshipLinks", - "type": "object", "properties": { "self": { - "title": "Self", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Self", "description": "A link for the relationship itself (a 'relationship link').\nThis link allows the client to directly manipulate the relationship.\nWhen fetched successfully, this link returns the [linkage](https://jsonapi.org/format/1.0/#document-resource-object-linkage) for the related resources as its primary data.\n(See [Fetching Relationships](https://jsonapi.org/format/1.0/#fetching-relationships).)" }, "related": { - "title": "Related", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Related", "description": "A [related resource link](https://jsonapi.org/format/1.0/#document-resource-object-related-resource-links)." } }, + "type": "object", + "title": "RelationshipLinks", "description": "A resource object **MAY** contain references to other resource objects (\"relationships\").\nRelationships may be to-one or to-many.\nRelationships can be specified by including a member in a resource's links object." }, "Relationships": { - "title": "Relationships", - "type": "object", "properties": {}, + "type": "object", + "title": "Relationships", "description": "Members of the relationships object (\"relationships\") represent references from the resource object in which it's defined to other resource objects.\nKeys MUST NOT be:\n type\n id" }, "Resource": { - "title": "Resource", - "required": [ - "id", - "type" - ], - "type": "object", "properties": { "id": { - "title": "Id", "type": "string", + "title": "Id", "description": "Resource ID" }, "type": { - "title": "Type", "type": "string", + "title": "Type", "description": "Resource type" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ResourceLinks" } ], + "title": "Links", "description": "a links object containing links related to the resource." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship." }, "attributes": { - "title": "Attributes", "allOf": [ { "$ref": "#/components/schemas/Attributes" } ], + "title": "Attributes", "description": "an attributes object representing some of the resource\u2019s data." }, "relationships": { - "title": "Relationships", "allOf": [ { "$ref": "#/components/schemas/Relationships" } ], + "title": "Relationships", "description": "[Relationships object](https://jsonapi.org/format/1.0/#document-resource-object-relationships)\ndescribing relationships between the resource and other JSON API resources." } }, + "type": "object", + "required": [ + "id", + "type" + ], + "title": "Resource", "description": "Resource objects appear in a JSON API document to represent resources." }, "ResourceLinks": { - "title": "ResourceLinks", - "type": "object", "properties": { "self": { - "title": "Self", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Self", "description": "A link that identifies the resource represented by the resource object." } }, + "type": "object", + "title": "ResourceLinks", "description": "A Resource Links object" }, "ResponseMeta": { - "title": "ResponseMeta", - "required": [ - "query", - "api_version", - "more_data_available" - ], - "type": "object", "properties": { "query": { - "title": "Query", "allOf": [ { "$ref": "#/components/schemas/ResponseMetaQuery" } ], + "title": "Query", "description": "Information on the Query that was requested" }, "api_version": { - "title": "Api Version", - "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", "type": "string", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", + "title": "Api Version", "description": "Presently used full version of the OPTIMADE API.\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\nExamples: `1.0.0`, `1.0.0-rc.2`.", "example": [ "0.10.1", @@ -1697,298 +1690,305 @@ ] }, "more_data_available": { - "title": "More Data Available", "type": "boolean", + "title": "More Data Available", "description": "`false` if the response contains all data for the request (e.g., a request issued to a single entry endpoint, or a `filter` query at the last page of a paginated response) and `true` if the response is incomplete in the sense that multiple objects match the request, and not all of them have been included in the response (e.g., a query with multiple pages that is not at the last page)." }, "schema": { - "title": "Schema", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Schema", "description": "A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) that points to a schema for the response.\nIf it is a string, or a dictionary containing no `meta` field, the provided URL MUST point at an [OpenAPI](https://swagger.io/specification/) schema.\nIt is possible that future versions of this specification allows for alternative schema types.\nHence, if the `meta` field of the JSON API links object is provided and contains a field `schema_type` that is not equal to the string `OpenAPI` the client MUST not handle failures to parse the schema or to validate the response against the schema as errors." }, "time_stamp": { - "title": "Time Stamp", "type": "string", - "description": "A timestamp containing the date and time at which the query was executed.", - "format": "date-time" + "format": "date-time", + "title": "Time Stamp", + "description": "A timestamp containing the date and time at which the query was executed." }, "data_returned": { - "title": "Data Returned", - "minimum": 0.0, "type": "integer", + "minimum": 0.0, + "title": "Data Returned", "description": "An integer containing the total number of data resource objects returned for the current `filter` query, independent of pagination." }, "provider": { - "title": "Provider", "allOf": [ { "$ref": "#/components/schemas/Provider" } ], + "title": "Provider", "description": "information on the database provider of the implementation." }, "data_available": { - "title": "Data Available", "type": "integer", + "title": "Data Available", "description": "An integer containing the total number of data resource objects available in the database for the endpoint." }, "last_id": { - "title": "Last Id", "type": "string", + "title": "Last Id", "description": "a string containing the last ID returned" }, "response_message": { - "title": "Response Message", "type": "string", + "title": "Response Message", "description": "response string from the server" }, "implementation": { - "title": "Implementation", "allOf": [ { "$ref": "#/components/schemas/Implementation" } ], + "title": "Implementation", "description": "a dictionary describing the server implementation" }, "warnings": { - "title": "Warnings", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/Warnings" }, + "type": "array", + "uniqueItems": true, + "title": "Warnings", "description": "A list of warning resource objects representing non-critical errors or warnings.\nA warning resource object is defined similarly to a [JSON API error object](http://jsonapi.org/format/1.0/#error-objects), but MUST also include the field `type`, which MUST have the value `\"warning\"`.\nThe field `detail` MUST be present and SHOULD contain a non-critical message, e.g., reporting unrecognized search attributes or deprecated features.\nThe field `status`, representing a HTTP response status code, MUST NOT be present for a warning resource object.\nThis is an exclusive field for error resource objects." } }, + "type": "object", + "required": [ + "query", + "api_version", + "more_data_available" + ], + "title": "ResponseMeta", "description": "A [JSON API meta member](https://jsonapi.org/format/1.0#document-meta)\nthat contains JSON API meta objects of non-standard\nmeta-information.\n\nOPTIONAL additional information global to the query that is not\nspecified in this document, MUST start with a\ndatabase-provider-specific prefix." }, "ResponseMetaQuery": { - "title": "ResponseMetaQuery", - "required": [ - "representation" - ], - "type": "object", "properties": { "representation": { - "title": "Representation", "type": "string", + "title": "Representation", "description": "A string with the part of the URL following the versioned or unversioned base URL that serves the API.\nQuery parameters that have not been used in processing the request MAY be omitted.\nIn particular, if no query parameters have been involved in processing the request, the query part of the URL MAY be excluded.\nExample: `/structures?filter=nelements=2`" } }, + "type": "object", + "required": [ + "representation" + ], + "title": "ResponseMetaQuery", "description": "Information on the query that was requested." }, "StructureRelationship": { - "title": "StructureRelationship", - "type": "object", "properties": { "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/RelationshipLinks" } ], + "title": "Links", "description": "a links object containing at least one of the following: self, related" }, "data": { - "title": "Data", - "uniqueItems": true, "anyOf": [ { "$ref": "#/components/schemas/BaseRelationshipResource" }, { - "type": "array", "items": { "$ref": "#/components/schemas/BaseRelationshipResource" - } + }, + "type": "array" } ], + "uniqueItems": true, + "title": "Data", "description": "Resource linkage" }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "a meta object that contains non-standard meta-information about the relationship." } }, + "type": "object", + "title": "StructureRelationship", "description": "Similar to normal JSON API relationship, but with addition of OPTIONAL meta field for a resource." }, "ToplevelLinks": { - "title": "ToplevelLinks", - "type": "object", "properties": { "self": { - "title": "Self", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Self", "description": "A link to itself" }, "related": { - "title": "Related", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Related", "description": "A related resource link" }, "first": { - "title": "First", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "First", "description": "The first page of data" }, "last": { - "title": "Last", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Last", "description": "The last page of data" }, "prev": { - "title": "Prev", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Prev", "description": "The previous page of data" }, "next": { - "title": "Next", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Next", "description": "The next page of data" } }, + "type": "object", + "title": "ToplevelLinks", "description": "A set of Links objects, possibly including pagination" }, "Warnings": { - "title": "Warnings", - "required": [ - "detail", - "type" - ], - "type": "object", "properties": { "id": { - "title": "Id", "type": "string", + "title": "Id", "description": "A unique identifier for this particular occurrence of the problem." }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ErrorLinks" } ], + "title": "Links", "description": "A links object storing about" }, "code": { - "title": "Code", "type": "string", + "title": "Code", "description": "an application-specific error code, expressed as a string value." }, "title": { - "title": "Title", "type": "string", + "title": "Title", "description": "A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization." }, "detail": { - "title": "Detail", "type": "string", + "title": "Detail", "description": "A human-readable explanation specific to this occurrence of the problem." }, "source": { - "title": "Source", "allOf": [ { "$ref": "#/components/schemas/ErrorSource" } ], + "title": "Source", "description": "An object containing references to the source of the error" }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "a meta object containing non-standard meta-information about the error." }, "type": { - "title": "Type", - "pattern": "^warning$", "type": "string", + "pattern": "^warning$", + "title": "Type", "description": "Warnings must be of type \"warning\"", "default": "warning" } }, + "type": "object", + "required": [ + "detail", + "type" + ], + "title": "Warnings", "description": "OPTIMADE-specific warning class based on OPTIMADE-specific JSON API Error.\n\nFrom the specification:\n\nA warning resource object is defined similarly to a JSON API error object, but MUST also include the field type, which MUST have the value \"warning\".\nThe field detail MUST be present and SHOULD contain a non-critical message, e.g., reporting unrecognized search attributes or deprecated features.\n\nNote: Must be named \"Warnings\", since \"Warning\" is a built-in Python class." } } diff --git a/openapi/openapi.json b/openapi/openapi.json index a78f70ca8..0f61f4d90 100644 --- a/openapi/openapi.json +++ b/openapi/openapi.json @@ -1,5 +1,5 @@ { - "openapi": "3.0.2", + "openapi": "3.1.0", "info": { "title": "OPTIMADE API", "description": "The [Open Databases Integration for Materials Design (OPTIMADE) consortium](https://www.optimade.org/) aims to make materials databases interoperational by developing a common REST API.\n\nThis specification is generated using [`optimade-python-tools`](https://github.com/Materials-Consortia/optimade-python-tools/tree/v0.25.2) v0.25.2.", @@ -108,8 +108,8 @@ { "required": true, "schema": { - "title": "Entry", - "type": "string" + "type": "string", + "title": "Entry" }, "name": "entry", "in": "path" @@ -211,8 +211,8 @@ "description": "A filter string, in the format described in section API Filtering Format Specification of the specification.", "required": false, "schema": { - "title": "Filter", "type": "string", + "title": "Filter", "description": "A filter string, in the format described in section API Filtering Format Specification of the specification.", "default": "" }, @@ -223,8 +223,8 @@ "description": "The output format requested (see section Response Format).\nDefaults to the format string 'json', which specifies the standard output format described in this specification.\nExample: `http://example.com/v1/structures?response_format=xml`", "required": false, "schema": { - "title": "Response Format", "type": "string", + "title": "Response Format", "description": "The output format requested (see section Response Format).\nDefaults to the format string 'json', which specifies the standard output format described in this specification.\nExample: `http://example.com/v1/structures?response_format=xml`", "default": "json" }, @@ -235,10 +235,10 @@ "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "required": false, "schema": { - "title": "Email Address", "type": "string", - "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "format": "email", + "title": "Email Address", + "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "default": "" }, "name": "email_address", @@ -248,9 +248,9 @@ "description": "A comma-delimited set of fields to be provided in the output.\nIf provided, these fields MUST be returned along with the REQUIRED fields.\nOther OPTIONAL fields MUST NOT be returned when this parameter is present.\nExample: `http://example.com/v1/structures?response_fields=last_modified,nsites`", "required": false, "schema": { - "title": "Response Fields", - "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", "type": "string", + "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", + "title": "Response Fields", "description": "A comma-delimited set of fields to be provided in the output.\nIf provided, these fields MUST be returned along with the REQUIRED fields.\nOther OPTIONAL fields MUST NOT be returned when this parameter is present.\nExample: `http://example.com/v1/structures?response_fields=last_modified,nsites`", "default": "" }, @@ -261,9 +261,9 @@ "description": "If supporting sortable queries, an implementation MUST use the `sort` query parameter with format as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-sorting).\n\nAn implementation MAY support multiple sort fields for a single query.\nIf it does, it again MUST conform to the JSON API 1.0 specification.\n\nIf an implementation supports sorting for an entry listing endpoint, then the `/info/` endpoint MUST include, for each field name `` in its `data.properties.` response value that can be used for sorting, the key `sortable` with value `true`.\nIf a field name under an entry listing endpoint supporting sorting cannot be used for sorting, the server MUST either leave out the `sortable` key or set it equal to `false` for the specific field name.\nThe set of field names, with `sortable` equal to `true` are allowed to be used in the \"sort fields\" list according to its definition in the JSON API 1.0 specification.\nThe field `sortable` is in addition to each property description and other OPTIONAL fields.\nAn example is shown in the section Entry Listing Info Endpoints.", "required": false, "schema": { - "title": "Sort", - "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", "type": "string", + "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", + "title": "Sort", "description": "If supporting sortable queries, an implementation MUST use the `sort` query parameter with format as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-sorting).\n\nAn implementation MAY support multiple sort fields for a single query.\nIf it does, it again MUST conform to the JSON API 1.0 specification.\n\nIf an implementation supports sorting for an entry listing endpoint, then the `/info/` endpoint MUST include, for each field name `` in its `data.properties.` response value that can be used for sorting, the key `sortable` with value `true`.\nIf a field name under an entry listing endpoint supporting sorting cannot be used for sorting, the server MUST either leave out the `sortable` key or set it equal to `false` for the specific field name.\nThe set of field names, with `sortable` equal to `true` are allowed to be used in the \"sort fields\" list according to its definition in the JSON API 1.0 specification.\nThe field `sortable` is in addition to each property description and other OPTIONAL fields.\nAn example is shown in the section Entry Listing Info Endpoints.", "default": "" }, @@ -274,9 +274,9 @@ "description": "Sets a numerical limit on the number of entries returned.\nSee [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-pagination).\nThe API implementation MUST return no more than the number specified.\nIt MAY return fewer.\nThe database MAY have a maximum limit and not accept larger numbers (in which case an error code -- 403 Forbidden -- MUST be returned).\nThe default limit value is up to the API implementation to decide.\nExample: `http://example.com/optimade/v1/structures?page_limit=100`", "required": false, "schema": { - "title": "Page Limit", - "minimum": 0.0, "type": "integer", + "minimum": 0.0, + "title": "Page Limit", "description": "Sets a numerical limit on the number of entries returned.\nSee [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-pagination).\nThe API implementation MUST return no more than the number specified.\nIt MAY return fewer.\nThe database MAY have a maximum limit and not accept larger numbers (in which case an error code -- 403 Forbidden -- MUST be returned).\nThe default limit value is up to the API implementation to decide.\nExample: `http://example.com/optimade/v1/structures?page_limit=100`", "default": 20 }, @@ -287,9 +287,9 @@ "description": "RECOMMENDED for use with _offset-based_ pagination: using `page_offset` and `page_limit` is RECOMMENDED.\nExample: Skip 50 structures and fetch up to 100: `/structures?page_offset=50&page_limit=100`.", "required": false, "schema": { - "title": "Page Offset", - "minimum": 0.0, "type": "integer", + "minimum": 0.0, + "title": "Page Offset", "description": "RECOMMENDED for use with _offset-based_ pagination: using `page_offset` and `page_limit` is RECOMMENDED.\nExample: Skip 50 structures and fetch up to 100: `/structures?page_offset=50&page_limit=100`.", "default": 0 }, @@ -300,8 +300,8 @@ "description": "RECOMMENDED for use with _page-based_ pagination: using `page_number` and `page_limit` is RECOMMENDED.\nIt is RECOMMENDED that the first page has number 1, i.e., that `page_number` is 1-based.\nExample: Fetch page 2 of up to 50 structures per page: `/structures?page_number=2&page_limit=50`.", "required": false, "schema": { - "title": "Page Number", "type": "integer", + "title": "Page Number", "description": "RECOMMENDED for use with _page-based_ pagination: using `page_number` and `page_limit` is RECOMMENDED.\nIt is RECOMMENDED that the first page has number 1, i.e., that `page_number` is 1-based.\nExample: Fetch page 2 of up to 50 structures per page: `/structures?page_number=2&page_limit=50`." }, "name": "page_number", @@ -311,9 +311,9 @@ "description": "RECOMMENDED for use with _cursor-based_ pagination: using `page_cursor` and `page_limit` is RECOMMENDED.", "required": false, "schema": { - "title": "Page Cursor", - "minimum": 0.0, "type": "integer", + "minimum": 0.0, + "title": "Page Cursor", "description": "RECOMMENDED for use with _cursor-based_ pagination: using `page_cursor` and `page_limit` is RECOMMENDED.", "default": 0 }, @@ -324,8 +324,8 @@ "description": "RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED.\nExample: Fetch up to 100 structures above sort-field value 4000 (in this example, server chooses to fetch results sorted by increasing `id`, so `page_above` value refers to an `id` value): `/structures?page_above=4000&page_limit=100`.", "required": false, "schema": { - "title": "Page Above", "type": "string", + "title": "Page Above", "description": "RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED.\nExample: Fetch up to 100 structures above sort-field value 4000 (in this example, server chooses to fetch results sorted by increasing `id`, so `page_above` value refers to an `id` value): `/structures?page_above=4000&page_limit=100`." }, "name": "page_above", @@ -335,8 +335,8 @@ "description": "RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED.", "required": false, "schema": { - "title": "Page Below", "type": "string", + "title": "Page Below", "description": "RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED." }, "name": "page_below", @@ -346,8 +346,8 @@ "description": "A server MAY implement the JSON API concept of returning [compound documents](https://jsonapi.org/format/1.0/#document-compound-documents) by utilizing the `include` query parameter as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-includes).\n\nAll related resource objects MUST be returned as part of an array value for the top-level `included` field, see the section JSON Response Schema: Common Fields.\n\nThe value of `include` MUST be a comma-separated list of \"relationship paths\", as defined in the [JSON API](https://jsonapi.org/format/1.0/#fetching-includes).\nIf relationship paths are not supported, or a server is unable to identify a relationship path a `400 Bad Request` response MUST be made.\n\nThe **default value** for `include` is `references`.\nThis means `references` entries MUST always be included under the top-level field `included` as default, since a server assumes if `include` is not specified by a client in the request, it is still specified as `include=references`.\nNote, if a client explicitly specifies `include` and leaves out `references`, `references` resource objects MUST NOT be included under the top-level field `included`, as per the definition of `included`, see section JSON Response Schema: Common Fields.\n\n> **Note**: A query with the parameter `include` set to the empty string means no related resource objects are to be returned under the top-level field `included`.", "required": false, "schema": { - "title": "Include", "type": "string", + "title": "Include", "description": "A server MAY implement the JSON API concept of returning [compound documents](https://jsonapi.org/format/1.0/#document-compound-documents) by utilizing the `include` query parameter as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-includes).\n\nAll related resource objects MUST be returned as part of an array value for the top-level `included` field, see the section JSON Response Schema: Common Fields.\n\nThe value of `include` MUST be a comma-separated list of \"relationship paths\", as defined in the [JSON API](https://jsonapi.org/format/1.0/#fetching-includes).\nIf relationship paths are not supported, or a server is unable to identify a relationship path a `400 Bad Request` response MUST be made.\n\nThe **default value** for `include` is `references`.\nThis means `references` entries MUST always be included under the top-level field `included` as default, since a server assumes if `include` is not specified by a client in the request, it is still specified as `include=references`.\nNote, if a client explicitly specifies `include` and leaves out `references`, `references` resource objects MUST NOT be included under the top-level field `included`, as per the definition of `included`, see section JSON Response Schema: Common Fields.\n\n> **Note**: A query with the parameter `include` set to the empty string means no related resource objects are to be returned under the top-level field `included`.", "default": "references" }, @@ -358,9 +358,9 @@ "description": "If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.", "required": false, "schema": { - "title": "Api Hint", - "pattern": "(v[0-9]+(\\.[0-9]+)?)?", "type": "string", + "pattern": "(v[0-9]+(\\.[0-9]+)?)?", + "title": "Api Hint", "description": "If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.", "default": "" }, @@ -464,8 +464,8 @@ "description": "A filter string, in the format described in section API Filtering Format Specification of the specification.", "required": false, "schema": { - "title": "Filter", "type": "string", + "title": "Filter", "description": "A filter string, in the format described in section API Filtering Format Specification of the specification.", "default": "" }, @@ -476,8 +476,8 @@ "description": "The output format requested (see section Response Format).\nDefaults to the format string 'json', which specifies the standard output format described in this specification.\nExample: `http://example.com/v1/structures?response_format=xml`", "required": false, "schema": { - "title": "Response Format", "type": "string", + "title": "Response Format", "description": "The output format requested (see section Response Format).\nDefaults to the format string 'json', which specifies the standard output format described in this specification.\nExample: `http://example.com/v1/structures?response_format=xml`", "default": "json" }, @@ -488,10 +488,10 @@ "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "required": false, "schema": { - "title": "Email Address", "type": "string", - "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "format": "email", + "title": "Email Address", + "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "default": "" }, "name": "email_address", @@ -501,9 +501,9 @@ "description": "A comma-delimited set of fields to be provided in the output.\nIf provided, these fields MUST be returned along with the REQUIRED fields.\nOther OPTIONAL fields MUST NOT be returned when this parameter is present.\nExample: `http://example.com/v1/structures?response_fields=last_modified,nsites`", "required": false, "schema": { - "title": "Response Fields", - "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", "type": "string", + "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", + "title": "Response Fields", "description": "A comma-delimited set of fields to be provided in the output.\nIf provided, these fields MUST be returned along with the REQUIRED fields.\nOther OPTIONAL fields MUST NOT be returned when this parameter is present.\nExample: `http://example.com/v1/structures?response_fields=last_modified,nsites`", "default": "" }, @@ -514,9 +514,9 @@ "description": "If supporting sortable queries, an implementation MUST use the `sort` query parameter with format as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-sorting).\n\nAn implementation MAY support multiple sort fields for a single query.\nIf it does, it again MUST conform to the JSON API 1.0 specification.\n\nIf an implementation supports sorting for an entry listing endpoint, then the `/info/` endpoint MUST include, for each field name `` in its `data.properties.` response value that can be used for sorting, the key `sortable` with value `true`.\nIf a field name under an entry listing endpoint supporting sorting cannot be used for sorting, the server MUST either leave out the `sortable` key or set it equal to `false` for the specific field name.\nThe set of field names, with `sortable` equal to `true` are allowed to be used in the \"sort fields\" list according to its definition in the JSON API 1.0 specification.\nThe field `sortable` is in addition to each property description and other OPTIONAL fields.\nAn example is shown in the section Entry Listing Info Endpoints.", "required": false, "schema": { - "title": "Sort", - "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", "type": "string", + "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", + "title": "Sort", "description": "If supporting sortable queries, an implementation MUST use the `sort` query parameter with format as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-sorting).\n\nAn implementation MAY support multiple sort fields for a single query.\nIf it does, it again MUST conform to the JSON API 1.0 specification.\n\nIf an implementation supports sorting for an entry listing endpoint, then the `/info/` endpoint MUST include, for each field name `` in its `data.properties.` response value that can be used for sorting, the key `sortable` with value `true`.\nIf a field name under an entry listing endpoint supporting sorting cannot be used for sorting, the server MUST either leave out the `sortable` key or set it equal to `false` for the specific field name.\nThe set of field names, with `sortable` equal to `true` are allowed to be used in the \"sort fields\" list according to its definition in the JSON API 1.0 specification.\nThe field `sortable` is in addition to each property description and other OPTIONAL fields.\nAn example is shown in the section Entry Listing Info Endpoints.", "default": "" }, @@ -527,9 +527,9 @@ "description": "Sets a numerical limit on the number of entries returned.\nSee [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-pagination).\nThe API implementation MUST return no more than the number specified.\nIt MAY return fewer.\nThe database MAY have a maximum limit and not accept larger numbers (in which case an error code -- 403 Forbidden -- MUST be returned).\nThe default limit value is up to the API implementation to decide.\nExample: `http://example.com/optimade/v1/structures?page_limit=100`", "required": false, "schema": { - "title": "Page Limit", - "minimum": 0.0, "type": "integer", + "minimum": 0.0, + "title": "Page Limit", "description": "Sets a numerical limit on the number of entries returned.\nSee [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-pagination).\nThe API implementation MUST return no more than the number specified.\nIt MAY return fewer.\nThe database MAY have a maximum limit and not accept larger numbers (in which case an error code -- 403 Forbidden -- MUST be returned).\nThe default limit value is up to the API implementation to decide.\nExample: `http://example.com/optimade/v1/structures?page_limit=100`", "default": 20 }, @@ -540,9 +540,9 @@ "description": "RECOMMENDED for use with _offset-based_ pagination: using `page_offset` and `page_limit` is RECOMMENDED.\nExample: Skip 50 structures and fetch up to 100: `/structures?page_offset=50&page_limit=100`.", "required": false, "schema": { - "title": "Page Offset", - "minimum": 0.0, "type": "integer", + "minimum": 0.0, + "title": "Page Offset", "description": "RECOMMENDED for use with _offset-based_ pagination: using `page_offset` and `page_limit` is RECOMMENDED.\nExample: Skip 50 structures and fetch up to 100: `/structures?page_offset=50&page_limit=100`.", "default": 0 }, @@ -553,8 +553,8 @@ "description": "RECOMMENDED for use with _page-based_ pagination: using `page_number` and `page_limit` is RECOMMENDED.\nIt is RECOMMENDED that the first page has number 1, i.e., that `page_number` is 1-based.\nExample: Fetch page 2 of up to 50 structures per page: `/structures?page_number=2&page_limit=50`.", "required": false, "schema": { - "title": "Page Number", "type": "integer", + "title": "Page Number", "description": "RECOMMENDED for use with _page-based_ pagination: using `page_number` and `page_limit` is RECOMMENDED.\nIt is RECOMMENDED that the first page has number 1, i.e., that `page_number` is 1-based.\nExample: Fetch page 2 of up to 50 structures per page: `/structures?page_number=2&page_limit=50`." }, "name": "page_number", @@ -564,9 +564,9 @@ "description": "RECOMMENDED for use with _cursor-based_ pagination: using `page_cursor` and `page_limit` is RECOMMENDED.", "required": false, "schema": { - "title": "Page Cursor", - "minimum": 0.0, "type": "integer", + "minimum": 0.0, + "title": "Page Cursor", "description": "RECOMMENDED for use with _cursor-based_ pagination: using `page_cursor` and `page_limit` is RECOMMENDED.", "default": 0 }, @@ -577,8 +577,8 @@ "description": "RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED.\nExample: Fetch up to 100 structures above sort-field value 4000 (in this example, server chooses to fetch results sorted by increasing `id`, so `page_above` value refers to an `id` value): `/structures?page_above=4000&page_limit=100`.", "required": false, "schema": { - "title": "Page Above", "type": "string", + "title": "Page Above", "description": "RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED.\nExample: Fetch up to 100 structures above sort-field value 4000 (in this example, server chooses to fetch results sorted by increasing `id`, so `page_above` value refers to an `id` value): `/structures?page_above=4000&page_limit=100`." }, "name": "page_above", @@ -588,8 +588,8 @@ "description": "RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED.", "required": false, "schema": { - "title": "Page Below", "type": "string", + "title": "Page Below", "description": "RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED." }, "name": "page_below", @@ -599,8 +599,8 @@ "description": "A server MAY implement the JSON API concept of returning [compound documents](https://jsonapi.org/format/1.0/#document-compound-documents) by utilizing the `include` query parameter as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-includes).\n\nAll related resource objects MUST be returned as part of an array value for the top-level `included` field, see the section JSON Response Schema: Common Fields.\n\nThe value of `include` MUST be a comma-separated list of \"relationship paths\", as defined in the [JSON API](https://jsonapi.org/format/1.0/#fetching-includes).\nIf relationship paths are not supported, or a server is unable to identify a relationship path a `400 Bad Request` response MUST be made.\n\nThe **default value** for `include` is `references`.\nThis means `references` entries MUST always be included under the top-level field `included` as default, since a server assumes if `include` is not specified by a client in the request, it is still specified as `include=references`.\nNote, if a client explicitly specifies `include` and leaves out `references`, `references` resource objects MUST NOT be included under the top-level field `included`, as per the definition of `included`, see section JSON Response Schema: Common Fields.\n\n> **Note**: A query with the parameter `include` set to the empty string means no related resource objects are to be returned under the top-level field `included`.", "required": false, "schema": { - "title": "Include", "type": "string", + "title": "Include", "description": "A server MAY implement the JSON API concept of returning [compound documents](https://jsonapi.org/format/1.0/#document-compound-documents) by utilizing the `include` query parameter as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-includes).\n\nAll related resource objects MUST be returned as part of an array value for the top-level `included` field, see the section JSON Response Schema: Common Fields.\n\nThe value of `include` MUST be a comma-separated list of \"relationship paths\", as defined in the [JSON API](https://jsonapi.org/format/1.0/#fetching-includes).\nIf relationship paths are not supported, or a server is unable to identify a relationship path a `400 Bad Request` response MUST be made.\n\nThe **default value** for `include` is `references`.\nThis means `references` entries MUST always be included under the top-level field `included` as default, since a server assumes if `include` is not specified by a client in the request, it is still specified as `include=references`.\nNote, if a client explicitly specifies `include` and leaves out `references`, `references` resource objects MUST NOT be included under the top-level field `included`, as per the definition of `included`, see section JSON Response Schema: Common Fields.\n\n> **Note**: A query with the parameter `include` set to the empty string means no related resource objects are to be returned under the top-level field `included`.", "default": "references" }, @@ -611,9 +611,9 @@ "description": "If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.", "required": false, "schema": { - "title": "Api Hint", - "pattern": "(v[0-9]+(\\.[0-9]+)?)?", "type": "string", + "pattern": "(v[0-9]+(\\.[0-9]+)?)?", + "title": "Api Hint", "description": "If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.", "default": "" }, @@ -716,8 +716,8 @@ { "required": true, "schema": { - "title": "Entry Id", - "type": "string" + "type": "string", + "title": "Entry Id" }, "name": "entry_id", "in": "path" @@ -726,8 +726,8 @@ "description": "The output format requested (see section Response Format).\nDefaults to the format string 'json', which specifies the standard output format described in this specification.\nExample: `http://example.com/v1/structures?response_format=xml`", "required": false, "schema": { - "title": "Response Format", "type": "string", + "title": "Response Format", "description": "The output format requested (see section Response Format).\nDefaults to the format string 'json', which specifies the standard output format described in this specification.\nExample: `http://example.com/v1/structures?response_format=xml`", "default": "json" }, @@ -738,10 +738,10 @@ "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "required": false, "schema": { - "title": "Email Address", "type": "string", - "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "format": "email", + "title": "Email Address", + "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "default": "" }, "name": "email_address", @@ -751,9 +751,9 @@ "description": "A comma-delimited set of fields to be provided in the output.\nIf provided, these fields MUST be returned along with the REQUIRED fields.\nOther OPTIONAL fields MUST NOT be returned when this parameter is present.\nExample: `http://example.com/v1/structures?response_fields=last_modified,nsites`", "required": false, "schema": { - "title": "Response Fields", - "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", "type": "string", + "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", + "title": "Response Fields", "description": "A comma-delimited set of fields to be provided in the output.\nIf provided, these fields MUST be returned along with the REQUIRED fields.\nOther OPTIONAL fields MUST NOT be returned when this parameter is present.\nExample: `http://example.com/v1/structures?response_fields=last_modified,nsites`", "default": "" }, @@ -764,8 +764,8 @@ "description": "A server MAY implement the JSON API concept of returning [compound documents](https://jsonapi.org/format/1.0/#document-compound-documents) by utilizing the `include` query parameter as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-includes).\n\nAll related resource objects MUST be returned as part of an array value for the top-level `included` field, see the section JSON Response Schema: Common Fields.\n\nThe value of `include` MUST be a comma-separated list of \"relationship paths\", as defined in the [JSON API](https://jsonapi.org/format/1.0/#fetching-includes).\nIf relationship paths are not supported, or a server is unable to identify a relationship path a `400 Bad Request` response MUST be made.\n\nThe **default value** for `include` is `references`.\nThis means `references` entries MUST always be included under the top-level field `included` as default, since a server assumes if `include` is not specified by a client in the request, it is still specified as `include=references`.\nNote, if a client explicitly specifies `include` and leaves out `references`, `references` resource objects MUST NOT be included under the top-level field `included`, as per the definition of `included`, see section JSON Response Schema: Common Fields.\n\n> **Note**: A query with the parameter `include` set to the empty string means no related resource objects are to be returned under the top-level field `included`.", "required": false, "schema": { - "title": "Include", "type": "string", + "title": "Include", "description": "A server MAY implement the JSON API concept of returning [compound documents](https://jsonapi.org/format/1.0/#document-compound-documents) by utilizing the `include` query parameter as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-includes).\n\nAll related resource objects MUST be returned as part of an array value for the top-level `included` field, see the section JSON Response Schema: Common Fields.\n\nThe value of `include` MUST be a comma-separated list of \"relationship paths\", as defined in the [JSON API](https://jsonapi.org/format/1.0/#fetching-includes).\nIf relationship paths are not supported, or a server is unable to identify a relationship path a `400 Bad Request` response MUST be made.\n\nThe **default value** for `include` is `references`.\nThis means `references` entries MUST always be included under the top-level field `included` as default, since a server assumes if `include` is not specified by a client in the request, it is still specified as `include=references`.\nNote, if a client explicitly specifies `include` and leaves out `references`, `references` resource objects MUST NOT be included under the top-level field `included`, as per the definition of `included`, see section JSON Response Schema: Common Fields.\n\n> **Note**: A query with the parameter `include` set to the empty string means no related resource objects are to be returned under the top-level field `included`.", "default": "references" }, @@ -776,9 +776,9 @@ "description": "If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.", "required": false, "schema": { - "title": "Api Hint", - "pattern": "(v[0-9]+(\\.[0-9]+)?)?", "type": "string", + "pattern": "(v[0-9]+(\\.[0-9]+)?)?", + "title": "Api Hint", "description": "If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.", "default": "" }, @@ -789,8 +789,8 @@ "description": "A list of lists which contains a range for each dimension of the property.", "required": false, "schema": { - "title": "Property Ranges", "type": "string", + "title": "Property Ranges", "description": "A list of lists which contains a range for each dimension of the property." }, "name": "property_ranges", @@ -893,8 +893,8 @@ "description": "A filter string, in the format described in section API Filtering Format Specification of the specification.", "required": false, "schema": { - "title": "Filter", "type": "string", + "title": "Filter", "description": "A filter string, in the format described in section API Filtering Format Specification of the specification.", "default": "" }, @@ -905,8 +905,8 @@ "description": "The output format requested (see section Response Format).\nDefaults to the format string 'json', which specifies the standard output format described in this specification.\nExample: `http://example.com/v1/structures?response_format=xml`", "required": false, "schema": { - "title": "Response Format", "type": "string", + "title": "Response Format", "description": "The output format requested (see section Response Format).\nDefaults to the format string 'json', which specifies the standard output format described in this specification.\nExample: `http://example.com/v1/structures?response_format=xml`", "default": "json" }, @@ -917,10 +917,10 @@ "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "required": false, "schema": { - "title": "Email Address", "type": "string", - "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "format": "email", + "title": "Email Address", + "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "default": "" }, "name": "email_address", @@ -930,9 +930,9 @@ "description": "A comma-delimited set of fields to be provided in the output.\nIf provided, these fields MUST be returned along with the REQUIRED fields.\nOther OPTIONAL fields MUST NOT be returned when this parameter is present.\nExample: `http://example.com/v1/structures?response_fields=last_modified,nsites`", "required": false, "schema": { - "title": "Response Fields", - "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", "type": "string", + "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", + "title": "Response Fields", "description": "A comma-delimited set of fields to be provided in the output.\nIf provided, these fields MUST be returned along with the REQUIRED fields.\nOther OPTIONAL fields MUST NOT be returned when this parameter is present.\nExample: `http://example.com/v1/structures?response_fields=last_modified,nsites`", "default": "" }, @@ -943,9 +943,9 @@ "description": "If supporting sortable queries, an implementation MUST use the `sort` query parameter with format as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-sorting).\n\nAn implementation MAY support multiple sort fields for a single query.\nIf it does, it again MUST conform to the JSON API 1.0 specification.\n\nIf an implementation supports sorting for an entry listing endpoint, then the `/info/` endpoint MUST include, for each field name `` in its `data.properties.` response value that can be used for sorting, the key `sortable` with value `true`.\nIf a field name under an entry listing endpoint supporting sorting cannot be used for sorting, the server MUST either leave out the `sortable` key or set it equal to `false` for the specific field name.\nThe set of field names, with `sortable` equal to `true` are allowed to be used in the \"sort fields\" list according to its definition in the JSON API 1.0 specification.\nThe field `sortable` is in addition to each property description and other OPTIONAL fields.\nAn example is shown in the section Entry Listing Info Endpoints.", "required": false, "schema": { - "title": "Sort", - "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", "type": "string", + "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", + "title": "Sort", "description": "If supporting sortable queries, an implementation MUST use the `sort` query parameter with format as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-sorting).\n\nAn implementation MAY support multiple sort fields for a single query.\nIf it does, it again MUST conform to the JSON API 1.0 specification.\n\nIf an implementation supports sorting for an entry listing endpoint, then the `/info/` endpoint MUST include, for each field name `` in its `data.properties.` response value that can be used for sorting, the key `sortable` with value `true`.\nIf a field name under an entry listing endpoint supporting sorting cannot be used for sorting, the server MUST either leave out the `sortable` key or set it equal to `false` for the specific field name.\nThe set of field names, with `sortable` equal to `true` are allowed to be used in the \"sort fields\" list according to its definition in the JSON API 1.0 specification.\nThe field `sortable` is in addition to each property description and other OPTIONAL fields.\nAn example is shown in the section Entry Listing Info Endpoints.", "default": "" }, @@ -956,9 +956,9 @@ "description": "Sets a numerical limit on the number of entries returned.\nSee [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-pagination).\nThe API implementation MUST return no more than the number specified.\nIt MAY return fewer.\nThe database MAY have a maximum limit and not accept larger numbers (in which case an error code -- 403 Forbidden -- MUST be returned).\nThe default limit value is up to the API implementation to decide.\nExample: `http://example.com/optimade/v1/structures?page_limit=100`", "required": false, "schema": { - "title": "Page Limit", - "minimum": 0.0, "type": "integer", + "minimum": 0.0, + "title": "Page Limit", "description": "Sets a numerical limit on the number of entries returned.\nSee [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-pagination).\nThe API implementation MUST return no more than the number specified.\nIt MAY return fewer.\nThe database MAY have a maximum limit and not accept larger numbers (in which case an error code -- 403 Forbidden -- MUST be returned).\nThe default limit value is up to the API implementation to decide.\nExample: `http://example.com/optimade/v1/structures?page_limit=100`", "default": 20 }, @@ -969,9 +969,9 @@ "description": "RECOMMENDED for use with _offset-based_ pagination: using `page_offset` and `page_limit` is RECOMMENDED.\nExample: Skip 50 structures and fetch up to 100: `/structures?page_offset=50&page_limit=100`.", "required": false, "schema": { - "title": "Page Offset", - "minimum": 0.0, "type": "integer", + "minimum": 0.0, + "title": "Page Offset", "description": "RECOMMENDED for use with _offset-based_ pagination: using `page_offset` and `page_limit` is RECOMMENDED.\nExample: Skip 50 structures and fetch up to 100: `/structures?page_offset=50&page_limit=100`.", "default": 0 }, @@ -982,8 +982,8 @@ "description": "RECOMMENDED for use with _page-based_ pagination: using `page_number` and `page_limit` is RECOMMENDED.\nIt is RECOMMENDED that the first page has number 1, i.e., that `page_number` is 1-based.\nExample: Fetch page 2 of up to 50 structures per page: `/structures?page_number=2&page_limit=50`.", "required": false, "schema": { - "title": "Page Number", "type": "integer", + "title": "Page Number", "description": "RECOMMENDED for use with _page-based_ pagination: using `page_number` and `page_limit` is RECOMMENDED.\nIt is RECOMMENDED that the first page has number 1, i.e., that `page_number` is 1-based.\nExample: Fetch page 2 of up to 50 structures per page: `/structures?page_number=2&page_limit=50`." }, "name": "page_number", @@ -993,9 +993,9 @@ "description": "RECOMMENDED for use with _cursor-based_ pagination: using `page_cursor` and `page_limit` is RECOMMENDED.", "required": false, "schema": { - "title": "Page Cursor", - "minimum": 0.0, "type": "integer", + "minimum": 0.0, + "title": "Page Cursor", "description": "RECOMMENDED for use with _cursor-based_ pagination: using `page_cursor` and `page_limit` is RECOMMENDED.", "default": 0 }, @@ -1006,8 +1006,8 @@ "description": "RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED.\nExample: Fetch up to 100 structures above sort-field value 4000 (in this example, server chooses to fetch results sorted by increasing `id`, so `page_above` value refers to an `id` value): `/structures?page_above=4000&page_limit=100`.", "required": false, "schema": { - "title": "Page Above", "type": "string", + "title": "Page Above", "description": "RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED.\nExample: Fetch up to 100 structures above sort-field value 4000 (in this example, server chooses to fetch results sorted by increasing `id`, so `page_above` value refers to an `id` value): `/structures?page_above=4000&page_limit=100`." }, "name": "page_above", @@ -1017,8 +1017,8 @@ "description": "RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED.", "required": false, "schema": { - "title": "Page Below", "type": "string", + "title": "Page Below", "description": "RECOMMENDED for use with _value-based_ pagination: using `page_above`/`page_below` and `page_limit` is RECOMMENDED." }, "name": "page_below", @@ -1028,8 +1028,8 @@ "description": "A server MAY implement the JSON API concept of returning [compound documents](https://jsonapi.org/format/1.0/#document-compound-documents) by utilizing the `include` query parameter as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-includes).\n\nAll related resource objects MUST be returned as part of an array value for the top-level `included` field, see the section JSON Response Schema: Common Fields.\n\nThe value of `include` MUST be a comma-separated list of \"relationship paths\", as defined in the [JSON API](https://jsonapi.org/format/1.0/#fetching-includes).\nIf relationship paths are not supported, or a server is unable to identify a relationship path a `400 Bad Request` response MUST be made.\n\nThe **default value** for `include` is `references`.\nThis means `references` entries MUST always be included under the top-level field `included` as default, since a server assumes if `include` is not specified by a client in the request, it is still specified as `include=references`.\nNote, if a client explicitly specifies `include` and leaves out `references`, `references` resource objects MUST NOT be included under the top-level field `included`, as per the definition of `included`, see section JSON Response Schema: Common Fields.\n\n> **Note**: A query with the parameter `include` set to the empty string means no related resource objects are to be returned under the top-level field `included`.", "required": false, "schema": { - "title": "Include", "type": "string", + "title": "Include", "description": "A server MAY implement the JSON API concept of returning [compound documents](https://jsonapi.org/format/1.0/#document-compound-documents) by utilizing the `include` query parameter as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-includes).\n\nAll related resource objects MUST be returned as part of an array value for the top-level `included` field, see the section JSON Response Schema: Common Fields.\n\nThe value of `include` MUST be a comma-separated list of \"relationship paths\", as defined in the [JSON API](https://jsonapi.org/format/1.0/#fetching-includes).\nIf relationship paths are not supported, or a server is unable to identify a relationship path a `400 Bad Request` response MUST be made.\n\nThe **default value** for `include` is `references`.\nThis means `references` entries MUST always be included under the top-level field `included` as default, since a server assumes if `include` is not specified by a client in the request, it is still specified as `include=references`.\nNote, if a client explicitly specifies `include` and leaves out `references`, `references` resource objects MUST NOT be included under the top-level field `included`, as per the definition of `included`, see section JSON Response Schema: Common Fields.\n\n> **Note**: A query with the parameter `include` set to the empty string means no related resource objects are to be returned under the top-level field `included`.", "default": "references" }, @@ -1040,9 +1040,9 @@ "description": "If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.", "required": false, "schema": { - "title": "Api Hint", - "pattern": "(v[0-9]+(\\.[0-9]+)?)?", "type": "string", + "pattern": "(v[0-9]+(\\.[0-9]+)?)?", + "title": "Api Hint", "description": "If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.", "default": "" }, @@ -1145,8 +1145,8 @@ { "required": true, "schema": { - "title": "Entry Id", - "type": "string" + "type": "string", + "title": "Entry Id" }, "name": "entry_id", "in": "path" @@ -1155,8 +1155,8 @@ "description": "The output format requested (see section Response Format).\nDefaults to the format string 'json', which specifies the standard output format described in this specification.\nExample: `http://example.com/v1/structures?response_format=xml`", "required": false, "schema": { - "title": "Response Format", "type": "string", + "title": "Response Format", "description": "The output format requested (see section Response Format).\nDefaults to the format string 'json', which specifies the standard output format described in this specification.\nExample: `http://example.com/v1/structures?response_format=xml`", "default": "json" }, @@ -1167,10 +1167,10 @@ "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "required": false, "schema": { - "title": "Email Address", "type": "string", - "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "format": "email", + "title": "Email Address", + "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "default": "" }, "name": "email_address", @@ -1180,9 +1180,9 @@ "description": "A comma-delimited set of fields to be provided in the output.\nIf provided, these fields MUST be returned along with the REQUIRED fields.\nOther OPTIONAL fields MUST NOT be returned when this parameter is present.\nExample: `http://example.com/v1/structures?response_fields=last_modified,nsites`", "required": false, "schema": { - "title": "Response Fields", - "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", "type": "string", + "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", + "title": "Response Fields", "description": "A comma-delimited set of fields to be provided in the output.\nIf provided, these fields MUST be returned along with the REQUIRED fields.\nOther OPTIONAL fields MUST NOT be returned when this parameter is present.\nExample: `http://example.com/v1/structures?response_fields=last_modified,nsites`", "default": "" }, @@ -1193,8 +1193,8 @@ "description": "A server MAY implement the JSON API concept of returning [compound documents](https://jsonapi.org/format/1.0/#document-compound-documents) by utilizing the `include` query parameter as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-includes).\n\nAll related resource objects MUST be returned as part of an array value for the top-level `included` field, see the section JSON Response Schema: Common Fields.\n\nThe value of `include` MUST be a comma-separated list of \"relationship paths\", as defined in the [JSON API](https://jsonapi.org/format/1.0/#fetching-includes).\nIf relationship paths are not supported, or a server is unable to identify a relationship path a `400 Bad Request` response MUST be made.\n\nThe **default value** for `include` is `references`.\nThis means `references` entries MUST always be included under the top-level field `included` as default, since a server assumes if `include` is not specified by a client in the request, it is still specified as `include=references`.\nNote, if a client explicitly specifies `include` and leaves out `references`, `references` resource objects MUST NOT be included under the top-level field `included`, as per the definition of `included`, see section JSON Response Schema: Common Fields.\n\n> **Note**: A query with the parameter `include` set to the empty string means no related resource objects are to be returned under the top-level field `included`.", "required": false, "schema": { - "title": "Include", "type": "string", + "title": "Include", "description": "A server MAY implement the JSON API concept of returning [compound documents](https://jsonapi.org/format/1.0/#document-compound-documents) by utilizing the `include` query parameter as specified by [JSON API 1.0](https://jsonapi.org/format/1.0/#fetching-includes).\n\nAll related resource objects MUST be returned as part of an array value for the top-level `included` field, see the section JSON Response Schema: Common Fields.\n\nThe value of `include` MUST be a comma-separated list of \"relationship paths\", as defined in the [JSON API](https://jsonapi.org/format/1.0/#fetching-includes).\nIf relationship paths are not supported, or a server is unable to identify a relationship path a `400 Bad Request` response MUST be made.\n\nThe **default value** for `include` is `references`.\nThis means `references` entries MUST always be included under the top-level field `included` as default, since a server assumes if `include` is not specified by a client in the request, it is still specified as `include=references`.\nNote, if a client explicitly specifies `include` and leaves out `references`, `references` resource objects MUST NOT be included under the top-level field `included`, as per the definition of `included`, see section JSON Response Schema: Common Fields.\n\n> **Note**: A query with the parameter `include` set to the empty string means no related resource objects are to be returned under the top-level field `included`.", "default": "references" }, @@ -1205,9 +1205,9 @@ "description": "If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.", "required": false, "schema": { - "title": "Api Hint", - "pattern": "(v[0-9]+(\\.[0-9]+)?)?", "type": "string", + "pattern": "(v[0-9]+(\\.[0-9]+)?)?", + "title": "Api Hint", "description": "If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.", "default": "" }, @@ -1218,8 +1218,8 @@ "description": "A list of lists which contains a range for each dimension of the property.", "required": false, "schema": { - "title": "Property Ranges", "type": "string", + "title": "Property Ranges", "description": "A list of lists which contains a range for each dimension of the property." }, "name": "property_ranges", @@ -1343,8 +1343,8 @@ { "required": true, "schema": { - "title": "Entry Id", - "type": "string" + "type": "string", + "title": "Entry Id" }, "name": "entry_id", "in": "path" @@ -1353,8 +1353,8 @@ "description": "The output format requested (see section Response Format).\nDefaults to the format string 'json', which specifies the standard output format described in this specification.\nExample: `http://example.com/v1/structures?response_format=xml`", "required": false, "schema": { - "title": "Response Format", "type": "string", + "title": "Response Format", "description": "The output format requested (see section Response Format).\nDefaults to the format string 'json', which specifies the standard output format described in this specification.\nExample: `http://example.com/v1/structures?response_format=xml`", "default": "jsonlines" }, @@ -1365,10 +1365,10 @@ "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "required": false, "schema": { - "title": "Email Address", "type": "string", - "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "format": "email", + "title": "Email Address", + "description": "An email address of the user making the request.\nThe email SHOULD be that of a person and not an automatic system.\nExample: `http://example.com/v1/structures?email_address=user@example.com`", "default": "" }, "name": "email_address", @@ -1378,9 +1378,9 @@ "description": "If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.", "required": false, "schema": { - "title": "Api Hint", - "pattern": "(v[0-9]+(\\.[0-9]+)?)?", "type": "string", + "pattern": "(v[0-9]+(\\.[0-9]+)?)?", + "title": "Api Hint", "description": "If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.", "default": "" }, @@ -1391,9 +1391,9 @@ "description": "A comma-delimited set of fields to be provided in the output.\nIf provided, these fields MUST be returned along with the REQUIRED fields.\nOther OPTIONAL fields MUST NOT be returned when this parameter is present.\nExample: `http://example.com/v1/structures?response_fields=last_modified,nsites`", "required": false, "schema": { - "title": "Response Fields", - "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", "type": "string", + "pattern": "([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", + "title": "Response Fields", "description": "A comma-delimited set of fields to be provided in the output.\nIf provided, these fields MUST be returned along with the REQUIRED fields.\nOther OPTIONAL fields MUST NOT be returned when this parameter is present.\nExample: `http://example.com/v1/structures?response_fields=last_modified,nsites`", "default": "" }, @@ -1404,8 +1404,8 @@ "description": "A filter string, in the format described in section API Filtering Format Specification of the specification.", "required": false, "schema": { - "title": "Filter", "type": "string", + "title": "Filter", "description": "A filter string, in the format described in section API Filtering Format Specification of the specification.", "default": "" }, @@ -1416,8 +1416,8 @@ "description": "A list of lists which contains a range for each dimension of the property.", "required": false, "schema": { - "title": "Property Ranges", "type": "string", + "title": "Property Ranges", "description": "A list of lists which contains a range for each dimension of the property.", "default": "" }, @@ -1513,76 +1513,70 @@ "components": { "schemas": { "Aggregate": { - "title": "Aggregate", "enum": [ "ok", "test", "staging", "no" ], + "title": "Aggregate", "description": "Enumeration of aggregate values" }, "Assembly": { - "title": "Assembly", - "required": [ - "sites_in_groups", - "group_probabilities" - ], - "type": "object", "properties": { "sites_in_groups": { - "title": "Sites In Groups", - "type": "array", "items": { - "type": "array", "items": { "type": "integer" - } + }, + "type": "array" }, + "type": "array", + "title": "Sites In Groups", "description": "Index of the sites (0-based) that belong to each group for each assembly.\n\n- **Examples**:\n - `[[1], [2]]`: two groups, one with the second site, one with the third.\n - `[[1,2], [3]]`: one group with the second and third site, one with the fourth.", "x-optimade-support": "must", "x-optimade-queryable": "optional" }, "group_probabilities": { - "title": "Group Probabilities", - "type": "array", "items": { "type": "number" }, + "type": "array", + "title": "Group Probabilities", "description": "Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\nIt SHOULD sum to one.\nSee below for examples of how to specify the probability of the occurrence of a vacancy.\nThe possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.", "x-optimade-support": "must", "x-optimade-queryable": "optional" } }, + "type": "object", + "required": [ + "sites_in_groups", + "group_probabilities" + ], + "title": "Assembly", "description": "A description of groups of sites that are statistically correlated.\n\n- **Examples** (for each entry of the assemblies list):\n - `{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}`: the first site and the second site never occur at the same time in the unit cell.\n Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.\n - `{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}`: the second and third site are either present together or not present; they form the first group of atoms for this assembly.\n The second group is formed by the fourth site. Sites of the first group (the second and the third) are never present at the same time as the fourth site.\n 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent)." }, "Attributes": { - "title": "Attributes", - "type": "object", "properties": {}, + "type": "object", + "title": "Attributes", "description": "Members of the attributes object (\"attributes\") represent information about the resource object in which it's defined.\nThe keys for Attributes MUST NOT be:\n relationships\n links\n id\n type" }, "AvailableApiVersion": { - "title": "AvailableApiVersion", - "required": [ - "url", - "version" - ], - "type": "object", "properties": { "url": { - "title": "Url", + "type": "string", "maxLength": 65536, "minLength": 1, "pattern": ".+/v[0-1](\\.[0-9]+)*/?$", - "type": "string", - "description": "A string specifying a versioned base URL that MUST adhere to the rules in section Base URL", - "format": "uri" + "format": "uri", + "title": "Url", + "description": "A string specifying a versioned base URL that MUST adhere to the rules in section Base URL" }, "version": { - "title": "Version", - "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", "type": "string", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", + "title": "Version", "description": "A string containing the full version number of the API served at that versioned base URL.\nThe version number string MUST NOT be prefixed by, e.g., 'v'.\nExamples: `1.0.0`, `1.0.0-rc.2`.", "example": [ "0.10.1", @@ -1591,22 +1585,20 @@ ] } }, + "type": "object", + "required": [ + "url", + "version" + ], + "title": "AvailableApiVersion", "description": "A JSON object containing information about an available API version" }, "BaseInfoAttributes": { - "title": "BaseInfoAttributes", - "required": [ - "api_version", - "available_api_versions", - "available_endpoints", - "entry_types_by_format" - ], - "type": "object", "properties": { "api_version": { - "title": "Api Version", - "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", "type": "string", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", + "title": "Api Version", "description": "Presently used full version of the OPTIMADE API.\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\nExamples: `1.0.0`, `1.0.0-rc.2`.", "example": [ "0.10.1", @@ -1615,153 +1607,160 @@ ] }, "available_api_versions": { - "title": "Available Api Versions", - "type": "array", "items": { "$ref": "#/components/schemas/AvailableApiVersion" }, + "type": "array", + "title": "Available Api Versions", "description": "A list of dictionaries of available API versions at other base URLs" }, "formats": { - "title": "Formats", - "type": "array", "items": { "type": "string" }, + "type": "array", + "title": "Formats", "description": "List of available output formats.", "default": [ "json" ] }, "available_endpoints": { - "title": "Available Endpoints", - "type": "array", "items": { "type": "string" }, + "type": "array", + "title": "Available Endpoints", "description": "List of available endpoints (i.e., the string to be appended to the versioned base URL)." }, "entry_types_by_format": { - "title": "Entry Types By Format", - "type": "object", "additionalProperties": { - "type": "array", "items": { "type": "string" - } + }, + "type": "array" }, + "type": "object", + "title": "Entry Types By Format", "description": "Available entry endpoints as a function of output formats." }, "is_index": { - "title": "Is Index", "type": "boolean", + "title": "Is Index", "description": "If true, this is an index meta-database base URL (see section Index Meta-Database). If this member is not provided, the client MUST assume this is not an index meta-database base URL (i.e., the default is for `is_index` to be `false`).", "default": false } }, + "type": "object", + "required": [ + "api_version", + "available_api_versions", + "available_endpoints", + "entry_types_by_format" + ], + "title": "BaseInfoAttributes", "description": "Attributes for Base URL Info endpoint" }, "BaseInfoResource": { - "title": "BaseInfoResource", - "required": [ - "id", - "type", - "attributes" - ], - "type": "object", "properties": { "id": { - "title": "Id", - "pattern": "^/$", "type": "string", + "pattern": "^/$", + "title": "Id", "default": "/" }, "type": { - "title": "Type", - "pattern": "^info$", "type": "string", + "pattern": "^info$", + "title": "Type", "default": "info" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ResourceLinks" } ], + "title": "Links", "description": "a links object containing links related to the resource." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship." }, "attributes": { "$ref": "#/components/schemas/BaseInfoAttributes" }, "relationships": { - "title": "Relationships", "allOf": [ { "$ref": "#/components/schemas/Relationships" } ], + "title": "Relationships", "description": "[Relationships object](https://jsonapi.org/format/1.0/#document-resource-object-relationships)\ndescribing relationships between the resource and other JSON API resources." } }, + "type": "object", + "required": [ + "id", + "type", + "attributes" + ], + "title": "BaseInfoResource", "description": "Resource objects appear in a JSON API document to represent resources." }, "BaseRelationshipMeta": { - "title": "BaseRelationshipMeta", - "required": [ - "description" - ], - "type": "object", "properties": { "description": { - "title": "Description", "type": "string", + "title": "Description", "description": "OPTIONAL human-readable description of the relationship." } }, + "type": "object", + "required": [ + "description" + ], + "title": "BaseRelationshipMeta", "description": "Specific meta field for base relationship resource" }, "BaseRelationshipResource": { - "title": "BaseRelationshipResource", - "required": [ - "id", - "type" - ], - "type": "object", "properties": { "id": { - "title": "Id", "type": "string", + "title": "Id", "description": "Resource ID" }, "type": { - "title": "Type", "type": "string", + "title": "Type", "description": "Resource type" }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/BaseRelationshipMeta" } ], + "title": "Meta", "description": "Relationship meta field. MUST contain 'description' if supplied." } }, + "type": "object", + "required": [ + "id", + "type" + ], + "title": "BaseRelationshipResource", "description": "Minimum requirements to represent a relationship resource" }, "DataType": { - "title": "DataType", "enum": [ "string", "integer", @@ -1772,1473 +1771,1467 @@ "dictionary", "unknown" ], + "title": "DataType", "description": "Optimade Data Types\n\nSee the section \"Data types\" in the OPTIMADE API specification for more information." }, "EntryInfoProperty": { - "title": "EntryInfoProperty", - "required": [ - "description" - ], - "type": "object", "properties": { "description": { - "title": "Description", "type": "string", + "title": "Description", "description": "A human-readable description of the entry property" }, "unit": { - "title": "Unit", "type": "string", + "title": "Unit", "description": "The physical unit of the entry property.\nThis MUST be a valid representation of units according to version 2.1 of [The Unified Code for Units of Measure](https://unitsofmeasure.org/ucum.html).\nIt is RECOMMENDED that non-standard (non-SI) units are described in the description for the property." }, "sortable": { - "title": "Sortable", "type": "boolean", + "title": "Sortable", "description": "Defines whether the entry property can be used for sorting with the \"sort\" parameter.\nIf the entry listing endpoint supports sorting, this key MUST be present for sortable properties with value `true`." }, "type": { - "title": "Type", "allOf": [ { "$ref": "#/components/schemas/DataType" } ], + "title": "Type", "description": "The type of the property's value.\nThis MUST be any of the types defined in the Data types section.\nFor the purpose of compatibility with future versions of this specification, a client MUST accept values that are not `string` values specifying any of the OPTIMADE Data types, but MUST then also disregard the `type` field.\nNote, if the value is a nested type, only the outermost type should be reported.\nE.g., for the entry resource `structures`, the `species` property is defined as a list of dictionaries, hence its `type` value would be `list`." } - } - }, - "EntryInfoResource": { - "title": "EntryInfoResource", + }, + "type": "object", "required": [ - "formats", - "description", - "properties", - "output_fields_by_format" + "description" ], - "type": "object", + "title": "EntryInfoProperty" + }, + "EntryInfoResource": { "properties": { "formats": { - "title": "Formats", - "type": "array", "items": { "type": "string" }, + "type": "array", + "title": "Formats", "description": "List of output formats available for this type of entry." }, "description": { - "title": "Description", "type": "string", + "title": "Description", "description": "Description of the entry." }, "properties": { - "title": "Properties", - "type": "object", "additionalProperties": { "$ref": "#/components/schemas/EntryInfoProperty" }, + "type": "object", + "title": "Properties", "description": "A dictionary describing queryable properties for this entry type, where each key is a property name." }, "output_fields_by_format": { - "title": "Output Fields By Format", - "type": "object", "additionalProperties": { - "type": "array", "items": { "type": "string" - } + }, + "type": "array" }, + "type": "object", + "title": "Output Fields By Format", "description": "Dictionary of available output fields for this entry type, where the keys are the values of the `formats` list and the values are the keys of the `properties` dictionary." } - } - }, - "EntryInfoResponse": { - "title": "EntryInfoResponse", + }, + "type": "object", "required": [ - "data", - "meta" + "formats", + "description", + "properties", + "output_fields_by_format" ], - "type": "object", + "title": "EntryInfoResource" + }, + "EntryInfoResponse": { "properties": { "data": { - "title": "Data", "allOf": [ { "$ref": "#/components/schemas/EntryInfoResource" } ], + "title": "Data", "description": "OPTIMADE information for an entry endpoint." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/ResponseMeta" } ], + "title": "Meta", "description": "A meta object containing non-standard information" }, "errors": { - "title": "Errors", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/Error" }, + "type": "array", + "uniqueItems": true, + "title": "Errors", "description": "A list of unique errors" }, "included": { - "title": "Included", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/Resource" }, + "type": "array", + "uniqueItems": true, + "title": "Included", "description": "A list of unique included resources" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ToplevelLinks" } ], + "title": "Links", "description": "Links associated with the primary data or errors" }, "jsonapi": { - "title": "Jsonapi", "allOf": [ { "$ref": "#/components/schemas/JsonApi" } ], + "title": "Jsonapi", "description": "Information about the JSON API used" } }, + "type": "object", + "required": [ + "data", + "meta" + ], + "title": "EntryInfoResponse", "description": "errors are not allowed" }, "EntryMetadata": { - "title": "EntryMetadata", - "type": "object", "properties": { "property_metadata": { - "title": "Property Metadata", "type": "object", + "title": "Property Metadata", "description": "A dictionary, where the keys are the names of the properties in the attributes field and the value is a dictionary containing the metadata for that property.\nDatabase-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes)." }, "partial_data_links": { - "title": "Partial Data Links", - "type": "object", "additionalProperties": { - "type": "array", "items": { "$ref": "#/components/schemas/PartialDataLink" - } + }, + "type": "array" }, + "type": "object", + "title": "Partial Data Links", "description": "A dictionary, where the keys are the names of the properties in the attributes field for which the value is too large to be shared by default.\n For each property one or more links are provided from which the value of the attribute can be retrieved." } }, + "type": "object", + "title": "EntryMetadata", "description": "Contains the metadata for the attributes of an entry" }, "EntryRelationships": { - "title": "EntryRelationships", - "type": "object", "properties": { "references": { - "title": "References", "allOf": [ { "$ref": "#/components/schemas/ReferenceRelationship" } ], + "title": "References", "description": "Object containing links to relationships with entries of the `references` type." }, "structures": { - "title": "Structures", "allOf": [ { "$ref": "#/components/schemas/StructureRelationship" } ], + "title": "Structures", "description": "Object containing links to relationships with entries of the `structures` type." } }, + "type": "object", + "title": "EntryRelationships", "description": "This model wraps the JSON API Relationships to include type-specific top level keys." }, "EntryResource": { - "title": "EntryResource", - "required": [ - "id", - "type", - "attributes" - ], - "type": "object", "properties": { "id": { - "title": "Id", "type": "string", + "title": "Id", "description": "An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`", "x-optimade-support": "must", "x-optimade-queryable": "must" }, "type": { - "title": "Type", "type": "string", + "title": "Type", "description": "The name of the type of an entry.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type `` and ID `` MUST be returned in response to a request for `//` under the versioned base URL.\n\n- **Example**: `\"structures\"`", "x-optimade-support": "must", "x-optimade-queryable": "must" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ResourceLinks" } ], + "title": "Links", "description": "a links object containing links related to the resource." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/EntryMetadata" } ], + "title": "Meta", "description": "A dictionary, containing entry and property-specific metadata for a given entry." }, "attributes": { - "title": "Attributes", "allOf": [ { "$ref": "#/components/schemas/EntryResourceAttributes" } ], + "title": "Attributes", "description": "A dictionary, containing key-value pairs representing the entry's properties, except for `type` and `id`.\nDatabase-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes)." }, "relationships": { - "title": "Relationships", "allOf": [ { "$ref": "#/components/schemas/EntryRelationships" } ], + "title": "Relationships", "description": "A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object." } }, + "type": "object", + "required": [ + "id", + "type", + "attributes" + ], + "title": "EntryResource", "description": "The base model for an entry resource." }, "EntryResourceAttributes": { - "title": "EntryResourceAttributes", - "required": [ - "last_modified" - ], - "type": "object", "properties": { "immutable_id": { - "title": "Immutable Id", "type": "string", + "title": "Immutable Id", "description": "The entry's immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)", "x-optimade-support": "optional", "x-optimade-queryable": "must" }, "last_modified": { - "title": "Last Modified", "type": "string", - "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "format": "date-time", + "title": "Last Modified", + "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "x-optimade-support": "should", "x-optimade-queryable": "must" } }, + "type": "object", + "required": [ + "last_modified" + ], + "title": "EntryResourceAttributes", "description": "Contains key-value pairs representing the entry's properties." }, "Error": { - "title": "Error", - "type": "object", "properties": { "id": { - "title": "Id", "type": "string", + "title": "Id", "description": "A unique identifier for this particular occurrence of the problem." }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ErrorLinks" } ], + "title": "Links", "description": "A links object storing about" }, "status": { - "title": "Status", "type": "string", + "title": "Status", "description": "the HTTP status code applicable to this problem, expressed as a string value." }, "code": { - "title": "Code", "type": "string", + "title": "Code", "description": "an application-specific error code, expressed as a string value." }, "title": { - "title": "Title", "type": "string", + "title": "Title", "description": "A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization." }, "detail": { - "title": "Detail", "type": "string", + "title": "Detail", "description": "A human-readable explanation specific to this occurrence of the problem." }, "source": { - "title": "Source", "allOf": [ { "$ref": "#/components/schemas/ErrorSource" } ], + "title": "Source", "description": "An object containing references to the source of the error" }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "a meta object containing non-standard meta-information about the error." } }, + "type": "object", + "title": "Error", "description": "An error response" }, "ErrorLinks": { - "title": "ErrorLinks", - "type": "object", "properties": { "about": { - "title": "About", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "About", "description": "A link that leads to further details about this particular occurrence of the problem." } }, + "type": "object", + "title": "ErrorLinks", "description": "A Links object specific to Error objects" }, "ErrorResponse": { - "title": "ErrorResponse", - "required": [ - "meta", - "errors" - ], - "type": "object", "properties": { "data": { - "title": "Data", - "uniqueItems": true, "anyOf": [ { "$ref": "#/components/schemas/Resource" }, { - "type": "array", "items": { "$ref": "#/components/schemas/Resource" - } + }, + "type": "array" } ], + "uniqueItems": true, + "title": "Data", "description": "Outputted Data" }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/ResponseMeta" } ], + "title": "Meta", "description": "A meta object containing non-standard information." }, "errors": { - "title": "Errors", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/OptimadeError" }, + "type": "array", + "uniqueItems": true, + "title": "Errors", "description": "A list of OPTIMADE-specific JSON API error objects, where the field detail MUST be present." }, "included": { - "title": "Included", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/Resource" }, + "type": "array", + "uniqueItems": true, + "title": "Included", "description": "A list of unique included resources" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ToplevelLinks" } ], + "title": "Links", "description": "Links associated with the primary data or errors" }, "jsonapi": { - "title": "Jsonapi", "allOf": [ { "$ref": "#/components/schemas/JsonApi" } ], + "title": "Jsonapi", "description": "Information about the JSON API used" } }, + "type": "object", + "required": [ + "meta", + "errors" + ], + "title": "ErrorResponse", "description": "errors MUST be present and data MUST be skipped" }, "ErrorSource": { - "title": "ErrorSource", - "type": "object", "properties": { "pointer": { - "title": "Pointer", "type": "string", + "title": "Pointer", "description": "a JSON Pointer [RFC6901] to the associated entity in the request document [e.g. \"/data\" for a primary data object, or \"/data/attributes/title\" for a specific attribute]." }, "parameter": { - "title": "Parameter", "type": "string", + "title": "Parameter", "description": "a string indicating which URI query parameter caused the error." } }, + "type": "object", + "title": "ErrorSource", "description": "an object containing references to the source of the error" }, "Implementation": { - "title": "Implementation", - "type": "object", "properties": { "name": { - "title": "Name", "type": "string", + "title": "Name", "description": "name of the implementation" }, "version": { - "title": "Version", "type": "string", + "title": "Version", "description": "version string of the current implementation" }, "homepage": { - "title": "Homepage", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Homepage", "description": "A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the homepage of the implementation." }, "source_url": { - "title": "Source Url", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Source Url", "description": "A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the implementation source, either downloadable archive or version control system." }, "maintainer": { - "title": "Maintainer", "allOf": [ { "$ref": "#/components/schemas/ImplementationMaintainer" } ], + "title": "Maintainer", "description": "A dictionary providing details about the maintainer of the implementation." }, "issue_tracker": { - "title": "Issue Tracker", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Issue Tracker", "description": "A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) pointing to the implementation's issue tracker." } }, + "type": "object", + "title": "Implementation", "description": "Information on the server implementation" }, "ImplementationMaintainer": { - "title": "ImplementationMaintainer", - "required": [ - "email" - ], - "type": "object", "properties": { "email": { - "title": "Email", "type": "string", - "description": "the maintainer's email address", - "format": "email" + "format": "email", + "title": "Email", + "description": "the maintainer's email address" } }, + "type": "object", + "required": [ + "email" + ], + "title": "ImplementationMaintainer", "description": "Details about the maintainer of the implementation" }, "InfoResponse": { - "title": "InfoResponse", - "required": [ - "data", - "meta" - ], - "type": "object", "properties": { "data": { - "title": "Data", "allOf": [ { "$ref": "#/components/schemas/BaseInfoResource" } ], + "title": "Data", "description": "The implementations /info data." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/ResponseMeta" } ], + "title": "Meta", "description": "A meta object containing non-standard information" }, "errors": { - "title": "Errors", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/Error" }, + "type": "array", + "uniqueItems": true, + "title": "Errors", "description": "A list of unique errors" }, "included": { - "title": "Included", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/Resource" }, + "type": "array", + "uniqueItems": true, + "title": "Included", "description": "A list of unique included resources" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ToplevelLinks" } ], + "title": "Links", "description": "Links associated with the primary data or errors" }, "jsonapi": { - "title": "Jsonapi", "allOf": [ { "$ref": "#/components/schemas/JsonApi" } ], + "title": "Jsonapi", "description": "Information about the JSON API used" } }, + "type": "object", + "required": [ + "data", + "meta" + ], + "title": "InfoResponse", "description": "errors are not allowed" }, "JsonApi": { - "title": "JsonApi", - "type": "object", "properties": { "version": { - "title": "Version", "type": "string", + "title": "Version", "description": "Version of the json API used", "default": "1.0" }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "Non-standard meta information" } }, + "type": "object", + "title": "JsonApi", "description": "An object describing the server's implementation" }, "Link": { - "title": "Link", - "required": [ - "href" - ], - "type": "object", "properties": { "href": { - "title": "Href", + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", - "description": "a string containing the link\u2019s URL.", - "format": "uri" + "format": "uri", + "title": "Href", + "description": "a string containing the link\u2019s URL." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "a meta object containing non-standard meta-information about the link." } }, + "type": "object", + "required": [ + "href" + ], + "title": "Link", "description": "A link **MUST** be represented as either: a string containing the link's URL or a link object." }, "LinkType": { - "title": "LinkType", "enum": [ "child", "root", "external", "providers" ], + "title": "LinkType", "description": "Enumeration of link_type values" }, "LinksResource": { - "title": "LinksResource", - "required": [ - "id", - "type", - "attributes" - ], - "type": "object", "properties": { "id": { - "title": "Id", "type": "string", + "title": "Id", "description": "An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`", "x-optimade-support": "must", "x-optimade-queryable": "must" }, "type": { - "title": "Type", - "pattern": "^links$", "type": "string", + "pattern": "^links$", + "title": "Type", "description": "These objects are described in detail in the section Links Endpoint", "default": "links" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ResourceLinks" } ], + "title": "Links", "description": "a links object containing links related to the resource." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/EntryMetadata" } ], + "title": "Meta", "description": "A dictionary, containing entry and property-specific metadata for a given entry." }, "attributes": { - "title": "Attributes", "allOf": [ { "$ref": "#/components/schemas/LinksResourceAttributes" } ], + "title": "Attributes", "description": "A dictionary containing key-value pairs representing the Links resource's properties." }, "relationships": { - "title": "Relationships", "allOf": [ { "$ref": "#/components/schemas/EntryRelationships" } ], + "title": "Relationships", "description": "A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object." } }, + "type": "object", + "required": [ + "id", + "type", + "attributes" + ], + "title": "LinksResource", "description": "A Links endpoint resource object" }, "LinksResourceAttributes": { - "title": "LinksResourceAttributes", - "required": [ - "name", - "description", - "base_url", - "homepage", - "link_type" - ], - "type": "object", "properties": { "name": { - "title": "Name", "type": "string", + "title": "Name", "description": "Human-readable name for the OPTIMADE API implementation, e.g., for use in clients to show the name to the end-user." }, "description": { - "title": "Description", "type": "string", + "title": "Description", "description": "Human-readable description for the OPTIMADE API implementation, e.g., for use in clients to show a description to the end-user." }, "base_url": { - "title": "Base Url", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Base Url", "description": "JSON API links object, pointing to the base URL for this implementation" }, "homepage": { - "title": "Homepage", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Homepage", "description": "JSON API links object, pointing to a homepage URL for this implementation" }, "link_type": { - "title": "Link Type", "allOf": [ { "$ref": "#/components/schemas/LinkType" } ], + "title": "Link Type", "description": "The type of the linked relation.\nMUST be one of these values: 'child', 'root', 'external', 'providers'." }, "aggregate": { - "title": "Aggregate", "allOf": [ { "$ref": "#/components/schemas/Aggregate" } ], + "title": "Aggregate", "description": "A string indicating whether a client that is following links to aggregate results from different OPTIMADE implementations should follow this link or not.\nThis flag SHOULD NOT be indicated for links where `link_type` is not `child`.\n\nIf not specified, clients MAY assume that the value is `ok`.\nIf specified, and the value is anything different than `ok`, the client MUST assume that the server is suggesting not to follow the link during aggregation by default (also if the value is not among the known ones, in case a future specification adds new accepted values).\n\nSpecific values indicate the reason why the server is providing the suggestion.\nA client MAY follow the link anyway if it has reason to do so (e.g., if the client is looking for all test databases, it MAY follow the links marked with `aggregate`=`test`).\n\nIf specified, it MUST be one of the values listed in section Link Aggregate Options.", "default": "ok" }, "no_aggregate_reason": { - "title": "No Aggregate Reason", "type": "string", + "title": "No Aggregate Reason", "description": "An OPTIONAL human-readable string indicating the reason for suggesting not to aggregate results following the link.\nIt SHOULD NOT be present if `aggregate`=`ok`." } }, + "type": "object", + "required": [ + "name", + "description", + "base_url", + "homepage", + "link_type" + ], + "title": "LinksResourceAttributes", "description": "Links endpoint resource object attributes" }, "LinksResponse": { - "title": "LinksResponse", - "required": [ - "data", - "meta" - ], - "type": "object", "properties": { "data": { - "title": "Data", - "uniqueItems": true, "anyOf": [ { - "type": "array", "items": { "$ref": "#/components/schemas/LinksResource" - } + }, + "type": "array" }, { - "type": "array", "items": { "type": "object" - } + }, + "type": "array" } ], + "uniqueItems": true, + "title": "Data", "description": "List of unique OPTIMADE links resource objects." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/ResponseMeta" } ], + "title": "Meta", "description": "A meta object containing non-standard information" }, "errors": { - "title": "Errors", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/Error" }, + "type": "array", + "uniqueItems": true, + "title": "Errors", "description": "A list of unique errors" }, "included": { - "title": "Included", - "uniqueItems": true, "anyOf": [ { - "type": "array", "items": { "$ref": "#/components/schemas/EntryResource" - } + }, + "type": "array" }, { - "type": "array", "items": { "type": "object" - } + }, + "type": "array" } - ] + ], + "uniqueItems": true, + "title": "Included" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ToplevelLinks" } ], + "title": "Links", "description": "Links associated with the primary data or errors" }, "jsonapi": { - "title": "Jsonapi", "allOf": [ { "$ref": "#/components/schemas/JsonApi" } ], + "title": "Jsonapi", "description": "Information about the JSON API used" } }, + "type": "object", + "required": [ + "data", + "meta" + ], + "title": "LinksResponse", "description": "errors are not allowed" }, "Meta": { - "title": "Meta", - "type": "object", "properties": {}, + "type": "object", + "title": "Meta", "description": "Non-standard meta-information that can not be represented as an attribute or relationship." }, "OptimadeError": { - "title": "OptimadeError", - "required": [ - "detail" - ], - "type": "object", "properties": { "id": { - "title": "Id", "type": "string", + "title": "Id", "description": "A unique identifier for this particular occurrence of the problem." }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ErrorLinks" } ], + "title": "Links", "description": "A links object storing about" }, "status": { - "title": "Status", "type": "string", + "title": "Status", "description": "the HTTP status code applicable to this problem, expressed as a string value." }, "code": { - "title": "Code", "type": "string", + "title": "Code", "description": "an application-specific error code, expressed as a string value." }, "title": { - "title": "Title", "type": "string", + "title": "Title", "description": "A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization." }, "detail": { - "title": "Detail", "type": "string", + "title": "Detail", "description": "A human-readable explanation specific to this occurrence of the problem." }, "source": { - "title": "Source", "allOf": [ { "$ref": "#/components/schemas/ErrorSource" } ], + "title": "Source", "description": "An object containing references to the source of the error" }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "a meta object containing non-standard meta-information about the error." } }, + "type": "object", + "required": [ + "detail" + ], + "title": "OptimadeError", "description": "detail MUST be present" }, "PartialDataLink": { - "title": "PartialDataLink", - "required": [ - "link", - "format" - ], - "type": "object", "properties": { "link": { - "title": "Link", + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", - "description": "String. A JSON API link that points to a location from which the omitted data can be fetched. There is no requirement on the syntax or format for the link URL.", - "format": "uri" + "format": "uri", + "title": "Link", + "description": "String. A JSON API link that points to a location from which the omitted data can be fetched. There is no requirement on the syntax or format for the link URL." }, "format": { - "title": "Format", "type": "string", + "title": "Format", "description": "String. The name of the format provided via this link. For one of the objects this format field SHOULD have the value \"jsonlines\", which refers to the format in OPTIMADE JSON lines partial data format." } - } + }, + "type": "object", + "required": [ + "link", + "format" + ], + "title": "PartialDataLink" }, "Periodicity": { - "title": "Periodicity", + "type": "integer", "enum": [ 0, 1 ], - "type": "integer", + "title": "Periodicity", "description": "Integer enumeration of dimension_types values" }, "Person": { - "title": "Person", - "required": [ - "name" - ], - "type": "object", "properties": { "name": { - "title": "Name", "type": "string", + "title": "Name", "description": "Full name of the person, REQUIRED.", "x-optimade-support": "must", "x-optimade-queryable": "optional" }, "firstname": { - "title": "Firstname", "type": "string", + "title": "Firstname", "description": "First name of the person.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "lastname": { - "title": "Lastname", "type": "string", + "title": "Lastname", "description": "Last name of the person.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" } }, + "type": "object", + "required": [ + "name" + ], + "title": "Person", "description": "A person, i.e., an author, editor or other." }, "Provider": { - "title": "Provider", - "required": [ - "name", - "description", - "prefix" - ], - "type": "object", "properties": { "name": { - "title": "Name", "type": "string", + "title": "Name", "description": "a short name for the database provider" }, "description": { - "title": "Description", "type": "string", + "title": "Description", "description": "a longer description of the database provider" }, "prefix": { - "title": "Prefix", - "pattern": "^[a-z]([a-z]|[0-9]|_)*$", "type": "string", + "pattern": "^[a-z]([a-z]|[0-9]|_)*$", + "title": "Prefix", "description": "database-provider-specific prefix as found in section Database-Provider-Specific Namespace Prefixes." }, "homepage": { - "title": "Homepage", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Homepage", "description": "a [JSON API links object](http://jsonapi.org/format/1.0#document-links) pointing to homepage of the database provider, either directly as a string, or as a link object." } }, + "type": "object", + "required": [ + "name", + "description", + "prefix" + ], + "title": "Provider", "description": "Information on the database provider of the implementation." }, "ReferenceRelationship": { - "title": "ReferenceRelationship", - "type": "object", "properties": { "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/RelationshipLinks" } ], + "title": "Links", "description": "a links object containing at least one of the following: self, related" }, "data": { - "title": "Data", - "uniqueItems": true, "anyOf": [ { "$ref": "#/components/schemas/BaseRelationshipResource" }, { - "type": "array", "items": { "$ref": "#/components/schemas/BaseRelationshipResource" - } + }, + "type": "array" } ], + "uniqueItems": true, + "title": "Data", "description": "Resource linkage" }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "a meta object that contains non-standard meta-information about the relationship." } }, + "type": "object", + "title": "ReferenceRelationship", "description": "Similar to normal JSON API relationship, but with addition of OPTIONAL meta field for a resource." }, "ReferenceResource": { - "title": "ReferenceResource", - "required": [ - "id", - "type", - "attributes" - ], - "type": "object", "properties": { "id": { - "title": "Id", "type": "string", + "title": "Id", "description": "An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`", "x-optimade-support": "must", "x-optimade-queryable": "must" }, "type": { - "title": "Type", - "pattern": "^references$", "type": "string", + "pattern": "^references$", + "title": "Type", "description": "The name of the type of an entry.\n- **Type**: string.\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type and ID MUST be returned in response to a request for `//` under the versioned base URL.\n- **Example**: `\"structures\"`", "default": "references", "x-optimade-support": "must", "x-optimade-queryable": "must" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ResourceLinks" } ], + "title": "Links", "description": "a links object containing links related to the resource." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/EntryMetadata" } ], + "title": "Meta", "description": "A dictionary, containing entry and property-specific metadata for a given entry." }, "attributes": { "$ref": "#/components/schemas/ReferenceResourceAttributes" }, "relationships": { - "title": "Relationships", "allOf": [ { "$ref": "#/components/schemas/EntryRelationships" } ], + "title": "Relationships", "description": "A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object." } }, + "type": "object", + "required": [ + "id", + "type", + "attributes" + ], + "title": "ReferenceResource", "description": "The `references` entries describe bibliographic references.\n\nThe following properties are used to provide the bibliographic details:\n\n- **address**, **annote**, **booktitle**, **chapter**, **crossref**, **edition**, **howpublished**, **institution**, **journal**, **key**, **month**, **note**, **number**, **organization**, **pages**, **publisher**, **school**, **series**, **title**, **volume**, **year**: meanings of these properties match the [BibTeX specification](http://bibtexml.sourceforge.net/btxdoc.pdf), values are strings;\n- **bib_type**: type of the reference, corresponding to **type** property in the BibTeX specification, value is string;\n- **authors** and **editors**: lists of *person objects* which are dictionaries with the following keys:\n - **name**: Full name of the person, REQUIRED.\n - **firstname**, **lastname**: Parts of the person's name, OPTIONAL.\n- **doi** and **url**: values are strings.\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., any of the properties MAY be `null`.\n - **Query**: Support for queries on any of these properties is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - Every references entry MUST contain at least one of the properties." }, "ReferenceResourceAttributes": { - "title": "ReferenceResourceAttributes", - "required": [ - "last_modified" - ], - "type": "object", "properties": { "immutable_id": { - "title": "Immutable Id", "type": "string", + "title": "Immutable Id", "description": "The entry's immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)", "x-optimade-support": "optional", "x-optimade-queryable": "must" }, "last_modified": { - "title": "Last Modified", "type": "string", - "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "format": "date-time", + "title": "Last Modified", + "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "x-optimade-support": "should", "x-optimade-queryable": "must" }, "authors": { - "title": "Authors", - "type": "array", "items": { "$ref": "#/components/schemas/Person" }, + "type": "array", + "title": "Authors", "description": "List of person objects containing the authors of the reference.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "editors": { - "title": "Editors", - "type": "array", "items": { "$ref": "#/components/schemas/Person" }, + "type": "array", + "title": "Editors", "description": "List of person objects containing the editors of the reference.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "doi": { - "title": "Doi", "type": "string", + "title": "Doi", "description": "The digital object identifier of the reference.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "url": { - "title": "Url", + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", - "description": "The URL of the reference.", "format": "uri", + "title": "Url", + "description": "The URL of the reference.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "address": { - "title": "Address", "type": "string", + "title": "Address", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "annote": { - "title": "Annote", "type": "string", + "title": "Annote", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "booktitle": { - "title": "Booktitle", "type": "string", + "title": "Booktitle", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "chapter": { - "title": "Chapter", "type": "string", + "title": "Chapter", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "crossref": { - "title": "Crossref", "type": "string", + "title": "Crossref", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "edition": { - "title": "Edition", "type": "string", + "title": "Edition", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "howpublished": { - "title": "Howpublished", "type": "string", + "title": "Howpublished", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "institution": { - "title": "Institution", "type": "string", + "title": "Institution", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "journal": { - "title": "Journal", "type": "string", + "title": "Journal", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "key": { - "title": "Key", "type": "string", + "title": "Key", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "month": { - "title": "Month", "type": "string", + "title": "Month", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "note": { - "title": "Note", "type": "string", + "title": "Note", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "number": { - "title": "Number", "type": "string", + "title": "Number", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "organization": { - "title": "Organization", "type": "string", + "title": "Organization", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "pages": { - "title": "Pages", "type": "string", + "title": "Pages", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "publisher": { - "title": "Publisher", "type": "string", + "title": "Publisher", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "school": { - "title": "School", "type": "string", + "title": "School", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "series": { - "title": "Series", "type": "string", + "title": "Series", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "title": { - "title": "Title", "type": "string", + "title": "Title", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "bib_type": { - "title": "Bib Type", "type": "string", + "title": "Bib Type", "description": "Type of the reference, corresponding to the **type** property in the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "volume": { - "title": "Volume", "type": "string", + "title": "Volume", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "year": { - "title": "Year", "type": "string", + "title": "Year", "description": "Meaning of property matches the BiBTeX specification.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" } }, + "type": "object", + "required": [ + "last_modified" + ], + "title": "ReferenceResourceAttributes", "description": "Model that stores the attributes of a reference.\n\nMany properties match the meaning described in the\n[BibTeX specification](http://bibtexml.sourceforge.net/btxdoc.pdf)." }, "ReferenceResponseMany": { - "title": "ReferenceResponseMany", - "required": [ - "data", - "meta" - ], - "type": "object", "properties": { "data": { - "title": "Data", - "uniqueItems": true, "anyOf": [ { - "type": "array", "items": { "$ref": "#/components/schemas/ReferenceResource" - } + }, + "type": "array" }, { - "type": "array", "items": { "type": "object" - } + }, + "type": "array" } ], + "uniqueItems": true, + "title": "Data", "description": "List of unique OPTIMADE references entry resource objects." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/ResponseMeta" } ], + "title": "Meta", "description": "A meta object containing non-standard information" }, "errors": { - "title": "Errors", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/Error" }, + "type": "array", + "uniqueItems": true, + "title": "Errors", "description": "A list of unique errors" }, "included": { - "title": "Included", - "uniqueItems": true, "anyOf": [ { - "type": "array", "items": { "$ref": "#/components/schemas/EntryResource" - } + }, + "type": "array" }, { - "type": "array", "items": { "type": "object" - } + }, + "type": "array" } - ] + ], + "uniqueItems": true, + "title": "Included" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ToplevelLinks" } ], + "title": "Links", "description": "Links associated with the primary data or errors" }, "jsonapi": { - "title": "Jsonapi", "allOf": [ { "$ref": "#/components/schemas/JsonApi" } ], + "title": "Jsonapi", "description": "Information about the JSON API used" } }, - "description": "errors are not allowed" - }, - "ReferenceResponseOne": { - "title": "ReferenceResponseOne", + "type": "object", "required": [ "data", "meta" ], - "type": "object", + "title": "ReferenceResponseMany", + "description": "errors are not allowed" + }, + "ReferenceResponseOne": { "properties": { "data": { - "title": "Data", "anyOf": [ { "$ref": "#/components/schemas/ReferenceResource" @@ -3247,209 +3240,209 @@ "type": "object" } ], + "title": "Data", "description": "A single references entry resource." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/ResponseMeta" } ], + "title": "Meta", "description": "A meta object containing non-standard information" }, "errors": { - "title": "Errors", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/Error" }, + "type": "array", + "uniqueItems": true, + "title": "Errors", "description": "A list of unique errors" }, "included": { - "title": "Included", - "uniqueItems": true, "anyOf": [ { - "type": "array", "items": { "$ref": "#/components/schemas/EntryResource" - } + }, + "type": "array" }, { - "type": "array", "items": { "type": "object" - } + }, + "type": "array" } - ] + ], + "uniqueItems": true, + "title": "Included" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ToplevelLinks" } ], + "title": "Links", "description": "Links associated with the primary data or errors" }, "jsonapi": { - "title": "Jsonapi", "allOf": [ { "$ref": "#/components/schemas/JsonApi" } ], + "title": "Jsonapi", "description": "Information about the JSON API used" } }, + "type": "object", + "required": [ + "data", + "meta" + ], + "title": "ReferenceResponseOne", "description": "errors are not allowed" }, "RelationshipLinks": { - "title": "RelationshipLinks", - "type": "object", "properties": { "self": { - "title": "Self", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Self", "description": "A link for the relationship itself (a 'relationship link').\nThis link allows the client to directly manipulate the relationship.\nWhen fetched successfully, this link returns the [linkage](https://jsonapi.org/format/1.0/#document-resource-object-linkage) for the related resources as its primary data.\n(See [Fetching Relationships](https://jsonapi.org/format/1.0/#fetching-relationships).)" }, "related": { - "title": "Related", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Related", "description": "A [related resource link](https://jsonapi.org/format/1.0/#document-resource-object-related-resource-links)." } }, + "type": "object", + "title": "RelationshipLinks", "description": "A resource object **MAY** contain references to other resource objects (\"relationships\").\nRelationships may be to-one or to-many.\nRelationships can be specified by including a member in a resource's links object." }, "Relationships": { - "title": "Relationships", - "type": "object", "properties": {}, + "type": "object", + "title": "Relationships", "description": "Members of the relationships object (\"relationships\") represent references from the resource object in which it's defined to other resource objects.\nKeys MUST NOT be:\n type\n id" }, "Resource": { - "title": "Resource", - "required": [ - "id", - "type" - ], - "type": "object", "properties": { "id": { - "title": "Id", "type": "string", + "title": "Id", "description": "Resource ID" }, "type": { - "title": "Type", "type": "string", + "title": "Type", "description": "Resource type" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ResourceLinks" } ], + "title": "Links", "description": "a links object containing links related to the resource." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "a meta object containing non-standard meta-information about a resource that can not be represented as an attribute or relationship." }, "attributes": { - "title": "Attributes", "allOf": [ { "$ref": "#/components/schemas/Attributes" } ], + "title": "Attributes", "description": "an attributes object representing some of the resource\u2019s data." }, "relationships": { - "title": "Relationships", "allOf": [ { "$ref": "#/components/schemas/Relationships" } ], + "title": "Relationships", "description": "[Relationships object](https://jsonapi.org/format/1.0/#document-resource-object-relationships)\ndescribing relationships between the resource and other JSON API resources." } }, + "type": "object", + "required": [ + "id", + "type" + ], + "title": "Resource", "description": "Resource objects appear in a JSON API document to represent resources." }, "ResourceLinks": { - "title": "ResourceLinks", - "type": "object", "properties": { "self": { - "title": "Self", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Self", "description": "A link that identifies the resource represented by the resource object." } }, + "type": "object", + "title": "ResourceLinks", "description": "A Resource Links object" }, "ResponseMeta": { - "title": "ResponseMeta", - "required": [ - "query", - "api_version", - "more_data_available" - ], - "type": "object", "properties": { "query": { - "title": "Query", "allOf": [ { "$ref": "#/components/schemas/ResponseMetaQuery" } ], + "title": "Query", "description": "Information on the Query that was requested" }, "api_version": { - "title": "Api Version", - "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", "type": "string", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", + "title": "Api Version", "description": "Presently used full version of the OPTIMADE API.\nThe version number string MUST NOT be prefixed by, e.g., \"v\".\nExamples: `1.0.0`, `1.0.0-rc.2`.", "example": [ "0.10.1", @@ -3458,588 +3451,588 @@ ] }, "more_data_available": { - "title": "More Data Available", "type": "boolean", + "title": "More Data Available", "description": "`false` if the response contains all data for the request (e.g., a request issued to a single entry endpoint, or a `filter` query at the last page of a paginated response) and `true` if the response is incomplete in the sense that multiple objects match the request, and not all of them have been included in the response (e.g., a query with multiple pages that is not at the last page)." }, "schema": { - "title": "Schema", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Schema", "description": "A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) that points to a schema for the response.\nIf it is a string, or a dictionary containing no `meta` field, the provided URL MUST point at an [OpenAPI](https://swagger.io/specification/) schema.\nIt is possible that future versions of this specification allows for alternative schema types.\nHence, if the `meta` field of the JSON API links object is provided and contains a field `schema_type` that is not equal to the string `OpenAPI` the client MUST not handle failures to parse the schema or to validate the response against the schema as errors." }, "time_stamp": { - "title": "Time Stamp", "type": "string", - "description": "A timestamp containing the date and time at which the query was executed.", - "format": "date-time" + "format": "date-time", + "title": "Time Stamp", + "description": "A timestamp containing the date and time at which the query was executed." }, "data_returned": { - "title": "Data Returned", - "minimum": 0.0, "type": "integer", + "minimum": 0.0, + "title": "Data Returned", "description": "An integer containing the total number of data resource objects returned for the current `filter` query, independent of pagination." }, "provider": { - "title": "Provider", "allOf": [ { "$ref": "#/components/schemas/Provider" } ], + "title": "Provider", "description": "information on the database provider of the implementation." }, "data_available": { - "title": "Data Available", "type": "integer", + "title": "Data Available", "description": "An integer containing the total number of data resource objects available in the database for the endpoint." }, "last_id": { - "title": "Last Id", "type": "string", + "title": "Last Id", "description": "a string containing the last ID returned" }, "response_message": { - "title": "Response Message", "type": "string", + "title": "Response Message", "description": "response string from the server" }, "implementation": { - "title": "Implementation", "allOf": [ { "$ref": "#/components/schemas/Implementation" } ], + "title": "Implementation", "description": "a dictionary describing the server implementation" }, "warnings": { - "title": "Warnings", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/Warnings" }, + "type": "array", + "uniqueItems": true, + "title": "Warnings", "description": "A list of warning resource objects representing non-critical errors or warnings.\nA warning resource object is defined similarly to a [JSON API error object](http://jsonapi.org/format/1.0/#error-objects), but MUST also include the field `type`, which MUST have the value `\"warning\"`.\nThe field `detail` MUST be present and SHOULD contain a non-critical message, e.g., reporting unrecognized search attributes or deprecated features.\nThe field `status`, representing a HTTP response status code, MUST NOT be present for a warning resource object.\nThis is an exclusive field for error resource objects." } }, + "type": "object", + "required": [ + "query", + "api_version", + "more_data_available" + ], + "title": "ResponseMeta", "description": "A [JSON API meta member](https://jsonapi.org/format/1.0#document-meta)\nthat contains JSON API meta objects of non-standard\nmeta-information.\n\nOPTIONAL additional information global to the query that is not\nspecified in this document, MUST start with a\ndatabase-provider-specific prefix." }, "ResponseMetaQuery": { - "title": "ResponseMetaQuery", - "required": [ - "representation" - ], - "type": "object", "properties": { "representation": { - "title": "Representation", "type": "string", + "title": "Representation", "description": "A string with the part of the URL following the versioned or unversioned base URL that serves the API.\nQuery parameters that have not been used in processing the request MAY be omitted.\nIn particular, if no query parameters have been involved in processing the request, the query part of the URL MAY be excluded.\nExample: `/structures?filter=nelements=2`" } }, + "type": "object", + "required": [ + "representation" + ], + "title": "ResponseMetaQuery", "description": "Information on the query that was requested." }, "Species": { - "title": "Species", - "required": [ - "name", - "chemical_symbols", - "concentration" - ], - "type": "object", "properties": { "name": { - "title": "Name", "type": "string", + "title": "Name", "description": "Gives the name of the species; the **name** value MUST be unique in the `species` list.", "x-optimade-support": "must", "x-optimade-queryable": "optional" }, "chemical_symbols": { - "title": "Chemical Symbols", - "type": "array", "items": { "type": "string" }, + "type": "array", + "title": "Chemical Symbols", "description": "MUST be a list of strings of all chemical elements composing this species. Each item of the list MUST be one of the following:\n\n- a valid chemical-element symbol, or\n- the special value `\"X\"` to represent a non-chemical element, or\n- the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\n\nIf any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.", "x-optimade-support": "must", "x-optimade-queryable": "optional" }, "concentration": { - "title": "Concentration", - "type": "array", "items": { "type": "number" }, + "type": "array", + "title": "Concentration", "description": "MUST be a list of floats, with same length as `chemical_symbols`. The numbers represent the relative concentration of the corresponding chemical symbol in this species. The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\n\n- Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\n- Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\n\nNote that concentrations are uncorrelated between different site (even of the same species).", "x-optimade-support": "must", "x-optimade-queryable": "optional" }, "mass": { - "title": "Mass", - "type": "array", "items": { "type": "number" }, + "type": "array", + "title": "Mass", "description": "If present MUST be a list of floats expressed in a.m.u.\nElements denoting vacancies MUST have masses equal to 0.", + "x-optimade-unit": "a.m.u.", "x-optimade-support": "optional", - "x-optimade-queryable": "optional", - "x-optimade-unit": "a.m.u." + "x-optimade-queryable": "optional" }, "original_name": { - "title": "Original Name", "type": "string", + "title": "Original Name", "description": "Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\n\nNote: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "attached": { - "title": "Attached", - "type": "array", "items": { "type": "string" }, + "type": "array", + "title": "Attached", "description": "If provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "nattached": { - "title": "Nattached", - "type": "array", "items": { "type": "integer" }, + "type": "array", + "title": "Nattached", "description": "If provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the :field:`attached` key.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" } }, + "type": "object", + "required": [ + "name", + "chemical_symbols", + "concentration" + ], + "title": "Species", "description": "A list describing the species of the sites of this structure.\n\nSpecies can represent pure chemical elements, virtual-crystal atoms representing a\nstatistical occupation of a given site by multiple chemical elements, and/or a\nlocation to which there are attached atoms, i.e., atoms whose precise location are\nunknown beyond that they are attached to that position (frequently used to indicate\nhydrogen atoms attached to another element, e.g., a carbon with three attached\nhydrogens might represent a methyl group, -CH3).\n\n- **Examples**:\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]`: any site with this species is occupied by a Ti atom.\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]`: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.\n - `[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]`: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.\n - `[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]`: any site with this species is occupied by a carbon isotope with mass 12.\n - `[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]`: any site with this species is occupied by a carbon isotope with mass 13.\n - `[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]`: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms." }, "StructureFeatures": { - "title": "StructureFeatures", "enum": [ "disorder", "implicit_atoms", "site_attachments", "assemblies" ], + "title": "StructureFeatures", "description": "Enumeration of structure_features values" }, "StructureRelationship": { - "title": "StructureRelationship", - "type": "object", "properties": { "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/RelationshipLinks" } ], + "title": "Links", "description": "a links object containing at least one of the following: self, related" }, "data": { - "title": "Data", - "uniqueItems": true, "anyOf": [ { "$ref": "#/components/schemas/BaseRelationshipResource" }, { - "type": "array", "items": { "$ref": "#/components/schemas/BaseRelationshipResource" - } + }, + "type": "array" } ], + "uniqueItems": true, + "title": "Data", "description": "Resource linkage" }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "a meta object that contains non-standard meta-information about the relationship." } }, + "type": "object", + "title": "StructureRelationship", "description": "Similar to normal JSON API relationship, but with addition of OPTIONAL meta field for a resource." }, "StructureResource": { - "title": "StructureResource", - "required": [ - "id", - "type", - "attributes" - ], - "type": "object", "properties": { "id": { - "title": "Id", "type": "string", + "title": "Id", "description": "An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`", "x-optimade-support": "must", "x-optimade-queryable": "must" }, "type": { - "title": "Type", - "pattern": "^structures$", "type": "string", + "pattern": "^structures$", + "title": "Type", "description": "The name of the type of an entry.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type `` and ID `` MUST be returned in response to a request for `//` under the versioned base URL.\n\n- **Examples**:\n - `\"structures\"`", "default": "structures", "x-optimade-support": "must", "x-optimade-queryable": "must" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ResourceLinks" } ], + "title": "Links", "description": "a links object containing links related to the resource." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/EntryMetadata" } ], + "title": "Meta", "description": "A dictionary, containing entry and property-specific metadata for a given entry." }, "attributes": { "$ref": "#/components/schemas/StructureResourceAttributes" }, "relationships": { - "title": "Relationships", "allOf": [ { "$ref": "#/components/schemas/EntryRelationships" } ], - "description": "A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object." - } - }, - "description": "Representing a structure." - }, - "StructureResourceAttributes": { - "title": "StructureResourceAttributes", - "required": [ - "last_modified", - "elements", - "nelements", - "elements_ratios", - "chemical_formula_descriptive", - "chemical_formula_reduced", - "chemical_formula_anonymous", - "dimension_types", - "nperiodic_dimensions", - "lattice_vectors", - "cartesian_site_positions", - "nsites", - "species", - "species_at_sites", - "structure_features" - ], + "title": "Relationships", + "description": "A dictionary containing references to other entries according to the description in section Relationships encoded as [JSON API Relationships](https://jsonapi.org/format/1.0/#document-resource-object-relationships).\nThe OPTIONAL human-readable description of the relationship MAY be provided in the `description` field inside the `meta` dictionary of the JSON API resource identifier object." + } + }, "type": "object", + "required": [ + "id", + "type", + "attributes" + ], + "title": "StructureResource", + "description": "Representing a structure." + }, + "StructureResourceAttributes": { "properties": { "immutable_id": { - "title": "Immutable Id", "type": "string", + "title": "Immutable Id", "description": "The entry's immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)", "x-optimade-support": "optional", "x-optimade-queryable": "must" }, "last_modified": { - "title": "Last Modified", "type": "string", - "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "format": "date-time", + "title": "Last Modified", + "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", "nullable": true, "x-optimade-support": "should", "x-optimade-queryable": "must" }, "elements": { - "title": "Elements", - "type": "array", "items": { "type": "string" }, + "type": "array", + "title": "Elements", "description": "The chemical symbols of the different elements present in the structure.\n\n- **Type**: list of strings.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The strings are the chemical symbols, i.e., either a single uppercase letter or an uppercase letter followed by a number of lowercase letters.\n - The order MUST be alphabetical.\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements_ratios`, if the latter is provided.\n - Note: This property SHOULD NOT contain the string \"X\" to indicate non-chemical elements or \"vacancy\" to indicate vacancies (in contrast to the field `chemical_symbols` for the `species` property).\n\n- **Examples**:\n - `[\"Si\"]`\n - `[\"Al\",\"O\",\"Si\"]`\n\n- **Query examples**:\n - A filter that matches all records of structures that contain Si, Al **and** O, and possibly other elements: `elements HAS ALL \"Si\", \"Al\", \"O\"`.\n - To match structures with exactly these three elements, use `elements HAS ALL \"Si\", \"Al\", \"O\" AND elements LENGTH 3`.\n - Note: length queries on this property can be equivalently formulated by filtering on the `nelements`_ property directly.", "nullable": true, "x-optimade-support": "should", "x-optimade-queryable": "must" }, "nelements": { - "title": "Nelements", "type": "integer", + "title": "Nelements", "description": "Number of different elements in the structure as an integer.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - MUST be equal to the lengths of the list properties `elements` and `elements_ratios`, if they are provided.\n\n- **Examples**:\n - `3`\n\n- **Querying**:\n - Note: queries on this property can equivalently be formulated using `elements LENGTH`.\n - A filter that matches structures that have exactly 4 elements: `nelements=4`.\n - A filter that matches structures that have between 2 and 7 elements: `nelements>=2 AND nelements<=7`.", - "nullable": true, "x-optimade-support": "should", + "nullable": true, "x-optimade-queryable": "must" }, "elements_ratios": { - "title": "Elements Ratios", - "type": "array", "items": { "type": "number" }, + "type": "array", + "title": "Elements Ratios", "description": "Relative proportions of different elements in the structure.\n\n- **Type**: list of floats\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - Composed by the proportions of elements in the structure as a list of floating point numbers.\n - The sum of the numbers MUST be 1.0 (within floating point accuracy)\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements`, if the latter is provided.\n\n- **Examples**:\n - `[1.0]`\n - `[0.3333333333333333, 0.2222222222222222, 0.4444444444444444]`\n\n- **Query examples**:\n - Note: Useful filters can be formulated using the set operator syntax for correlated values.\n However, since the values are floating point values, the use of equality comparisons is generally inadvisable.\n - OPTIONAL: a filter that matches structures where approximately 1/3 of the atoms in the structure are the element Al is: `elements:elements_ratios HAS ALL \"Al\":>0.3333, \"Al\":<0.3334`.", "nullable": true, "x-optimade-support": "should", "x-optimade-queryable": "must" }, "chemical_formula_descriptive": { - "title": "Chemical Formula Descriptive", "type": "string", + "title": "Chemical Formula Descriptive", "description": "The chemical formula for a structure as a string in a form chosen by the API implementation.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The chemical formula is given as a string consisting of properly capitalized element symbols followed by integers or decimal numbers, balanced parentheses, square, and curly brackets `(`,`)`, `[`,`]`, `{`, `}`, commas, the `+`, `-`, `:` and `=` symbols. The parentheses are allowed to be followed by a number. Spaces are allowed anywhere except within chemical symbols. The order of elements and any groupings indicated by parentheses or brackets are chosen freely by the API implementation.\n - The string SHOULD be arithmetically consistent with the element ratios in the `chemical_formula_reduced` property.\n - It is RECOMMENDED, but not mandatory, that symbols, parentheses and brackets, if used, are used with the meanings prescribed by [IUPAC's Nomenclature of Organic Chemistry](https://www.qmul.ac.uk/sbcs/iupac/bibliog/blue.html).\n\n- **Examples**:\n - `\"(H2O)2 Na\"`\n - `\"NaCl\"`\n - `\"CaCO3\"`\n - `\"CCaO3\"`\n - `\"(CH3)3N+ - [CH2]2-OH = Me3N+ - CH2 - CH2OH\"`\n\n- **Query examples**:\n - Note: the free-form nature of this property is likely to make queries on it across different databases inconsistent.\n - A filter that matches an exactly given formula: `chemical_formula_descriptive=\"(H2O)2 Na\"`.\n - A filter that does a partial match: `chemical_formula_descriptive CONTAINS \"H2O\"`.", - "nullable": true, "x-optimade-support": "should", + "nullable": true, "x-optimade-queryable": "must" }, "chemical_formula_reduced": { - "title": "Chemical Formula Reduced", - "pattern": "(^$)|^([A-Z][a-z]?([2-9]|[1-9]\\d+)?)+$", "type": "string", + "pattern": "(^$)|^([A-Z][a-z]?([2-9]|[1-9]\\d+)?)+$", + "title": "Chemical Formula Reduced", "description": "The reduced chemical formula for a structure as a string with element symbols and integer chemical proportion numbers.\nThe proportion number MUST be omitted if it is 1.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property.\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\n Intricate queries on formula components are instead suggested to be formulated using set-type filter operators on the multi valued `elements` and `elements_ratios` properties.\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\n - Elements MUST be placed in alphabetical order, followed by their integer chemical proportion number.\n - For structures with no partial occupation, the chemical proportion numbers are the smallest integers for which the chemical proportion is exactly correct.\n - For structures with partial occupation, the chemical proportion numbers are integers that within reasonable approximation indicate the correct chemical proportions. The precise details of how to perform the rounding is chosen by the API implementation.\n - No spaces or separators are allowed.\n\n- **Examples**:\n - `\"H2NaO\"`\n - `\"ClNa\"`\n - `\"CCaO3\"`\n\n- **Query examples**:\n - A filter that matches an exactly given formula is `chemical_formula_reduced=\"H2NaO\"`.", "nullable": true, "x-optimade-support": "should", "x-optimade-queryable": "must" }, "chemical_formula_hill": { - "title": "Chemical Formula Hill", - "pattern": "(^$)|^([A-Z][a-z]?([2-9]|[1-9]\\d+)?)+$", "type": "string", + "pattern": "(^$)|^([A-Z][a-z]?([2-9]|[1-9]\\d+)?)+$", + "title": "Chemical Formula Hill", "description": "The chemical formula for a structure in [Hill form](https://dx.doi.org/10.1021/ja02046a005) with element symbols followed by integer chemical proportion numbers. The proportion number MUST be omitted if it is 1.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, only a subset of the filter features MAY be supported.\n - The overall scale factor of the chemical proportions is chosen such that the resulting values are integers that indicate the most chemically relevant unit of which the system is composed.\n For example, if the structure is a repeating unit cell with four hydrogens and four oxygens that represents two hydroperoxide molecules, `chemical_formula_hill` is `\"H2O2\"` (i.e., not `\"HO\"`, nor `\"H4O4\"`).\n - If the chemical insight needed to ascribe a Hill formula to the system is not present, the property MUST be handled as unset.\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\n - Elements MUST be placed in [Hill order](https://dx.doi.org/10.1021/ja02046a005), followed by their integer chemical proportion number.\n Hill order means: if carbon is present, it is placed first, and if also present, hydrogen is placed second.\n After that, all other elements are ordered alphabetically.\n If carbon is not present, all elements are ordered alphabetically.\n - If the system has sites with partial occupation and the total occupations of each element do not all sum up to integers, then the Hill formula SHOULD be handled as unset.\n - No spaces or separators are allowed.\n\n- **Examples**:\n - `\"H2O2\"`\n\n- **Query examples**:\n - A filter that matches an exactly given formula is `chemical_formula_hill=\"H2O2\"`.", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "chemical_formula_anonymous": { - "title": "Chemical Formula Anonymous", - "pattern": "(^$)|^([A-Z][a-z]?([2-9]|[1-9]\\d+)?)+$", "type": "string", + "pattern": "(^$)|^([A-Z][a-z]?([2-9]|[1-9]\\d+)?)+$", + "title": "Chemical Formula Anonymous", "description": "The anonymous formula is the `chemical_formula_reduced`, but where the elements are instead first ordered by their chemical proportion number, and then, in order left to right, replaced by anonymous symbols A, B, C, ..., Z, Aa, Ba, ..., Za, Ab, Bb, ... and so on.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property.\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\n\n- **Examples**:\n - `\"A2B\"`\n - `\"A42B42C16D12E10F9G5\"`\n\n- **Querying**:\n - A filter that matches an exactly given formula is `chemical_formula_anonymous=\"A2B\"`.", "nullable": true, "x-optimade-support": "should", "x-optimade-queryable": "must" }, "dimension_types": { - "title": "Dimension Types", - "maxItems": 3, - "minItems": 3, - "type": "array", "items": { "$ref": "#/components/schemas/Periodicity" }, + "type": "array", + "maxItems": 3, + "minItems": 3, + "title": "Dimension Types", "description": "List of three integers.\nFor each of the three directions indicated by the three lattice vectors (see property `lattice_vectors`), this list indicates if the direction is periodic (value `1`) or non-periodic (value `0`).\nNote: the elements in this list each refer to the direction of the corresponding entry in `lattice_vectors` and *not* the Cartesian x, y, z directions.\n\n- **Type**: list of integers.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n - MUST be a list of length 3.\n - Each integer element MUST assume only the value 0 or 1.\n\n- **Examples**:\n - For a molecule: `[0, 0, 0]`\n - For a wire along the direction specified by the third lattice vector: `[0, 0, 1]`\n - For a 2D surface/slab, periodic on the plane defined by the first and third lattice vectors: `[1, 0, 1]`\n - For a bulk 3D system: `[1, 1, 1]`", - "nullable": true, "x-optimade-support": "should", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "nullable": true }, "nperiodic_dimensions": { - "title": "Nperiodic Dimensions", "type": "integer", + "title": "Nperiodic Dimensions", "description": "An integer specifying the number of periodic dimensions in the structure, equivalent to the number of non-zero entries in `dimension_types`.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The integer value MUST be between 0 and 3 inclusive and MUST be equal to the sum of the items in the `dimension_types` property.\n - This property only reflects the treatment of the lattice vectors provided for the structure, and not any physical interpretation of the dimensionality of its contents.\n\n- **Examples**:\n - `2` should be indicated in cases where `dimension_types` is any of `[1, 1, 0]`, `[1, 0, 1]`, `[0, 1, 1]`.\n\n- **Query examples**:\n - Match only structures with exactly 3 periodic dimensions: `nperiodic_dimensions=3`\n - Match all structures with 2 or fewer periodic dimensions: `nperiodic_dimensions<=2`", - "nullable": true, "x-optimade-support": "should", + "nullable": true, "x-optimade-queryable": "must" }, "lattice_vectors": { - "title": "Lattice Vectors", - "maxItems": 3, - "minItems": 3, - "type": "array", "items": { - "maxItems": 3, - "minItems": 3, - "type": "array", "items": { "type": "number" - } + }, + "type": "array", + "maxItems": 3, + "minItems": 3 }, + "type": "array", + "maxItems": 3, + "minItems": 3, + "title": "Lattice Vectors", "description": "The three lattice vectors in Cartesian coordinates, in \u00e5ngstr\u00f6m (\u00c5).\n\n- **Type**: list of list of floats or unknown values.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - MUST be a list of three vectors *a*, *b*, and *c*, where each of the vectors MUST BE a list of the vector's coordinates along the x, y, and z Cartesian coordinates.\n (Therefore, the first index runs over the three lattice vectors and the second index runs over the x, y, z Cartesian coordinates).\n - For databases that do not define an absolute Cartesian system (e.g., only defining the length and angles between vectors), the first lattice vector SHOULD be set along *x* and the second on the *xy*-plane.\n - MUST always contain three vectors of three coordinates each, independently of the elements of property `dimension_types`.\n The vectors SHOULD by convention be chosen so the determinant of the `lattice_vectors` matrix is different from zero.\n The vectors in the non-periodic directions have no significance beyond fulfilling these requirements.\n - The coordinates of the lattice vectors of non-periodic dimensions (i.e., those dimensions for which `dimension_types` is `0`) MAY be given as a list of all `null` values.\n If a lattice vector contains the value `null`, all coordinates of that lattice vector MUST be `null`.\n\n- **Examples**:\n - `[[4.0,0.0,0.0],[0.0,4.0,0.0],[0.0,1.0,4.0]]` represents a cell, where the first vector is `(4, 0, 0)`, i.e., a vector aligned along the `x` axis of length 4 \u00c5; the second vector is `(0, 4, 0)`; and the third vector is `(0, 1, 4)`.", - "nullable": true, "x-optimade-support": "should", + "x-optimade-queryable": "optional", "x-optimade-unit": "\u00c5", - "x-optimade-queryable": "optional" + "nullable": true }, "cartesian_site_positions": { - "title": "Cartesian Site Positions", - "type": "array", "items": { - "maxItems": 3, - "minItems": 3, - "type": "array", "items": { "type": "number" - } + }, + "type": "array", + "maxItems": 3, + "minItems": 3 }, + "type": "array", + "title": "Cartesian Site Positions", "description": "Cartesian positions of each site in the structure.\nA site is usually used to describe positions of atoms; what atoms can be encountered at a given site is conveyed by the `species_at_sites` property, and the species themselves are described in the `species` property.\n\n- **Type**: list of list of floats\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - It MUST be a list of length equal to the number of sites in the structure, where every element is a list of the three Cartesian coordinates of a site expressed as float values in the unit angstrom (\u00c5).\n - An entry MAY have multiple sites at the same Cartesian position (for a relevant use of this, see e.g., the property `assemblies`).\n\n- **Examples**:\n - `[[0,0,0],[0,0,2]]` indicates a structure with two sites, one sitting at the origin and one along the (positive) *z*-axis, 2 \u00c5 away from the origin.", - "nullable": true, "x-optimade-support": "should", + "x-optimade-queryable": "optional", "x-optimade-unit": "\u00c5", - "x-optimade-queryable": "optional" + "nullable": true }, "nsites": { - "title": "Nsites", "type": "integer", + "title": "Nsites", "description": "An integer specifying the length of the `cartesian_site_positions` property.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `42`\n\n- **Query examples**:\n - Match only structures with exactly 4 sites: `nsites=4`\n - Match structures that have between 2 and 7 sites: `nsites>=2 AND nsites<=7`", - "nullable": true, "x-optimade-support": "should", + "nullable": true, "x-optimade-queryable": "must" }, "species": { - "title": "Species", - "type": "array", "items": { "$ref": "#/components/schemas/Species" }, + "type": "array", + "title": "Species", "description": "A list describing the species of the sites of this structure.\nSpecies can represent pure chemical elements, virtual-crystal atoms representing a statistical occupation of a given site by multiple chemical elements, and/or a location to which there are attached atoms, i.e., atoms whose precise location are unknown beyond that they are attached to that position (frequently used to indicate hydrogen atoms attached to another element, e.g., a carbon with three attached hydrogens might represent a methyl group, -CH3).\n\n- **Type**: list of dictionary with keys:\n - `name`: string (REQUIRED)\n - `chemical_symbols`: list of strings (REQUIRED)\n - `concentration`: list of float (REQUIRED)\n - `attached`: list of strings (REQUIRED)\n - `nattached`: list of integers (OPTIONAL)\n - `mass`: list of floats (OPTIONAL)\n - `original_name`: string (OPTIONAL).\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - Each list member MUST be a dictionary with the following keys:\n - **name**: REQUIRED; gives the name of the species; the **name** value MUST be unique in the `species` list;\n - **chemical_symbols**: REQUIRED; MUST be a list of strings of all chemical elements composing this species.\n Each item of the list MUST be one of the following:\n - a valid chemical-element symbol, or\n - the special value `\"X\"` to represent a non-chemical element, or\n - the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\n\n If any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.\n\n - **concentration**: REQUIRED; MUST be a list of floats, with same length as `chemical_symbols`.\n The numbers represent the relative concentration of the corresponding chemical symbol in this species.\n The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\n\n - Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\n - Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\n\n Note that concentrations are uncorrelated between different sites (even of the same species).\n\n - **attached**: OPTIONAL; if provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.\n\n - **nattached**: OPTIONAL; if provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the `attached` key.\n\n The implementation MUST include either both or none of the `attached` and `nattached` keys, and if they are provided, they MUST be of the same length.\n Furthermore, if they are provided, the `structure_features` property MUST include the string `site_attachments`.\n\n - **mass**: OPTIONAL. If present MUST be a list of floats, with the same length as `chemical_symbols`, providing element masses expressed in a.m.u.\n Elements denoting vacancies MUST have masses equal to 0.\n\n - **original_name**: OPTIONAL. Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\n\n Note: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.\n\n The main use of this field is for source databases that use species names, containing characters that are not allowed (see description of the list property `species_at_sites`).\n\n - For systems that have only species formed by a single chemical symbol, and that have at most one species per chemical symbol, SHOULD use the chemical symbol as species name (e.g., `\"Ti\"` for titanium, `\"O\"` for oxygen, etc.)\n However, note that this is OPTIONAL, and client implementations MUST NOT assume that the key corresponds to a chemical symbol, nor assume that if the species name is a valid chemical symbol, that it represents a species with that chemical symbol.\n This means that a species `{\"name\": \"C\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]}` is valid and represents a titanium species (and *not* a carbon species).\n - It is NOT RECOMMENDED that a structure includes species that do not have at least one corresponding site.\n\n- **Examples**:\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]`: any site with this species is occupied by a Ti atom.\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]`: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.\n - `[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]`: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.\n - `[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]`: any site with this species is occupied by a carbon isotope with mass 12.\n - `[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]`: any site with this species is occupied by a carbon isotope with mass 13.\n - `[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]`: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms.", "nullable": true, "x-optimade-support": "should", "x-optimade-queryable": "optional" }, "species_at_sites": { - "title": "Species At Sites", - "type": "array", "items": { "type": "string" }, + "type": "array", + "title": "Species At Sites", "description": "Name of the species at each site (where values for sites are specified with the same order of the property `cartesian_site_positions`).\nThe properties of the species are found in the property `species`.\n\n- **Type**: list of strings.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - MUST have length equal to the number of sites in the structure (first dimension of the list property `cartesian_site_positions`).\n - Each species name mentioned in the `species_at_sites` list MUST be described in the list property `species` (i.e. for each value in the `species_at_sites` list there MUST exist exactly one dictionary in the `species` list with the `name` attribute equal to the corresponding `species_at_sites` value).\n - Each site MUST be associated only to a single species.\n **Note**: However, species can represent mixtures of atoms, and multiple species MAY be defined for the same chemical element.\n This latter case is useful when different atoms of the same type need to be grouped or distinguished, for instance in simulation codes to assign different initial spin states.\n\n- **Examples**:\n - `[\"Ti\",\"O2\"]` indicates that the first site is hosting a species labeled `\"Ti\"` and the second a species labeled `\"O2\"`.\n - `[\"Ac\", \"Ac\", \"Ag\", \"Ir\"]` indicating the first two sites contains the `\"Ac\"` species, while the third and fourth sites contain the `\"Ag\"` and `\"Ir\"` species, respectively.", "nullable": true, "x-optimade-support": "should", "x-optimade-queryable": "optional" }, "assemblies": { - "title": "Assemblies", - "type": "array", "items": { "$ref": "#/components/schemas/Assembly" }, + "type": "array", + "title": "Assemblies", "description": "A description of groups of sites that are statistically correlated.\n\n- **Type**: list of dictionary with keys:\n - `sites_in_groups`: list of list of integers (REQUIRED)\n - `group_probabilities`: list of floats (REQUIRED)\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - The property SHOULD be `null` for entries that have no partial occupancies.\n - If present, the correct flag MUST be set in the list `structure_features`.\n - Client implementations MUST check its presence (as its presence changes the interpretation of the structure).\n - If present, it MUST be a list of dictionaries, each of which represents an assembly and MUST have the following two keys:\n - **sites_in_groups**: Index of the sites (0-based) that belong to each group for each assembly.\n\n Example: `[[1], [2]]`: two groups, one with the second site, one with the third.\n Example: `[[1,2], [3]]`: one group with the second and third site, one with the fourth.\n\n - **group_probabilities**: Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\n It SHOULD sum to one.\n See below for examples of how to specify the probability of the occurrence of a vacancy.\n The possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.\n\n - If a site is not present in any group, it means that it is present with 100 % probability (as if no assembly was specified).\n - A site MUST NOT appear in more than one group.\n\n- **Examples** (for each entry of the assemblies list):\n - `{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}`: the first site and the second site never occur at the same time in the unit cell.\n Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.\n - `{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}`: the second and third site are either present together or not present; they form the first group of atoms for this assembly.\n The second group is formed by the fourth site.\n Sites of the first group (the second and the third) are never present at the same time as the fourth site.\n 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent).\n\n- **Notes**:\n - Assemblies are essential to represent, for instance, the situation where an atom can statistically occupy two different positions (sites).\n\n - By defining groups, it is possible to represent, e.g., the case where a functional molecule (and not just one atom) is either present or absent (or the case where it it is present in two conformations)\n\n - Considerations on virtual alloys and on vacancies: In the special case of a virtual alloy, these specifications allow two different, equivalent ways of specifying them.\n For instance, for a site at the origin with 30 % probability of being occupied by Si, 50 % probability of being occupied by Ge, and 20 % of being a vacancy, the following two representations are possible:\n\n - Using a single species:\n ```json\n {\n \"cartesian_site_positions\": [[0,0,0]],\n \"species_at_sites\": [\"SiGe-vac\"],\n \"species\": [\n {\n \"name\": \"SiGe-vac\",\n \"chemical_symbols\": [\"Si\", \"Ge\", \"vacancy\"],\n \"concentration\": [0.3, 0.5, 0.2]\n }\n ]\n // ...\n }\n ```\n\n - Using multiple species and the assemblies:\n ```json\n {\n \"cartesian_site_positions\": [ [0,0,0], [0,0,0], [0,0,0] ],\n \"species_at_sites\": [\"Si\", \"Ge\", \"vac\"],\n \"species\": [\n { \"name\": \"Si\", \"chemical_symbols\": [\"Si\"], \"concentration\": [1.0] },\n { \"name\": \"Ge\", \"chemical_symbols\": [\"Ge\"], \"concentration\": [1.0] },\n { \"name\": \"vac\", \"chemical_symbols\": [\"vacancy\"], \"concentration\": [1.0] }\n ],\n \"assemblies\": [\n {\n \"sites_in_groups\": [ [0], [1], [2] ],\n \"group_probabilities\": [0.3, 0.5, 0.2]\n }\n ]\n // ...\n }\n ```\n\n - It is up to the database provider to decide which representation to use, typically depending on the internal format in which the structure is stored.\n However, given a structure identified by a unique ID, the API implementation MUST always provide the same representation for it.\n\n - The probabilities of occurrence of different assemblies are uncorrelated.\n So, for instance in the following case with two assemblies:\n ```json\n {\n \"assemblies\": [\n {\n \"sites_in_groups\": [ [0], [1] ],\n \"group_probabilities\": [0.2, 0.8],\n },\n {\n \"sites_in_groups\": [ [2], [3] ],\n \"group_probabilities\": [0.3, 0.7]\n }\n ]\n }\n ```\n\n Site 0 is present with a probability of 20 % and site 1 with a probability of 80 %. These two sites are correlated (either site 0 or 1 is present). Similarly, site 2 is present with a probability of 30 % and site 3 with a probability of 70 %.\n These two sites are correlated (either site 2 or 3 is present).\n However, the presence or absence of sites 0 and 1 is not correlated with the presence or absence of sites 2 and 3 (in the specific example, the pair of sites (0, 2) can occur with 0.2*0.3 = 6 % probability; the pair (0, 3) with 0.2*0.7 = 14 % probability; the pair (1, 2) with 0.8*0.3 = 24 % probability; and the pair (1, 3) with 0.8*0.7 = 56 % probability).", "x-optimade-support": "optional", "x-optimade-queryable": "optional" }, "structure_features": { - "title": "Structure Features", - "type": "array", "items": { "$ref": "#/components/schemas/StructureFeatures" }, + "type": "array", + "title": "Structure Features", "description": "A list of strings that flag which special features are used by the structure.\n\n- **Type**: list of strings\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property.\n Filters on the list MUST support all mandatory HAS-type queries.\n Filter operators for comparisons on the string components MUST support equality, support for other comparison operators are OPTIONAL.\n - MUST be an empty list if no special features are used.\n - MUST be sorted alphabetically.\n - If a special feature listed below is used, the list MUST contain the corresponding string.\n - If a special feature listed below is not used, the list MUST NOT contain the corresponding string.\n - **List of strings used to indicate special structure features**:\n - `disorder`: this flag MUST be present if any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element.\n - `implicit_atoms`: this flag MUST be present if the structure contains atoms that are not assigned to sites via the property `species_at_sites` (e.g., because their positions are unknown).\n When this flag is present, the properties related to the chemical formula will likely not match the type and count of atoms represented by the `species_at_sites`, `species` and `assemblies` properties.\n - `site_attachments`: this flag MUST be present if any one entry in the `species` list includes `attached` and `nattached`.\n - `assemblies`: this flag MUST be present if the property `assemblies` is present.\n\n- **Examples**: A structure having implicit atoms and using assemblies: `[\"assemblies\", \"implicit_atoms\"]`", "x-optimade-support": "must", "x-optimade-queryable": "must" } }, + "type": "object", + "required": [ + "last_modified", + "elements", + "nelements", + "elements_ratios", + "chemical_formula_descriptive", + "chemical_formula_reduced", + "chemical_formula_anonymous", + "dimension_types", + "nperiodic_dimensions", + "lattice_vectors", + "cartesian_site_positions", + "nsites", + "species", + "species_at_sites", + "structure_features" + ], + "title": "StructureResourceAttributes", "description": "This class contains the Field for the attributes used to represent a structure, e.g. unit cell, atoms, positions." }, "StructureResponseMany": { - "title": "StructureResponseMany", - "required": [ - "data", - "meta" - ], - "type": "object", "properties": { "data": { - "title": "Data", - "uniqueItems": true, "anyOf": [ { - "type": "array", "items": { "$ref": "#/components/schemas/StructureResource" - } + }, + "type": "array" }, { - "type": "array", "items": { "type": "object" - } + }, + "type": "array" } ], + "uniqueItems": true, + "title": "Data", "description": "List of unique OPTIMADE structures entry resource objects." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/ResponseMeta" } ], + "title": "Meta", "description": "A meta object containing non-standard information" }, "errors": { - "title": "Errors", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/Error" }, + "type": "array", + "uniqueItems": true, + "title": "Errors", "description": "A list of unique errors" }, "included": { - "title": "Included", - "uniqueItems": true, "anyOf": [ { - "type": "array", "items": { "$ref": "#/components/schemas/EntryResource" - } + }, + "type": "array" }, { - "type": "array", "items": { "type": "object" - } + }, + "type": "array" } - ] + ], + "uniqueItems": true, + "title": "Included" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ToplevelLinks" } ], + "title": "Links", "description": "Links associated with the primary data or errors" }, "jsonapi": { - "title": "Jsonapi", "allOf": [ { "$ref": "#/components/schemas/JsonApi" } ], + "title": "Jsonapi", "description": "Information about the JSON API used" } }, - "description": "errors are not allowed" - }, - "StructureResponseOne": { - "title": "StructureResponseOne", + "type": "object", "required": [ "data", "meta" ], - "type": "object", + "title": "StructureResponseMany", + "description": "errors are not allowed" + }, + "StructureResponseOne": { "properties": { "data": { - "title": "Data", "anyOf": [ { "$ref": "#/components/schemas/StructureResource" @@ -4048,225 +4041,232 @@ "type": "object" } ], + "title": "Data", "description": "A single structures entry resource." }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/ResponseMeta" } ], + "title": "Meta", "description": "A meta object containing non-standard information" }, "errors": { - "title": "Errors", - "uniqueItems": true, - "type": "array", "items": { "$ref": "#/components/schemas/Error" }, + "type": "array", + "uniqueItems": true, + "title": "Errors", "description": "A list of unique errors" }, "included": { - "title": "Included", - "uniqueItems": true, "anyOf": [ { - "type": "array", "items": { "$ref": "#/components/schemas/EntryResource" - } + }, + "type": "array" }, { - "type": "array", "items": { "type": "object" - } + }, + "type": "array" } - ] + ], + "uniqueItems": true, + "title": "Included" }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ToplevelLinks" } ], + "title": "Links", "description": "Links associated with the primary data or errors" }, "jsonapi": { - "title": "Jsonapi", "allOf": [ { "$ref": "#/components/schemas/JsonApi" } ], + "title": "Jsonapi", "description": "Information about the JSON API used" } }, + "type": "object", + "required": [ + "data", + "meta" + ], + "title": "StructureResponseOne", "description": "errors are not allowed" }, "ToplevelLinks": { - "title": "ToplevelLinks", - "type": "object", "properties": { "self": { - "title": "Self", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Self", "description": "A link to itself" }, "related": { - "title": "Related", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Related", "description": "A related resource link" }, "first": { - "title": "First", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "First", "description": "The first page of data" }, "last": { - "title": "Last", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Last", "description": "The last page of data" }, "prev": { - "title": "Prev", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Prev", "description": "The previous page of data" }, "next": { - "title": "Next", "anyOf": [ { + "type": "string", "maxLength": 65536, "minLength": 1, - "type": "string", "format": "uri" }, { "$ref": "#/components/schemas/Link" } ], + "title": "Next", "description": "The next page of data" } }, + "type": "object", + "title": "ToplevelLinks", "description": "A set of Links objects, possibly including pagination" }, "Warnings": { - "title": "Warnings", - "required": [ - "detail", - "type" - ], - "type": "object", "properties": { "id": { - "title": "Id", "type": "string", + "title": "Id", "description": "A unique identifier for this particular occurrence of the problem." }, "links": { - "title": "Links", "allOf": [ { "$ref": "#/components/schemas/ErrorLinks" } ], + "title": "Links", "description": "A links object storing about" }, "code": { - "title": "Code", "type": "string", + "title": "Code", "description": "an application-specific error code, expressed as a string value." }, "title": { - "title": "Title", "type": "string", + "title": "Title", "description": "A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization." }, "detail": { - "title": "Detail", "type": "string", + "title": "Detail", "description": "A human-readable explanation specific to this occurrence of the problem." }, "source": { - "title": "Source", "allOf": [ { "$ref": "#/components/schemas/ErrorSource" } ], + "title": "Source", "description": "An object containing references to the source of the error" }, "meta": { - "title": "Meta", "allOf": [ { "$ref": "#/components/schemas/Meta" } ], + "title": "Meta", "description": "a meta object containing non-standard meta-information about the error." }, "type": { - "title": "Type", - "pattern": "^warning$", "type": "string", + "pattern": "^warning$", + "title": "Type", "description": "Warnings must be of type \"warning\"", "default": "warning" } }, + "type": "object", + "required": [ + "detail", + "type" + ], + "title": "Warnings", "description": "OPTIMADE-specific warning class based on OPTIMADE-specific JSON API Error.\n\nFrom the specification:\n\nA warning resource object is defined similarly to a JSON API error object, but MUST also include the field type, which MUST have the value \"warning\".\nThe field detail MUST be present and SHOULD contain a non-critical message, e.g., reporting unrecognized search attributes or deprecated features.\n\nNote: Must be named \"Warnings\", since \"Warning\" is a built-in Python class." } } diff --git a/optimade/server/query_params.py b/optimade/server/query_params.py index 772232326..473f30046 100644 --- a/optimade/server/query_params.py +++ b/optimade/server/query_params.py @@ -378,12 +378,12 @@ def __init__( api_hint: str = Query( "", description="If the client provides the parameter, the value SHOULD have the format `vMAJOR` or `vMAJOR.MINOR`, where MAJOR is a major version and MINOR is a minor version of the API. For example, if a client appends `api_hint=v1.0` to the query string, the hint provided is for major version 1 and minor version 0.", - regex=r"(v[0-9]+(\.[0-9]+)?)?", + pattern=r"(v[0-9]+(\.[0-9]+)?)?", ), response_fields: str = Query( "", description="A comma-delimited set of fields to be provided in the output.\nIf provided, these fields MUST be returned along with the REQUIRED fields.\nOther OPTIONAL fields MUST NOT be returned when this parameter is present.\nExample: `http://example.com/v1/structures?response_fields=last_modified,nsites`", - regex=r"([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", + pattern=r"([a-z_][a-z_0-9]*(,[a-z_][a-z_0-9]*)*)?", ), filter: str = Query( # pylint: disable=redefined-builtin "", From c87d4369f609df2e4aad84abc329696ffa8ab954 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Thu, 14 Sep 2023 17:40:29 +0200 Subject: [PATCH 65/86] Moved generate_links_partial_data function to routers/utils.py and made it use the request url when no base url was defined in the configuration. --- optimade/server/config.py | 1 - .../entry_collections/entry_collections.py | 19 -------------- optimade/server/routers/utils.py | 26 +++++++++++++++++++ 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/optimade/server/config.py b/optimade/server/config.py index cddc3bdeb..a88d0b895 100644 --- a/optimade/server/config.py +++ b/optimade/server/config.py @@ -71,7 +71,6 @@ class SupportedResponseFormats(Enum): """Enumeration of supported response formats. - 'JSON': [JSON](https://www.json.org/json-en.html) - - 'HDF5': [HDF5](https://portal.hdfgroup.org/display/HDF5/HDF5) - `JSONL`: [JSONL](https://jsonlines.org/) """ diff --git a/optimade/server/entry_collections/entry_collections.py b/optimade/server/entry_collections/entry_collections.py index 345b9f5f0..d148f1dc7 100644 --- a/optimade/server/entry_collections/entry_collections.py +++ b/optimade/server/entry_collections/entry_collections.py @@ -228,7 +228,6 @@ def find( if raw_results: results = [self.resource_mapper.map_back(doc) for doc in raw_results] - self.generate_links_partial_data(results) if single_entry: results = results[0] # type: ignore[assignment] @@ -247,24 +246,6 @@ def find( include_fields, ) - def generate_links_partial_data(self, results): - for entry in results: - if entry.get("meta", {}) and entry["meta"].get("partial_data_links", {}): - for property in entry["meta"]["partial_data_links"]: - for response_format in CONFIG.partial_data_formats: - entry["meta"]["partial_data_links"][property].append( - { - "format": str(response_format.value), - "link": CONFIG.base_url - + "/partial_data/" - + entry["id"] - + "?response_fields=" - + property - + "&response_format=" - + str(response_format.value), - } - ) - @abstractmethod def _run_db_query( self, diff --git a/optimade/server/routers/utils.py b/optimade/server/routers/utils.py index 7cbb2257c..ed384c2d1 100644 --- a/optimade/server/routers/utils.py +++ b/optimade/server/routers/utils.py @@ -258,6 +258,30 @@ def get_base_url( ) +def generate_links_partial_data( + results, + parsed_url_request: Union[ + urllib.parse.ParseResult, urllib.parse.SplitResult, StarletteURL, str + ], +): + for entry in results: + if entry.get("meta", {}) and entry["meta"].get("partial_data_links", {}): + for property in entry["meta"]["partial_data_links"]: + for response_format in CONFIG.partial_data_formats: + entry["meta"]["partial_data_links"][property].append( + { + "format": str(response_format.value), + "link": get_base_url(parsed_url_request) + + "/partial_data/" + + entry["id"] + + "?response_fields=" + + property + + "&response_format=" + + str(response_format.value), + } + ) + + def get_entries( collection: EntryCollection, response: Type[EntryResponseMany], # noqa @@ -282,6 +306,7 @@ def get_entries( included = [] if results is not None: + generate_links_partial_data(results, request.url) included = get_included_relationships(results, ENTRY_COLLECTIONS, include) if more_data_available: @@ -346,6 +371,7 @@ def get_single_entry( included = [] if results is not None: included = get_included_relationships(results, ENTRY_COLLECTIONS, include) + generate_links_partial_data([results], request.url) links = ToplevelLinks(next=None) From 674696957d341c6b0e6b354683792435f8f97684 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Sun, 24 Sep 2023 14:46:22 +0200 Subject: [PATCH 66/86] Expanded model partial_data. --- optimade/models/partial_data.py | 36 ++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/optimade/models/partial_data.py b/optimade/models/partial_data.py index d5d717c54..26e440d01 100644 --- a/optimade/models/partial_data.py +++ b/optimade/models/partial_data.py @@ -5,14 +5,36 @@ from optimade.models.entries import EntryResource from optimade.models.utils import OptimadeField, StrictField, SupportLevel -__all__ = ( - "PartialDataHeader", - "PartialDataResource", -) +__all__ = ("PartialDataHeader", "PartialDataResource", "LinksObject") + + +class LinksObject(BaseModel): + base_url: Optional[str] = OptimadeField( + None, + description="""The base URL of the implementation serving the database to which this property belongs.""", + ) + # todo should it not be item_described_by? check with json Api they may have defined this field name. + item_describedby: Optional[str] = OptimadeField( + None, + description="""A URL to an external JSON Schema that validates the data lines of the response. + The format and requirements on this schema are the same as for the inline schema field :field:`item_schema`. +The format of data lines of the response (i.e., all lines except the first and the last) depends on whether the header object specifies the layout as :val:`"dense"` or :val:`"sparse"`. +""", + ) + + +class PartialDataInfo(BaseModel): + version: str = OptimadeField( + ..., + description="""Specifies the minor version of the partial data format used. + The string MUST be of the format "MAJOR.MINOR", referring to the version of the OPTIMADE standard that describes the format. + The version number string MUST NOT be prefixed by, e.g., "v". In implementations of the present version of the standard, the value MUST be exactly :val:`1.2`. + A client MUST NOT expect to be able to parse the :field:`format` value if the field is not a string of the format MAJOR.MINOR or if the MAJOR version number is unrecognized.""", + ) class PartialDataHeader(BaseModel): - optimade_partial_data: dict = OptimadeField( + optimade_partial_data: PartialDataInfo = OptimadeField( ..., description="""An object identifying the response as being on OPTIMADE partial data format. It MUST contain the following key: @@ -26,7 +48,7 @@ class PartialDataHeader(BaseModel): - **Examples**: - `""optimade-partial-data": {"version": "1.2.0"}"`""", support=SupportLevel.MUST, - ) # Todo add format and link fields + ) layout: Literal["dense", "sparse"] = OptimadeField( ..., description="""A string either equal to "dense" or "sparse" to indicate whether the returned format uses a dense or sparse layout. @@ -109,7 +131,7 @@ class PartialDataHeader(BaseModel): support=SupportLevel.OPTIONAL, ) - links: Optional[dict] = OptimadeField( + links: Optional[LinksObject] = OptimadeField( None, description=""" An object to provide relevant links for the property being provided. It MAY contain the following key: From 63651f10d64e83a488b5ffec4585d664a2cf38c5 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Sat, 30 Sep 2023 15:15:19 +0200 Subject: [PATCH 67/86] use lstrip instead of manually removing / one at a time. --- tests/server/utils.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/server/utils.py b/tests/server/utils.py index 6a046c36b..7449e7cc5 100644 --- a/tests/server/utils.py +++ b/tests/server/utils.py @@ -61,9 +61,7 @@ def request( # pylint: disable=too-many-locals re.match(r"/?v[0-9](.[0-9]){0,2}/", url) is None and not urlparse(url).scheme ): - while url.startswith("/"): - url = url[1:] - url = f"{self.version}/{url}" + url = f"{self.version}/{url.lstrip('/')}" return super(OptimadeTestClient, self).request( method=method, url=url, From 389e67918c9e89d9ae388f31ba0072dc89d19275 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Mon, 2 Oct 2023 18:21:05 +0200 Subject: [PATCH 68/86] Update optimade/models/entries.py update description property_metadata field Co-authored-by: Matthew Evans <7916000+ml-evs@users.noreply.github.com> --- optimade/models/entries.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/optimade/models/entries.py b/optimade/models/entries.py index d0bffac77..c2286fb96 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -109,8 +109,7 @@ class EntryMetadata(Meta): property_metadata: Dict = StrictField( None, - description="""A dictionary, where the keys are the names of the properties in the attributes field and the value is a dictionary containing the metadata for that property. -Database-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes).""", + description="""An object containing per-entry and per-property metadata. The keys are the names of the fields in attributes for which metadata is available. The values belonging to these keys are dictionaries containing the relevant metadata fields. See also [Metadata properties](https://github.com/Materials-Consortia/OPTIMADE/blob/develop/optimade.rst#metadata-properties)""", ) @validator("property_metadata") From 4ec23c9aa70309794e582dc7953dc7488a8cadbe Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Mon, 2 Oct 2023 18:54:57 +0200 Subject: [PATCH 69/86] Update description per entry meta datafield. --- openapi/index_openapi.json | 26 ++-- openapi/openapi.json | 274 ++++++++++++++++++------------------- optimade/models/entries.py | 2 +- 3 files changed, 151 insertions(+), 151 deletions(-) diff --git a/openapi/index_openapi.json b/openapi/index_openapi.json index 2c2c3165f..3517374b7 100644 --- a/openapi/index_openapi.json +++ b/openapi/index_openapi.json @@ -472,7 +472,7 @@ "property_metadata": { "type": "object", "title": "Property Metadata", - "description": "A dictionary, where the keys are the names of the properties in the attributes field and the value is a dictionary containing the metadata for that property.\nDatabase-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes)." + "description": "An object containing per-entry and per-property metadata. The keys are the names of the fields in attributes for which metadata is available. The values belonging to these keys are dictionaries containing the relevant metadata fields. See also [Metadata properties](https://github.com/Materials-Consortia/OPTIMADE/blob/develop/optimade.rst#metadata-properties)" } }, "type": "object", @@ -510,15 +510,15 @@ "type": "string", "title": "Id", "description": "An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`", - "x-optimade-support": "must", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "must" }, "type": { "type": "string", "title": "Type", "description": "The name of the type of an entry.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type `` and ID `` MUST be returned in response to a request for `//` under the versioned base URL.\n\n- **Example**: `\"structures\"`", - "x-optimade-support": "must", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "must" }, "links": { "allOf": [ @@ -536,7 +536,7 @@ } ], "title": "Meta", - "description": "A dictionary, containing entry and property-specific metadata for a given entry." + "description": "A [JSON API meta object](https://jsonapi.org/format/1.1/#document-meta) that is used to communicate metadata." }, "attributes": { "allOf": [ @@ -572,16 +572,16 @@ "type": "string", "title": "Immutable Id", "description": "The entry's immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)", - "x-optimade-support": "optional", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "optional" }, "last_modified": { "type": "string", "format": "date-time", "title": "Last Modified", "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" } }, "type": "object", @@ -1119,8 +1119,8 @@ "type": "string", "title": "Id", "description": "An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`", - "x-optimade-support": "must", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "must" }, "type": { "type": "string", @@ -1145,7 +1145,7 @@ } ], "title": "Meta", - "description": "A dictionary, containing entry and property-specific metadata for a given entry." + "description": "A [JSON API meta object](https://jsonapi.org/format/1.1/#document-meta) that is used to communicate metadata." }, "attributes": { "allOf": [ diff --git a/openapi/openapi.json b/openapi/openapi.json index 3e2a2f51a..4775bbeb8 100644 --- a/openapi/openapi.json +++ b/openapi/openapi.json @@ -1335,8 +1335,8 @@ "type": "array", "title": "Sites In Groups", "description": "Index of the sites (0-based) that belong to each group for each assembly.\n\n- **Examples**:\n - `[[1], [2]]`: two groups, one with the second site, one with the third.\n - `[[1,2], [3]]`: one group with the second and third site, one with the fourth.", - "x-optimade-support": "must", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "must" }, "group_probabilities": { "items": { @@ -1345,8 +1345,8 @@ "type": "array", "title": "Group Probabilities", "description": "Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\nIt SHOULD sum to one.\nSee below for examples of how to specify the probability of the occurrence of a vacancy.\nThe possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.", - "x-optimade-support": "must", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "must" } }, "type": "object", @@ -1722,7 +1722,7 @@ "property_metadata": { "type": "object", "title": "Property Metadata", - "description": "A dictionary, where the keys are the names of the properties in the attributes field and the value is a dictionary containing the metadata for that property.\nDatabase-provider-specific properties need to include the database-provider-specific prefix (see section on Database-Provider-Specific Namespace Prefixes)." + "description": "An object containing per-entry and per-property metadata. The keys are the names of the fields in attributes for which metadata is available. The values belonging to these keys are dictionaries containing the relevant metadata fields. See also [Metadata properties](https://github.com/Materials-Consortia/OPTIMADE/blob/develop/optimade.rst#metadata-properties)" } }, "type": "object", @@ -1760,15 +1760,15 @@ "type": "string", "title": "Id", "description": "An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`", - "x-optimade-support": "must", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "must" }, "type": { "type": "string", "title": "Type", "description": "The name of the type of an entry.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type `` and ID `` MUST be returned in response to a request for `//` under the versioned base URL.\n\n- **Example**: `\"structures\"`", - "x-optimade-support": "must", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "must" }, "links": { "allOf": [ @@ -1786,7 +1786,7 @@ } ], "title": "Meta", - "description": "A dictionary, containing entry and property-specific metadata for a given entry." + "description": "A [JSON API meta object](https://jsonapi.org/format/1.1/#document-meta) that is used to communicate metadata." }, "attributes": { "allOf": [ @@ -1822,16 +1822,16 @@ "type": "string", "title": "Immutable Id", "description": "The entry's immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)", - "x-optimade-support": "optional", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "optional" }, "last_modified": { "type": "string", "format": "date-time", "title": "Last Modified", "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" } }, "type": "object", @@ -2228,8 +2228,8 @@ "type": "string", "title": "Id", "description": "An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`", - "x-optimade-support": "must", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "must" }, "type": { "type": "string", @@ -2254,7 +2254,7 @@ } ], "title": "Meta", - "description": "A dictionary, containing entry and property-specific metadata for a given entry." + "description": "A [JSON API meta object](https://jsonapi.org/format/1.1/#document-meta) that is used to communicate metadata." }, "attributes": { "allOf": [ @@ -2529,22 +2529,22 @@ "type": "string", "title": "Name", "description": "Full name of the person, REQUIRED.", - "x-optimade-support": "must", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "must" }, "firstname": { "type": "string", "title": "Firstname", "description": "First name of the person.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "lastname": { "type": "string", "title": "Lastname", "description": "Last name of the person.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" } }, "type": "object", @@ -2644,8 +2644,8 @@ "type": "string", "title": "Id", "description": "An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`", - "x-optimade-support": "must", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "must" }, "type": { "type": "string", @@ -2653,8 +2653,8 @@ "title": "Type", "description": "The name of the type of an entry.\n- **Type**: string.\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type and ID MUST be returned in response to a request for `//` under the versioned base URL.\n- **Example**: `\"structures\"`", "default": "references", - "x-optimade-support": "must", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "must" }, "links": { "allOf": [ @@ -2672,7 +2672,7 @@ } ], "title": "Meta", - "description": "A dictionary, containing entry and property-specific metadata for a given entry." + "description": "A [JSON API meta object](https://jsonapi.org/format/1.1/#document-meta) that is used to communicate metadata." }, "attributes": { "$ref": "#/components/schemas/ReferenceResourceAttributes" @@ -2702,16 +2702,16 @@ "type": "string", "title": "Immutable Id", "description": "The entry's immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)", - "x-optimade-support": "optional", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "optional" }, "last_modified": { "type": "string", "format": "date-time", "title": "Last Modified", "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", - "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "should" }, "authors": { "items": { @@ -2720,8 +2720,8 @@ "type": "array", "title": "Authors", "description": "List of person objects containing the authors of the reference.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "editors": { "items": { @@ -2730,15 +2730,15 @@ "type": "array", "title": "Editors", "description": "List of person objects containing the editors of the reference.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "doi": { "type": "string", "title": "Doi", "description": "The digital object identifier of the reference.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "url": { "type": "string", @@ -2754,155 +2754,155 @@ "type": "string", "title": "Address", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "annote": { "type": "string", "title": "Annote", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "booktitle": { "type": "string", "title": "Booktitle", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "chapter": { "type": "string", "title": "Chapter", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "crossref": { "type": "string", "title": "Crossref", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "edition": { "type": "string", "title": "Edition", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "howpublished": { "type": "string", "title": "Howpublished", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "institution": { "type": "string", "title": "Institution", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "journal": { "type": "string", "title": "Journal", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "key": { "type": "string", "title": "Key", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "month": { "type": "string", "title": "Month", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "note": { "type": "string", "title": "Note", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "number": { "type": "string", "title": "Number", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "organization": { "type": "string", "title": "Organization", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "pages": { "type": "string", "title": "Pages", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "publisher": { "type": "string", "title": "Publisher", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "school": { "type": "string", "title": "School", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "series": { "type": "string", "title": "Series", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "title": { "type": "string", "title": "Title", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "bib_type": { "type": "string", "title": "Bib Type", "description": "Type of the reference, corresponding to the **type** property in the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "volume": { "type": "string", "title": "Volume", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "year": { "type": "string", "title": "Year", "description": "Meaning of property matches the BiBTeX specification.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" } }, "type": "object", @@ -3322,8 +3322,8 @@ "type": "string", "title": "Name", "description": "Gives the name of the species; the **name** value MUST be unique in the `species` list.", - "x-optimade-support": "must", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "must" }, "chemical_symbols": { "items": { @@ -3332,8 +3332,8 @@ "type": "array", "title": "Chemical Symbols", "description": "MUST be a list of strings of all chemical elements composing this species. Each item of the list MUST be one of the following:\n\n- a valid chemical-element symbol, or\n- the special value `\"X\"` to represent a non-chemical element, or\n- the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\n\nIf any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.", - "x-optimade-support": "must", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "must" }, "concentration": { "items": { @@ -3342,8 +3342,8 @@ "type": "array", "title": "Concentration", "description": "MUST be a list of floats, with same length as `chemical_symbols`. The numbers represent the relative concentration of the corresponding chemical symbol in this species. The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\n\n- Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\n- Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\n\nNote that concentrations are uncorrelated between different site (even of the same species).", - "x-optimade-support": "must", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "must" }, "mass": { "items": { @@ -3352,16 +3352,16 @@ "type": "array", "title": "Mass", "description": "If present MUST be a list of floats expressed in a.m.u.\nElements denoting vacancies MUST have masses equal to 0.", - "x-optimade-unit": "a.m.u.", + "x-optimade-queryable": "optional", "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-unit": "a.m.u." }, "original_name": { "type": "string", "title": "Original Name", "description": "Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\n\nNote: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "attached": { "items": { @@ -3370,8 +3370,8 @@ "type": "array", "title": "Attached", "description": "If provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "nattached": { "items": { @@ -3380,8 +3380,8 @@ "type": "array", "title": "Nattached", "description": "If provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the :field:`attached` key.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" } }, "type": "object", @@ -3450,8 +3450,8 @@ "type": "string", "title": "Id", "description": "An entry's ID as defined in section Definition of Terms.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n\n- **Examples**:\n - `\"db/1234567\"`\n - `\"cod/2000000\"`\n - `\"cod/2000000@1234567\"`\n - `\"nomad/L1234567890\"`\n - `\"42\"`", - "x-optimade-support": "must", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "must" }, "type": { "type": "string", @@ -3459,8 +3459,8 @@ "title": "Type", "description": "The name of the type of an entry.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response.\n - MUST be an existing entry type.\n - The entry of type `` and ID `` MUST be returned in response to a request for `//` under the versioned base URL.\n\n- **Examples**:\n - `\"structures\"`", "default": "structures", - "x-optimade-support": "must", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "must" }, "links": { "allOf": [ @@ -3478,7 +3478,7 @@ } ], "title": "Meta", - "description": "A dictionary, containing entry and property-specific metadata for a given entry." + "description": "A [JSON API meta object](https://jsonapi.org/format/1.1/#document-meta) that is used to communicate metadata." }, "attributes": { "$ref": "#/components/schemas/StructureResourceAttributes" @@ -3508,17 +3508,17 @@ "type": "string", "title": "Immutable Id", "description": "The entry's immutable ID (e.g., an UUID). This is important for databases having preferred IDs that point to \"the latest version\" of a record, but still offer access to older variants. This ID maps to the version-specific record, in case it changes in the future.\n\n- **Type**: string.\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `\"8bd3e750-b477-41a0-9b11-3a799f21b44f\"`\n - `\"fjeiwoj,54;@=%<>#32\"` (Strings that are not URL-safe are allowed.)", - "x-optimade-support": "optional", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "optional" }, "last_modified": { "type": "string", "format": "date-time", "title": "Last Modified", "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", + "x-optimade-queryable": "must", "x-optimade-support": "should", - "nullable": true, - "x-optimade-queryable": "must" + "nullable": true }, "elements": { "items": { @@ -3527,17 +3527,17 @@ "type": "array", "title": "Elements", "description": "The chemical symbols of the different elements present in the structure.\n\n- **Type**: list of strings.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The strings are the chemical symbols, i.e., either a single uppercase letter or an uppercase letter followed by a number of lowercase letters.\n - The order MUST be alphabetical.\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements_ratios`, if the latter is provided.\n - Note: This property SHOULD NOT contain the string \"X\" to indicate non-chemical elements or \"vacancy\" to indicate vacancies (in contrast to the field `chemical_symbols` for the `species` property).\n\n- **Examples**:\n - `[\"Si\"]`\n - `[\"Al\",\"O\",\"Si\"]`\n\n- **Query examples**:\n - A filter that matches all records of structures that contain Si, Al **and** O, and possibly other elements: `elements HAS ALL \"Si\", \"Al\", \"O\"`.\n - To match structures with exactly these three elements, use `elements HAS ALL \"Si\", \"Al\", \"O\" AND elements LENGTH 3`.\n - Note: length queries on this property can be equivalently formulated by filtering on the `nelements`_ property directly.", + "x-optimade-queryable": "must", "x-optimade-support": "should", - "nullable": true, - "x-optimade-queryable": "must" + "nullable": true }, "nelements": { "type": "integer", "title": "Nelements", "description": "Number of different elements in the structure as an integer.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - MUST be equal to the lengths of the list properties `elements` and `elements_ratios`, if they are provided.\n\n- **Examples**:\n - `3`\n\n- **Querying**:\n - Note: queries on this property can equivalently be formulated using `elements LENGTH`.\n - A filter that matches structures that have exactly 4 elements: `nelements=4`.\n - A filter that matches structures that have between 2 and 7 elements: `nelements>=2 AND nelements<=7`.", + "x-optimade-queryable": "must", "x-optimade-support": "should", - "nullable": true, - "x-optimade-queryable": "must" + "nullable": true }, "elements_ratios": { "items": { @@ -3546,43 +3546,43 @@ "type": "array", "title": "Elements Ratios", "description": "Relative proportions of different elements in the structure.\n\n- **Type**: list of floats\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - Composed by the proportions of elements in the structure as a list of floating point numbers.\n - The sum of the numbers MUST be 1.0 (within floating point accuracy)\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements`, if the latter is provided.\n\n- **Examples**:\n - `[1.0]`\n - `[0.3333333333333333, 0.2222222222222222, 0.4444444444444444]`\n\n- **Query examples**:\n - Note: Useful filters can be formulated using the set operator syntax for correlated values.\n However, since the values are floating point values, the use of equality comparisons is generally inadvisable.\n - OPTIONAL: a filter that matches structures where approximately 1/3 of the atoms in the structure are the element Al is: `elements:elements_ratios HAS ALL \"Al\":>0.3333, \"Al\":<0.3334`.", + "x-optimade-queryable": "must", "x-optimade-support": "should", - "nullable": true, - "x-optimade-queryable": "must" + "nullable": true }, "chemical_formula_descriptive": { "type": "string", "title": "Chemical Formula Descriptive", "description": "The chemical formula for a structure as a string in a form chosen by the API implementation.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The chemical formula is given as a string consisting of properly capitalized element symbols followed by integers or decimal numbers, balanced parentheses, square, and curly brackets `(`,`)`, `[`,`]`, `{`, `}`, commas, the `+`, `-`, `:` and `=` symbols. The parentheses are allowed to be followed by a number. Spaces are allowed anywhere except within chemical symbols. The order of elements and any groupings indicated by parentheses or brackets are chosen freely by the API implementation.\n - The string SHOULD be arithmetically consistent with the element ratios in the `chemical_formula_reduced` property.\n - It is RECOMMENDED, but not mandatory, that symbols, parentheses and brackets, if used, are used with the meanings prescribed by [IUPAC's Nomenclature of Organic Chemistry](https://www.qmul.ac.uk/sbcs/iupac/bibliog/blue.html).\n\n- **Examples**:\n - `\"(H2O)2 Na\"`\n - `\"NaCl\"`\n - `\"CaCO3\"`\n - `\"CCaO3\"`\n - `\"(CH3)3N+ - [CH2]2-OH = Me3N+ - CH2 - CH2OH\"`\n\n- **Query examples**:\n - Note: the free-form nature of this property is likely to make queries on it across different databases inconsistent.\n - A filter that matches an exactly given formula: `chemical_formula_descriptive=\"(H2O)2 Na\"`.\n - A filter that does a partial match: `chemical_formula_descriptive CONTAINS \"H2O\"`.", + "x-optimade-queryable": "must", "x-optimade-support": "should", - "nullable": true, - "x-optimade-queryable": "must" + "nullable": true }, "chemical_formula_reduced": { "type": "string", "pattern": "(^$)|^([A-Z][a-z]?([2-9]|[1-9]\\d+)?)+$", "title": "Chemical Formula Reduced", "description": "The reduced chemical formula for a structure as a string with element symbols and integer chemical proportion numbers.\nThe proportion number MUST be omitted if it is 1.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property.\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\n Intricate queries on formula components are instead suggested to be formulated using set-type filter operators on the multi valued `elements` and `elements_ratios` properties.\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\n - Elements MUST be placed in alphabetical order, followed by their integer chemical proportion number.\n - For structures with no partial occupation, the chemical proportion numbers are the smallest integers for which the chemical proportion is exactly correct.\n - For structures with partial occupation, the chemical proportion numbers are integers that within reasonable approximation indicate the correct chemical proportions. The precise details of how to perform the rounding is chosen by the API implementation.\n - No spaces or separators are allowed.\n\n- **Examples**:\n - `\"H2NaO\"`\n - `\"ClNa\"`\n - `\"CCaO3\"`\n\n- **Query examples**:\n - A filter that matches an exactly given formula is `chemical_formula_reduced=\"H2NaO\"`.", + "x-optimade-queryable": "must", "x-optimade-support": "should", - "nullable": true, - "x-optimade-queryable": "must" + "nullable": true }, "chemical_formula_hill": { "type": "string", "pattern": "(^$)|^([A-Z][a-z]?([2-9]|[1-9]\\d+)?)+$", "title": "Chemical Formula Hill", "description": "The chemical formula for a structure in [Hill form](https://dx.doi.org/10.1021/ja02046a005) with element symbols followed by integer chemical proportion numbers. The proportion number MUST be omitted if it is 1.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, only a subset of the filter features MAY be supported.\n - The overall scale factor of the chemical proportions is chosen such that the resulting values are integers that indicate the most chemically relevant unit of which the system is composed.\n For example, if the structure is a repeating unit cell with four hydrogens and four oxygens that represents two hydroperoxide molecules, `chemical_formula_hill` is `\"H2O2\"` (i.e., not `\"HO\"`, nor `\"H4O4\"`).\n - If the chemical insight needed to ascribe a Hill formula to the system is not present, the property MUST be handled as unset.\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\n - Elements MUST be placed in [Hill order](https://dx.doi.org/10.1021/ja02046a005), followed by their integer chemical proportion number.\n Hill order means: if carbon is present, it is placed first, and if also present, hydrogen is placed second.\n After that, all other elements are ordered alphabetically.\n If carbon is not present, all elements are ordered alphabetically.\n - If the system has sites with partial occupation and the total occupations of each element do not all sum up to integers, then the Hill formula SHOULD be handled as unset.\n - No spaces or separators are allowed.\n\n- **Examples**:\n - `\"H2O2\"`\n\n- **Query examples**:\n - A filter that matches an exactly given formula is `chemical_formula_hill=\"H2O2\"`.", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "chemical_formula_anonymous": { "type": "string", "pattern": "(^$)|^([A-Z][a-z]?([2-9]|[1-9]\\d+)?)+$", "title": "Chemical Formula Anonymous", "description": "The anonymous formula is the `chemical_formula_reduced`, but where the elements are instead first ordered by their chemical proportion number, and then, in order left to right, replaced by anonymous symbols A, B, C, ..., Z, Aa, Ba, ..., Za, Ab, Bb, ... and so on.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property.\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\n\n- **Examples**:\n - `\"A2B\"`\n - `\"A42B42C16D12E10F9G5\"`\n\n- **Querying**:\n - A filter that matches an exactly given formula is `chemical_formula_anonymous=\"A2B\"`.", + "x-optimade-queryable": "must", "x-optimade-support": "should", - "nullable": true, - "x-optimade-queryable": "must" + "nullable": true }, "dimension_types": { "items": { @@ -3594,16 +3594,16 @@ "title": "Dimension Types", "description": "List of three integers.\nFor each of the three directions indicated by the three lattice vectors (see property `lattice_vectors`), this list indicates if the direction is periodic (value `1`) or non-periodic (value `0`).\nNote: the elements in this list each refer to the direction of the corresponding entry in `lattice_vectors` and *not* the Cartesian x, y, z directions.\n\n- **Type**: list of integers.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n - MUST be a list of length 3.\n - Each integer element MUST assume only the value 0 or 1.\n\n- **Examples**:\n - For a molecule: `[0, 0, 0]`\n - For a wire along the direction specified by the third lattice vector: `[0, 0, 1]`\n - For a 2D surface/slab, periodic on the plane defined by the first and third lattice vectors: `[1, 0, 1]`\n - For a bulk 3D system: `[1, 1, 1]`", "x-optimade-support": "should", - "nullable": true, - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "nullable": true }, "nperiodic_dimensions": { "type": "integer", "title": "Nperiodic Dimensions", "description": "An integer specifying the number of periodic dimensions in the structure, equivalent to the number of non-zero entries in `dimension_types`.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The integer value MUST be between 0 and 3 inclusive and MUST be equal to the sum of the items in the `dimension_types` property.\n - This property only reflects the treatment of the lattice vectors provided for the structure, and not any physical interpretation of the dimensionality of its contents.\n\n- **Examples**:\n - `2` should be indicated in cases where `dimension_types` is any of `[1, 1, 0]`, `[1, 0, 1]`, `[0, 1, 1]`.\n\n- **Query examples**:\n - Match only structures with exactly 3 periodic dimensions: `nperiodic_dimensions=3`\n - Match all structures with 2 or fewer periodic dimensions: `nperiodic_dimensions<=2`", + "x-optimade-queryable": "must", "x-optimade-support": "should", - "nullable": true, - "x-optimade-queryable": "must" + "nullable": true }, "lattice_vectors": { "items": { @@ -3619,10 +3619,10 @@ "minItems": 3, "title": "Lattice Vectors", "description": "The three lattice vectors in Cartesian coordinates, in \u00e5ngstr\u00f6m (\u00c5).\n\n- **Type**: list of list of floats or unknown values.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - MUST be a list of three vectors *a*, *b*, and *c*, where each of the vectors MUST BE a list of the vector's coordinates along the x, y, and z Cartesian coordinates.\n (Therefore, the first index runs over the three lattice vectors and the second index runs over the x, y, z Cartesian coordinates).\n - For databases that do not define an absolute Cartesian system (e.g., only defining the length and angles between vectors), the first lattice vector SHOULD be set along *x* and the second on the *xy*-plane.\n - MUST always contain three vectors of three coordinates each, independently of the elements of property `dimension_types`.\n The vectors SHOULD by convention be chosen so the determinant of the `lattice_vectors` matrix is different from zero.\n The vectors in the non-periodic directions have no significance beyond fulfilling these requirements.\n - The coordinates of the lattice vectors of non-periodic dimensions (i.e., those dimensions for which `dimension_types` is `0`) MAY be given as a list of all `null` values.\n If a lattice vector contains the value `null`, all coordinates of that lattice vector MUST be `null`.\n\n- **Examples**:\n - `[[4.0,0.0,0.0],[0.0,4.0,0.0],[0.0,1.0,4.0]]` represents a cell, where the first vector is `(4, 0, 0)`, i.e., a vector aligned along the `x` axis of length 4 \u00c5; the second vector is `(0, 4, 0)`; and the third vector is `(0, 1, 4)`.", - "x-optimade-unit": "\u00c5", "x-optimade-support": "should", - "nullable": true, - "x-optimade-queryable": "optional" + "x-optimade-unit": "\u00c5", + "x-optimade-queryable": "optional", + "nullable": true }, "cartesian_site_positions": { "items": { @@ -3636,8 +3636,8 @@ "type": "array", "title": "Cartesian Site Positions", "description": "Cartesian positions of each site in the structure.\nA site is usually used to describe positions of atoms; what atoms can be encountered at a given site is conveyed by the `species_at_sites` property, and the species themselves are described in the `species` property.\n\n- **Type**: list of list of floats\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - It MUST be a list of length equal to the number of sites in the structure, where every element is a list of the three Cartesian coordinates of a site expressed as float values in the unit angstrom (\u00c5).\n - An entry MAY have multiple sites at the same Cartesian position (for a relevant use of this, see e.g., the property `assemblies`).\n\n- **Examples**:\n - `[[0,0,0],[0,0,2]]` indicates a structure with two sites, one sitting at the origin and one along the (positive) *z*-axis, 2 \u00c5 away from the origin.", - "x-optimade-unit": "\u00c5", "x-optimade-support": "should", + "x-optimade-unit": "\u00c5", "x-optimade-queryable": "optional", "nullable": true }, @@ -3645,9 +3645,9 @@ "type": "integer", "title": "Nsites", "description": "An integer specifying the length of the `cartesian_site_positions` property.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `42`\n\n- **Query examples**:\n - Match only structures with exactly 4 sites: `nsites=4`\n - Match structures that have between 2 and 7 sites: `nsites>=2 AND nsites<=7`", + "x-optimade-queryable": "must", "x-optimade-support": "should", - "nullable": true, - "x-optimade-queryable": "must" + "nullable": true }, "species": { "items": { @@ -3656,9 +3656,9 @@ "type": "array", "title": "Species", "description": "A list describing the species of the sites of this structure.\nSpecies can represent pure chemical elements, virtual-crystal atoms representing a statistical occupation of a given site by multiple chemical elements, and/or a location to which there are attached atoms, i.e., atoms whose precise location are unknown beyond that they are attached to that position (frequently used to indicate hydrogen atoms attached to another element, e.g., a carbon with three attached hydrogens might represent a methyl group, -CH3).\n\n- **Type**: list of dictionary with keys:\n - `name`: string (REQUIRED)\n - `chemical_symbols`: list of strings (REQUIRED)\n - `concentration`: list of float (REQUIRED)\n - `attached`: list of strings (REQUIRED)\n - `nattached`: list of integers (OPTIONAL)\n - `mass`: list of floats (OPTIONAL)\n - `original_name`: string (OPTIONAL).\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - Each list member MUST be a dictionary with the following keys:\n - **name**: REQUIRED; gives the name of the species; the **name** value MUST be unique in the `species` list;\n - **chemical_symbols**: REQUIRED; MUST be a list of strings of all chemical elements composing this species.\n Each item of the list MUST be one of the following:\n - a valid chemical-element symbol, or\n - the special value `\"X\"` to represent a non-chemical element, or\n - the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\n\n If any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.\n\n - **concentration**: REQUIRED; MUST be a list of floats, with same length as `chemical_symbols`.\n The numbers represent the relative concentration of the corresponding chemical symbol in this species.\n The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\n\n - Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\n - Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\n\n Note that concentrations are uncorrelated between different sites (even of the same species).\n\n - **attached**: OPTIONAL; if provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.\n\n - **nattached**: OPTIONAL; if provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the `attached` key.\n\n The implementation MUST include either both or none of the `attached` and `nattached` keys, and if they are provided, they MUST be of the same length.\n Furthermore, if they are provided, the `structure_features` property MUST include the string `site_attachments`.\n\n - **mass**: OPTIONAL. If present MUST be a list of floats, with the same length as `chemical_symbols`, providing element masses expressed in a.m.u.\n Elements denoting vacancies MUST have masses equal to 0.\n\n - **original_name**: OPTIONAL. Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\n\n Note: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.\n\n The main use of this field is for source databases that use species names, containing characters that are not allowed (see description of the list property `species_at_sites`).\n\n - For systems that have only species formed by a single chemical symbol, and that have at most one species per chemical symbol, SHOULD use the chemical symbol as species name (e.g., `\"Ti\"` for titanium, `\"O\"` for oxygen, etc.)\n However, note that this is OPTIONAL, and client implementations MUST NOT assume that the key corresponds to a chemical symbol, nor assume that if the species name is a valid chemical symbol, that it represents a species with that chemical symbol.\n This means that a species `{\"name\": \"C\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]}` is valid and represents a titanium species (and *not* a carbon species).\n - It is NOT RECOMMENDED that a structure includes species that do not have at least one corresponding site.\n\n- **Examples**:\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]`: any site with this species is occupied by a Ti atom.\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]`: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.\n - `[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]`: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.\n - `[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]`: any site with this species is occupied by a carbon isotope with mass 12.\n - `[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]`: any site with this species is occupied by a carbon isotope with mass 13.\n - `[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]`: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms.", + "x-optimade-queryable": "optional", "x-optimade-support": "should", - "nullable": true, - "x-optimade-queryable": "optional" + "nullable": true }, "species_at_sites": { "items": { @@ -3667,9 +3667,9 @@ "type": "array", "title": "Species At Sites", "description": "Name of the species at each site (where values for sites are specified with the same order of the property `cartesian_site_positions`).\nThe properties of the species are found in the property `species`.\n\n- **Type**: list of strings.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - MUST have length equal to the number of sites in the structure (first dimension of the list property `cartesian_site_positions`).\n - Each species name mentioned in the `species_at_sites` list MUST be described in the list property `species` (i.e. for each value in the `species_at_sites` list there MUST exist exactly one dictionary in the `species` list with the `name` attribute equal to the corresponding `species_at_sites` value).\n - Each site MUST be associated only to a single species.\n **Note**: However, species can represent mixtures of atoms, and multiple species MAY be defined for the same chemical element.\n This latter case is useful when different atoms of the same type need to be grouped or distinguished, for instance in simulation codes to assign different initial spin states.\n\n- **Examples**:\n - `[\"Ti\",\"O2\"]` indicates that the first site is hosting a species labeled `\"Ti\"` and the second a species labeled `\"O2\"`.\n - `[\"Ac\", \"Ac\", \"Ag\", \"Ir\"]` indicating the first two sites contains the `\"Ac\"` species, while the third and fourth sites contain the `\"Ag\"` and `\"Ir\"` species, respectively.", + "x-optimade-queryable": "optional", "x-optimade-support": "should", - "nullable": true, - "x-optimade-queryable": "optional" + "nullable": true }, "assemblies": { "items": { @@ -3678,8 +3678,8 @@ "type": "array", "title": "Assemblies", "description": "A description of groups of sites that are statistically correlated.\n\n- **Type**: list of dictionary with keys:\n - `sites_in_groups`: list of list of integers (REQUIRED)\n - `group_probabilities`: list of floats (REQUIRED)\n\n- **Requirements/Conventions**:\n - **Support**: OPTIONAL support in implementations, i.e., MAY be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - The property SHOULD be `null` for entries that have no partial occupancies.\n - If present, the correct flag MUST be set in the list `structure_features`.\n - Client implementations MUST check its presence (as its presence changes the interpretation of the structure).\n - If present, it MUST be a list of dictionaries, each of which represents an assembly and MUST have the following two keys:\n - **sites_in_groups**: Index of the sites (0-based) that belong to each group for each assembly.\n\n Example: `[[1], [2]]`: two groups, one with the second site, one with the third.\n Example: `[[1,2], [3]]`: one group with the second and third site, one with the fourth.\n\n - **group_probabilities**: Statistical probability of each group. It MUST have the same length as `sites_in_groups`.\n It SHOULD sum to one.\n See below for examples of how to specify the probability of the occurrence of a vacancy.\n The possible reasons for the values not to sum to one are the same as already specified above for the `concentration` of each `species`.\n\n - If a site is not present in any group, it means that it is present with 100 % probability (as if no assembly was specified).\n - A site MUST NOT appear in more than one group.\n\n- **Examples** (for each entry of the assemblies list):\n - `{\"sites_in_groups\": [[0], [1]], \"group_probabilities: [0.3, 0.7]}`: the first site and the second site never occur at the same time in the unit cell.\n Statistically, 30 % of the times the first site is present, while 70 % of the times the second site is present.\n - `{\"sites_in_groups\": [[1,2], [3]], \"group_probabilities: [0.3, 0.7]}`: the second and third site are either present together or not present; they form the first group of atoms for this assembly.\n The second group is formed by the fourth site.\n Sites of the first group (the second and the third) are never present at the same time as the fourth site.\n 30 % of times sites 1 and 2 are present (and site 3 is absent); 70 % of times site 3 is present (and sites 1 and 2 are absent).\n\n- **Notes**:\n - Assemblies are essential to represent, for instance, the situation where an atom can statistically occupy two different positions (sites).\n\n - By defining groups, it is possible to represent, e.g., the case where a functional molecule (and not just one atom) is either present or absent (or the case where it it is present in two conformations)\n\n - Considerations on virtual alloys and on vacancies: In the special case of a virtual alloy, these specifications allow two different, equivalent ways of specifying them.\n For instance, for a site at the origin with 30 % probability of being occupied by Si, 50 % probability of being occupied by Ge, and 20 % of being a vacancy, the following two representations are possible:\n\n - Using a single species:\n ```json\n {\n \"cartesian_site_positions\": [[0,0,0]],\n \"species_at_sites\": [\"SiGe-vac\"],\n \"species\": [\n {\n \"name\": \"SiGe-vac\",\n \"chemical_symbols\": [\"Si\", \"Ge\", \"vacancy\"],\n \"concentration\": [0.3, 0.5, 0.2]\n }\n ]\n // ...\n }\n ```\n\n - Using multiple species and the assemblies:\n ```json\n {\n \"cartesian_site_positions\": [ [0,0,0], [0,0,0], [0,0,0] ],\n \"species_at_sites\": [\"Si\", \"Ge\", \"vac\"],\n \"species\": [\n { \"name\": \"Si\", \"chemical_symbols\": [\"Si\"], \"concentration\": [1.0] },\n { \"name\": \"Ge\", \"chemical_symbols\": [\"Ge\"], \"concentration\": [1.0] },\n { \"name\": \"vac\", \"chemical_symbols\": [\"vacancy\"], \"concentration\": [1.0] }\n ],\n \"assemblies\": [\n {\n \"sites_in_groups\": [ [0], [1], [2] ],\n \"group_probabilities\": [0.3, 0.5, 0.2]\n }\n ]\n // ...\n }\n ```\n\n - It is up to the database provider to decide which representation to use, typically depending on the internal format in which the structure is stored.\n However, given a structure identified by a unique ID, the API implementation MUST always provide the same representation for it.\n\n - The probabilities of occurrence of different assemblies are uncorrelated.\n So, for instance in the following case with two assemblies:\n ```json\n {\n \"assemblies\": [\n {\n \"sites_in_groups\": [ [0], [1] ],\n \"group_probabilities\": [0.2, 0.8],\n },\n {\n \"sites_in_groups\": [ [2], [3] ],\n \"group_probabilities\": [0.3, 0.7]\n }\n ]\n }\n ```\n\n Site 0 is present with a probability of 20 % and site 1 with a probability of 80 %. These two sites are correlated (either site 0 or 1 is present). Similarly, site 2 is present with a probability of 30 % and site 3 with a probability of 70 %.\n These two sites are correlated (either site 2 or 3 is present).\n However, the presence or absence of sites 0 and 1 is not correlated with the presence or absence of sites 2 and 3 (in the specific example, the pair of sites (0, 2) can occur with 0.2*0.3 = 6 % probability; the pair (0, 3) with 0.2*0.7 = 14 % probability; the pair (1, 2) with 0.8*0.3 = 24 % probability; and the pair (1, 3) with 0.8*0.7 = 56 % probability).", - "x-optimade-support": "optional", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-support": "optional" }, "structure_features": { "items": { @@ -3688,8 +3688,8 @@ "type": "array", "title": "Structure Features", "description": "A list of strings that flag which special features are used by the structure.\n\n- **Type**: list of strings\n\n- **Requirements/Conventions**:\n - **Support**: MUST be supported by all implementations, MUST NOT be `null`.\n - **Query**: MUST be a queryable property.\n Filters on the list MUST support all mandatory HAS-type queries.\n Filter operators for comparisons on the string components MUST support equality, support for other comparison operators are OPTIONAL.\n - MUST be an empty list if no special features are used.\n - MUST be sorted alphabetically.\n - If a special feature listed below is used, the list MUST contain the corresponding string.\n - If a special feature listed below is not used, the list MUST NOT contain the corresponding string.\n - **List of strings used to indicate special structure features**:\n - `disorder`: this flag MUST be present if any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element.\n - `implicit_atoms`: this flag MUST be present if the structure contains atoms that are not assigned to sites via the property `species_at_sites` (e.g., because their positions are unknown).\n When this flag is present, the properties related to the chemical formula will likely not match the type and count of atoms represented by the `species_at_sites`, `species` and `assemblies` properties.\n - `site_attachments`: this flag MUST be present if any one entry in the `species` list includes `attached` and `nattached`.\n - `assemblies`: this flag MUST be present if the property `assemblies` is present.\n\n- **Examples**: A structure having implicit atoms and using assemblies: `[\"assemblies\", \"implicit_atoms\"]`", - "x-optimade-support": "must", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "x-optimade-support": "must" } }, "type": "object", diff --git a/optimade/models/entries.py b/optimade/models/entries.py index c2286fb96..8987e0a33 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -178,7 +178,7 @@ class EntryResource(Resource): meta: Optional[EntryMetadata] = StrictField( None, - description="""A dictionary, containing entry and property-specific metadata for a given entry.""", + description="""A [JSON API meta object](https://jsonapi.org/format/1.1/#document-meta) that is used to communicate metadata.""", ) relationships: Optional[EntryRelationships] = StrictField( From d3b8b22a76fd1f464a21caf32204526a2275260e Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Tue, 3 Oct 2023 12:44:49 +0200 Subject: [PATCH 70/86] update openapijson. --- openapi/openapi.json | 56 ++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/openapi/openapi.json b/openapi/openapi.json index 4775bbeb8..376a81909 100644 --- a/openapi/openapi.json +++ b/openapi/openapi.json @@ -3352,9 +3352,9 @@ "type": "array", "title": "Mass", "description": "If present MUST be a list of floats expressed in a.m.u.\nElements denoting vacancies MUST have masses equal to 0.", + "x-optimade-unit": "a.m.u.", "x-optimade-queryable": "optional", - "x-optimade-support": "optional", - "x-optimade-unit": "a.m.u." + "x-optimade-support": "optional" }, "original_name": { "type": "string", @@ -3516,9 +3516,9 @@ "format": "date-time", "title": "Last Modified", "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", + "nullable": true, "x-optimade-queryable": "must", - "x-optimade-support": "should", - "nullable": true + "x-optimade-support": "should" }, "elements": { "items": { @@ -3527,17 +3527,17 @@ "type": "array", "title": "Elements", "description": "The chemical symbols of the different elements present in the structure.\n\n- **Type**: list of strings.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The strings are the chemical symbols, i.e., either a single uppercase letter or an uppercase letter followed by a number of lowercase letters.\n - The order MUST be alphabetical.\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements_ratios`, if the latter is provided.\n - Note: This property SHOULD NOT contain the string \"X\" to indicate non-chemical elements or \"vacancy\" to indicate vacancies (in contrast to the field `chemical_symbols` for the `species` property).\n\n- **Examples**:\n - `[\"Si\"]`\n - `[\"Al\",\"O\",\"Si\"]`\n\n- **Query examples**:\n - A filter that matches all records of structures that contain Si, Al **and** O, and possibly other elements: `elements HAS ALL \"Si\", \"Al\", \"O\"`.\n - To match structures with exactly these three elements, use `elements HAS ALL \"Si\", \"Al\", \"O\" AND elements LENGTH 3`.\n - Note: length queries on this property can be equivalently formulated by filtering on the `nelements`_ property directly.", + "nullable": true, "x-optimade-queryable": "must", - "x-optimade-support": "should", - "nullable": true + "x-optimade-support": "should" }, "nelements": { "type": "integer", "title": "Nelements", "description": "Number of different elements in the structure as an integer.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - MUST be equal to the lengths of the list properties `elements` and `elements_ratios`, if they are provided.\n\n- **Examples**:\n - `3`\n\n- **Querying**:\n - Note: queries on this property can equivalently be formulated using `elements LENGTH`.\n - A filter that matches structures that have exactly 4 elements: `nelements=4`.\n - A filter that matches structures that have between 2 and 7 elements: `nelements>=2 AND nelements<=7`.", + "nullable": true, "x-optimade-queryable": "must", - "x-optimade-support": "should", - "nullable": true + "x-optimade-support": "should" }, "elements_ratios": { "items": { @@ -3546,26 +3546,26 @@ "type": "array", "title": "Elements Ratios", "description": "Relative proportions of different elements in the structure.\n\n- **Type**: list of floats\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - Composed by the proportions of elements in the structure as a list of floating point numbers.\n - The sum of the numbers MUST be 1.0 (within floating point accuracy)\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements`, if the latter is provided.\n\n- **Examples**:\n - `[1.0]`\n - `[0.3333333333333333, 0.2222222222222222, 0.4444444444444444]`\n\n- **Query examples**:\n - Note: Useful filters can be formulated using the set operator syntax for correlated values.\n However, since the values are floating point values, the use of equality comparisons is generally inadvisable.\n - OPTIONAL: a filter that matches structures where approximately 1/3 of the atoms in the structure are the element Al is: `elements:elements_ratios HAS ALL \"Al\":>0.3333, \"Al\":<0.3334`.", + "nullable": true, "x-optimade-queryable": "must", - "x-optimade-support": "should", - "nullable": true + "x-optimade-support": "should" }, "chemical_formula_descriptive": { "type": "string", "title": "Chemical Formula Descriptive", "description": "The chemical formula for a structure as a string in a form chosen by the API implementation.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The chemical formula is given as a string consisting of properly capitalized element symbols followed by integers or decimal numbers, balanced parentheses, square, and curly brackets `(`,`)`, `[`,`]`, `{`, `}`, commas, the `+`, `-`, `:` and `=` symbols. The parentheses are allowed to be followed by a number. Spaces are allowed anywhere except within chemical symbols. The order of elements and any groupings indicated by parentheses or brackets are chosen freely by the API implementation.\n - The string SHOULD be arithmetically consistent with the element ratios in the `chemical_formula_reduced` property.\n - It is RECOMMENDED, but not mandatory, that symbols, parentheses and brackets, if used, are used with the meanings prescribed by [IUPAC's Nomenclature of Organic Chemistry](https://www.qmul.ac.uk/sbcs/iupac/bibliog/blue.html).\n\n- **Examples**:\n - `\"(H2O)2 Na\"`\n - `\"NaCl\"`\n - `\"CaCO3\"`\n - `\"CCaO3\"`\n - `\"(CH3)3N+ - [CH2]2-OH = Me3N+ - CH2 - CH2OH\"`\n\n- **Query examples**:\n - Note: the free-form nature of this property is likely to make queries on it across different databases inconsistent.\n - A filter that matches an exactly given formula: `chemical_formula_descriptive=\"(H2O)2 Na\"`.\n - A filter that does a partial match: `chemical_formula_descriptive CONTAINS \"H2O\"`.", + "nullable": true, "x-optimade-queryable": "must", - "x-optimade-support": "should", - "nullable": true + "x-optimade-support": "should" }, "chemical_formula_reduced": { "type": "string", "pattern": "(^$)|^([A-Z][a-z]?([2-9]|[1-9]\\d+)?)+$", "title": "Chemical Formula Reduced", "description": "The reduced chemical formula for a structure as a string with element symbols and integer chemical proportion numbers.\nThe proportion number MUST be omitted if it is 1.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property.\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\n Intricate queries on formula components are instead suggested to be formulated using set-type filter operators on the multi valued `elements` and `elements_ratios` properties.\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\n - Elements MUST be placed in alphabetical order, followed by their integer chemical proportion number.\n - For structures with no partial occupation, the chemical proportion numbers are the smallest integers for which the chemical proportion is exactly correct.\n - For structures with partial occupation, the chemical proportion numbers are integers that within reasonable approximation indicate the correct chemical proportions. The precise details of how to perform the rounding is chosen by the API implementation.\n - No spaces or separators are allowed.\n\n- **Examples**:\n - `\"H2NaO\"`\n - `\"ClNa\"`\n - `\"CCaO3\"`\n\n- **Query examples**:\n - A filter that matches an exactly given formula is `chemical_formula_reduced=\"H2NaO\"`.", + "nullable": true, "x-optimade-queryable": "must", - "x-optimade-support": "should", - "nullable": true + "x-optimade-support": "should" }, "chemical_formula_hill": { "type": "string", @@ -3580,9 +3580,9 @@ "pattern": "(^$)|^([A-Z][a-z]?([2-9]|[1-9]\\d+)?)+$", "title": "Chemical Formula Anonymous", "description": "The anonymous formula is the `chemical_formula_reduced`, but where the elements are instead first ordered by their chemical proportion number, and then, in order left to right, replaced by anonymous symbols A, B, C, ..., Z, Aa, Ba, ..., Za, Ab, Bb, ... and so on.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property.\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\n\n- **Examples**:\n - `\"A2B\"`\n - `\"A42B42C16D12E10F9G5\"`\n\n- **Querying**:\n - A filter that matches an exactly given formula is `chemical_formula_anonymous=\"A2B\"`.", + "nullable": true, "x-optimade-queryable": "must", - "x-optimade-support": "should", - "nullable": true + "x-optimade-support": "should" }, "dimension_types": { "items": { @@ -3601,9 +3601,9 @@ "type": "integer", "title": "Nperiodic Dimensions", "description": "An integer specifying the number of periodic dimensions in the structure, equivalent to the number of non-zero entries in `dimension_types`.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The integer value MUST be between 0 and 3 inclusive and MUST be equal to the sum of the items in the `dimension_types` property.\n - This property only reflects the treatment of the lattice vectors provided for the structure, and not any physical interpretation of the dimensionality of its contents.\n\n- **Examples**:\n - `2` should be indicated in cases where `dimension_types` is any of `[1, 1, 0]`, `[1, 0, 1]`, `[0, 1, 1]`.\n\n- **Query examples**:\n - Match only structures with exactly 3 periodic dimensions: `nperiodic_dimensions=3`\n - Match all structures with 2 or fewer periodic dimensions: `nperiodic_dimensions<=2`", + "nullable": true, "x-optimade-queryable": "must", - "x-optimade-support": "should", - "nullable": true + "x-optimade-support": "should" }, "lattice_vectors": { "items": { @@ -3620,9 +3620,9 @@ "title": "Lattice Vectors", "description": "The three lattice vectors in Cartesian coordinates, in \u00e5ngstr\u00f6m (\u00c5).\n\n- **Type**: list of list of floats or unknown values.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - MUST be a list of three vectors *a*, *b*, and *c*, where each of the vectors MUST BE a list of the vector's coordinates along the x, y, and z Cartesian coordinates.\n (Therefore, the first index runs over the three lattice vectors and the second index runs over the x, y, z Cartesian coordinates).\n - For databases that do not define an absolute Cartesian system (e.g., only defining the length and angles between vectors), the first lattice vector SHOULD be set along *x* and the second on the *xy*-plane.\n - MUST always contain three vectors of three coordinates each, independently of the elements of property `dimension_types`.\n The vectors SHOULD by convention be chosen so the determinant of the `lattice_vectors` matrix is different from zero.\n The vectors in the non-periodic directions have no significance beyond fulfilling these requirements.\n - The coordinates of the lattice vectors of non-periodic dimensions (i.e., those dimensions for which `dimension_types` is `0`) MAY be given as a list of all `null` values.\n If a lattice vector contains the value `null`, all coordinates of that lattice vector MUST be `null`.\n\n- **Examples**:\n - `[[4.0,0.0,0.0],[0.0,4.0,0.0],[0.0,1.0,4.0]]` represents a cell, where the first vector is `(4, 0, 0)`, i.e., a vector aligned along the `x` axis of length 4 \u00c5; the second vector is `(0, 4, 0)`; and the third vector is `(0, 1, 4)`.", "x-optimade-support": "should", - "x-optimade-unit": "\u00c5", "x-optimade-queryable": "optional", - "nullable": true + "nullable": true, + "x-optimade-unit": "\u00c5" }, "cartesian_site_positions": { "items": { @@ -3637,17 +3637,17 @@ "title": "Cartesian Site Positions", "description": "Cartesian positions of each site in the structure.\nA site is usually used to describe positions of atoms; what atoms can be encountered at a given site is conveyed by the `species_at_sites` property, and the species themselves are described in the `species` property.\n\n- **Type**: list of list of floats\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - It MUST be a list of length equal to the number of sites in the structure, where every element is a list of the three Cartesian coordinates of a site expressed as float values in the unit angstrom (\u00c5).\n - An entry MAY have multiple sites at the same Cartesian position (for a relevant use of this, see e.g., the property `assemblies`).\n\n- **Examples**:\n - `[[0,0,0],[0,0,2]]` indicates a structure with two sites, one sitting at the origin and one along the (positive) *z*-axis, 2 \u00c5 away from the origin.", "x-optimade-support": "should", - "x-optimade-unit": "\u00c5", "x-optimade-queryable": "optional", - "nullable": true + "nullable": true, + "x-optimade-unit": "\u00c5" }, "nsites": { "type": "integer", "title": "Nsites", "description": "An integer specifying the length of the `cartesian_site_positions` property.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `42`\n\n- **Query examples**:\n - Match only structures with exactly 4 sites: `nsites=4`\n - Match structures that have between 2 and 7 sites: `nsites>=2 AND nsites<=7`", + "nullable": true, "x-optimade-queryable": "must", - "x-optimade-support": "should", - "nullable": true + "x-optimade-support": "should" }, "species": { "items": { @@ -3656,9 +3656,9 @@ "type": "array", "title": "Species", "description": "A list describing the species of the sites of this structure.\nSpecies can represent pure chemical elements, virtual-crystal atoms representing a statistical occupation of a given site by multiple chemical elements, and/or a location to which there are attached atoms, i.e., atoms whose precise location are unknown beyond that they are attached to that position (frequently used to indicate hydrogen atoms attached to another element, e.g., a carbon with three attached hydrogens might represent a methyl group, -CH3).\n\n- **Type**: list of dictionary with keys:\n - `name`: string (REQUIRED)\n - `chemical_symbols`: list of strings (REQUIRED)\n - `concentration`: list of float (REQUIRED)\n - `attached`: list of strings (REQUIRED)\n - `nattached`: list of integers (OPTIONAL)\n - `mass`: list of floats (OPTIONAL)\n - `original_name`: string (OPTIONAL).\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - Each list member MUST be a dictionary with the following keys:\n - **name**: REQUIRED; gives the name of the species; the **name** value MUST be unique in the `species` list;\n - **chemical_symbols**: REQUIRED; MUST be a list of strings of all chemical elements composing this species.\n Each item of the list MUST be one of the following:\n - a valid chemical-element symbol, or\n - the special value `\"X\"` to represent a non-chemical element, or\n - the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\n\n If any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.\n\n - **concentration**: REQUIRED; MUST be a list of floats, with same length as `chemical_symbols`.\n The numbers represent the relative concentration of the corresponding chemical symbol in this species.\n The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\n\n - Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\n - Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\n\n Note that concentrations are uncorrelated between different sites (even of the same species).\n\n - **attached**: OPTIONAL; if provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.\n\n - **nattached**: OPTIONAL; if provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the `attached` key.\n\n The implementation MUST include either both or none of the `attached` and `nattached` keys, and if they are provided, they MUST be of the same length.\n Furthermore, if they are provided, the `structure_features` property MUST include the string `site_attachments`.\n\n - **mass**: OPTIONAL. If present MUST be a list of floats, with the same length as `chemical_symbols`, providing element masses expressed in a.m.u.\n Elements denoting vacancies MUST have masses equal to 0.\n\n - **original_name**: OPTIONAL. Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\n\n Note: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.\n\n The main use of this field is for source databases that use species names, containing characters that are not allowed (see description of the list property `species_at_sites`).\n\n - For systems that have only species formed by a single chemical symbol, and that have at most one species per chemical symbol, SHOULD use the chemical symbol as species name (e.g., `\"Ti\"` for titanium, `\"O\"` for oxygen, etc.)\n However, note that this is OPTIONAL, and client implementations MUST NOT assume that the key corresponds to a chemical symbol, nor assume that if the species name is a valid chemical symbol, that it represents a species with that chemical symbol.\n This means that a species `{\"name\": \"C\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]}` is valid and represents a titanium species (and *not* a carbon species).\n - It is NOT RECOMMENDED that a structure includes species that do not have at least one corresponding site.\n\n- **Examples**:\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]`: any site with this species is occupied by a Ti atom.\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]`: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.\n - `[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]`: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.\n - `[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]`: any site with this species is occupied by a carbon isotope with mass 12.\n - `[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]`: any site with this species is occupied by a carbon isotope with mass 13.\n - `[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]`: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms.", + "nullable": true, "x-optimade-queryable": "optional", - "x-optimade-support": "should", - "nullable": true + "x-optimade-support": "should" }, "species_at_sites": { "items": { @@ -3667,9 +3667,9 @@ "type": "array", "title": "Species At Sites", "description": "Name of the species at each site (where values for sites are specified with the same order of the property `cartesian_site_positions`).\nThe properties of the species are found in the property `species`.\n\n- **Type**: list of strings.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - MUST have length equal to the number of sites in the structure (first dimension of the list property `cartesian_site_positions`).\n - Each species name mentioned in the `species_at_sites` list MUST be described in the list property `species` (i.e. for each value in the `species_at_sites` list there MUST exist exactly one dictionary in the `species` list with the `name` attribute equal to the corresponding `species_at_sites` value).\n - Each site MUST be associated only to a single species.\n **Note**: However, species can represent mixtures of atoms, and multiple species MAY be defined for the same chemical element.\n This latter case is useful when different atoms of the same type need to be grouped or distinguished, for instance in simulation codes to assign different initial spin states.\n\n- **Examples**:\n - `[\"Ti\",\"O2\"]` indicates that the first site is hosting a species labeled `\"Ti\"` and the second a species labeled `\"O2\"`.\n - `[\"Ac\", \"Ac\", \"Ag\", \"Ir\"]` indicating the first two sites contains the `\"Ac\"` species, while the third and fourth sites contain the `\"Ag\"` and `\"Ir\"` species, respectively.", + "nullable": true, "x-optimade-queryable": "optional", - "x-optimade-support": "should", - "nullable": true + "x-optimade-support": "should" }, "assemblies": { "items": { From 68f14d3fe6524e7e7475fec6e70e9889f722ccc7 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Tue, 3 Oct 2023 13:05:27 +0200 Subject: [PATCH 71/86] try to see if moving yaml is still necessary. --- optimade/server/config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/optimade/server/config.py b/optimade/server/config.py index 78a33fee2..3a3e7cea7 100644 --- a/optimade/server/config.py +++ b/optimade/server/config.py @@ -87,6 +87,8 @@ def config_file_settings(settings: BaseSettings) -> Dict[str, Any]: import json import os + import yaml + encoding = settings.__config__.env_file_encoding config_file = Path(os.getenv("OPTIMADE_CONFIG_FILE", DEFAULT_CONFIG_FILE_PATH)) @@ -98,8 +100,6 @@ def config_file_settings(settings: BaseSettings) -> Dict[str, Any]: res = json.loads(config_file_content) except json.JSONDecodeError as json_exc: try: - import yaml - # This can essentially also load JSON files, as JSON is a subset of YAML v1, # but I suspect it is not as rigorous res = yaml.safe_load(config_file_content) From 1d46e1b1c70fafe1a80681a23b7ef855da0c24b5 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Tue, 3 Oct 2023 13:35:56 +0200 Subject: [PATCH 72/86] Revert "try to see if moving yaml is still necessary." This reverts commit 68f14d3fe6524e7e7475fec6e70e9889f722ccc7. --- optimade/server/config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/optimade/server/config.py b/optimade/server/config.py index 3a3e7cea7..78a33fee2 100644 --- a/optimade/server/config.py +++ b/optimade/server/config.py @@ -87,8 +87,6 @@ def config_file_settings(settings: BaseSettings) -> Dict[str, Any]: import json import os - import yaml - encoding = settings.__config__.env_file_encoding config_file = Path(os.getenv("OPTIMADE_CONFIG_FILE", DEFAULT_CONFIG_FILE_PATH)) @@ -100,6 +98,8 @@ def config_file_settings(settings: BaseSettings) -> Dict[str, Any]: res = json.loads(config_file_content) except json.JSONDecodeError as json_exc: try: + import yaml + # This can essentially also load JSON files, as JSON is a subset of YAML v1, # but I suspect it is not as rigorous res = yaml.safe_load(config_file_content) From 774be723479a190768d8d09603aadff1db69fa24 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Wed, 11 Oct 2023 16:50:23 +0200 Subject: [PATCH 73/86] Added supported_prefixes field to config.py --- optimade/server/config.py | 9 +++++++++ optimade/server/mappers/entries.py | 12 ++---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/optimade/server/config.py b/optimade/server/config.py index 78a33fee2..b2a47c05e 100644 --- a/optimade/server/config.py +++ b/optimade/server/config.py @@ -234,6 +234,9 @@ class ServerConfig(BaseSettings): "broken down by endpoint." ), ) + supported_prefixes: List[str] = Field( + [], description="A list of all the prefixes that are supported by this server." + ) aliases: Dict[Literal["links", "references", "structures"], Dict[str, str]] = Field( {}, description=( @@ -309,6 +312,12 @@ class ServerConfig(BaseSettings): only the mapping of aliases will occur.""", ) + @validator("supported_prefixes") + def add_own_prefix_to_supported_prefixes(value, values): + if values["provider"].prefix not in value: + value.append(values["provider"].prefix) + return value + @validator("implementation", pre=True) def set_implementation_version(cls, v): """Set defaults and modify bypassed value(s)""" diff --git a/optimade/server/mappers/entries.py b/optimade/server/mappers/entries.py index d46566386..1bfa91391 100644 --- a/optimade/server/mappers/entries.py +++ b/optimade/server/mappers/entries.py @@ -133,18 +133,10 @@ def all_aliases(cls) -> Iterable[Tuple[str, str]]: @classproperty @lru_cache(maxsize=1) def SUPPORTED_PREFIXES(cls) -> Set[str]: - """A set of prefixes handled by this entry type. - - !!! note - This implementation only includes the provider prefix, - but in the future this property may be extended to include other - namespaces (for serving fields from, e.g., other providers or - domain-specific terms). - - """ + """A set of prefixes handled by this entry type.""" from optimade.server.config import CONFIG - return {CONFIG.provider.prefix} + return set(CONFIG.supported_prefixes) @classproperty def ALL_ATTRIBUTES(cls) -> Set[str]: From 13fcdc00c041a018dc5305469536aca13c5d18e5 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Thu, 12 Oct 2023 16:11:08 +0200 Subject: [PATCH 74/86] Add numpy to requirements. --- pyproject.toml | 1 + requirements-server.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 1cbfffc3a..4b08f5830 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -68,6 +68,7 @@ server = [ "fastapi>=0.103.1", "pyyaml~=6.0", "optimade[mongo]", + "numpy>=1.20" ] # Client minded diff --git a/requirements-server.txt b/requirements-server.txt index 04f5dc957..7ac9463e2 100644 --- a/requirements-server.txt +++ b/requirements-server.txt @@ -2,4 +2,5 @@ elasticsearch==7.17.7 elasticsearch-dsl==7.4.0 fastapi==0.103.1 mongomock==4.1.2 +numpy>=1.20 pymongo==4.5.0 From 3be7d5404d4a30a8b9e76864fcaad75907747aa6 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Fri, 13 Oct 2023 10:49:44 +0200 Subject: [PATCH 75/86] Added jsonlines dependancy. --- pyproject.toml | 3 ++- requirements-server.txt | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 4b08f5830..e61822474 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -68,7 +68,8 @@ server = [ "fastapi>=0.103.1", "pyyaml~=6.0", "optimade[mongo]", - "numpy>=1.20" + "numpy>=1.20", + "jsonlines>=3.1" ] # Client minded diff --git a/requirements-server.txt b/requirements-server.txt index 7ac9463e2..f486c1d85 100644 --- a/requirements-server.txt +++ b/requirements-server.txt @@ -1,6 +1,7 @@ elasticsearch==7.17.7 elasticsearch-dsl==7.4.0 fastapi==0.103.1 +jsonlines>=3.1 mongomock==4.1.2 numpy>=1.20 pymongo==4.5.0 From 075ef065a91922d68e8748811541885ec760f78b Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Fri, 13 Oct 2023 17:02:02 +0200 Subject: [PATCH 76/86] Made changes to make mypy happy. --- optimade/server/entry_collections/mongo.py | 17 ++++++++++------- optimade/server/exception_handlers.py | 2 +- optimade/server/main.py | 2 +- optimade/server/routers/utils.py | 18 ++++++++++-------- 4 files changed, 22 insertions(+), 17 deletions(-) diff --git a/optimade/server/entry_collections/mongo.py b/optimade/server/entry_collections/mongo.py index 3f2dca327..9e40b0853 100644 --- a/optimade/server/entry_collections/mongo.py +++ b/optimade/server/entry_collections/mongo.py @@ -102,21 +102,24 @@ def count(self, **kwargs: Any) -> int: kwargs["filter"] = {} return len(self.collection.find(**kwargs)) - def insert(self, content: bytes, filename: str, metadata: dict = {}) -> None: + def insert(self, data: list) -> None: """Add the given entries to the underlying database. Warning: No validation is performed on the incoming data. Arguments: - content: The file content to add to gridfs. - filename: The filename of the added content. - metadata: extra metadata to add to the gridfs entry. + data: a list of dictionaries. Each dictionary contains the data belonging to one file. + These dictionaries contain the fields: + data: The file content to add to gridfs. + filename: The filename of the added content. + metadata: extra metadata to add to the gridfs entry. """ - self.collection.put(content, filename=filename, metadata=metadata) + for entry in data: # todo check whether I can insert multiple files in one go. + self.collection.put(**entry) def handle_query_params( - self, params: Union[SingleEntryQueryParams, PartialDataQueryParams] + self, params: Union[SingleEntryQueryParams, PartialDataQueryParams] # type: ignore[override] ) -> Dict[str, Any]: """Parse and interpret the backend-agnostic query parameter models into a dictionary that can be used by MongoDB. @@ -373,7 +376,7 @@ def insert(self, data: List[EntryResource]) -> None: self.collection.insert_many(data) def handle_query_params( - self, params: Union[EntryListingQueryParams, SingleEntryQueryParams] + self, params: Union[EntryListingQueryParams, SingleEntryQueryParams] # type: ignore[override] ) -> Dict[str, Any]: """Parse and interpret the backend-agnostic query parameter models into a dictionary that can be used by MongoDB. diff --git a/optimade/server/exception_handlers.py b/optimade/server/exception_handlers.py index 06fc083a8..efbaa0a72 100644 --- a/optimade/server/exception_handlers.py +++ b/optimade/server/exception_handlers.py @@ -230,7 +230,7 @@ def general_exception_handler(request: Request, exc: Exception) -> JSONAPIRespon (OptimadeHTTPException, http_exception_handler), (RequestValidationError, request_validation_exception_handler), (ValidationError, validation_exception_handler), - (VisitError, grammar_not_implemented_handler), + (VisitError, grammar_not_implemented_handler), # type: ignore[list-item] # not entirely sure why this entry triggers mypy (NotImplementedError, not_implemented_handler), # type: ignore[list-item] # not entirely sure why this entry triggers mypy (Exception, general_exception_handler), ] diff --git a/optimade/server/main.py b/optimade/server/main.py index 56efd4c16..5f75db972 100644 --- a/optimade/server/main.py +++ b/optimade/server/main.py @@ -101,7 +101,7 @@ def read_array_header(fobj): "name": numpy_meta[2].name, "itemsize": numpy_meta[2].itemsize, } - partial_data_coll.insert(f, filename=filename, metadata=metadata) + partial_data_coll.insert([{"data": f, "filename": filename, "metadata": metadata}]) # type: ignore[list-item] # Todo : Perhaps this can be reduced to a single insert statement. def load_entries(endpoint_name: str, endpoint_collection: EntryCollection): LOGGER.debug("Loading test %s...", endpoint_name) diff --git a/optimade/server/routers/utils.py b/optimade/server/routers/utils.py index 27fd2a6c9..24b1a2018 100644 --- a/optimade/server/routers/utils.py +++ b/optimade/server/routers/utils.py @@ -420,11 +420,13 @@ def get_partial_entry( ) array = np.frombuffer( - results["attributes"]["data"], - dtype=getattr(np, results["attributes"]["dtype"]["name"]), - ).reshape(results["attributes"]["shape"]) + results["attributes"]["data"], # type: ignore[call-overload] + dtype=getattr(np, results["attributes"]["dtype"]["name"]), # type: ignore[call-overload] + ).reshape( + results["attributes"]["shape"] # type: ignore[call-overload] + ) # slice array - property_ranges = results["attributes"]["property_ranges"] + property_ranges = results["attributes"]["property_ranges"] # type: ignore[call-overload] slice_ind = [ slice( 0, @@ -455,14 +457,14 @@ def get_partial_entry( "has_references": False, } # Todo: add support for non_dense data if more_data_available: - next_link = ["PARTIAL-DATA-NEXT", [results["attributes"].pop("next")]] + next_link = ["PARTIAL-DATA-NEXT", [results["attributes"].pop("next")]] # type: ignore[call-overload] if params.response_format == "json": for key in header: - results["attributes"][key] = header[key] - results["attributes"]["data"] = array.tolist() + results["attributes"][key] = header[key] # type: ignore[call-overload] + results["attributes"]["data"] = array.tolist() # type: ignore[call-overload] if more_data_available: - results["attributes"]["next"] = next_link + results["attributes"]["next"] = next_link # type: ignore[call-overload] return dict( links=links, data=[results] if results else None, From ca2a0f219174963a5d6b0e8f985080c25a72cd5e Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Fri, 13 Oct 2023 17:44:39 +0200 Subject: [PATCH 77/86] Trying to determine why jsonlines is not impoeted properly on github. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index e61822474..536eca497 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -69,7 +69,7 @@ server = [ "pyyaml~=6.0", "optimade[mongo]", "numpy>=1.20", - "jsonlines>=3.1" + "jsonlines>=3.1", ] # Client minded From cf454885588f8154d817ac5a56d449f4203eebed Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Fri, 13 Oct 2023 18:15:58 +0200 Subject: [PATCH 78/86] Update requirements-client.txt (#1813) removed duplicate requirement --- requirements-client.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements-client.txt b/requirements-client.txt index 2c0b7f4f4..d25adb217 100644 --- a/requirements-client.txt +++ b/requirements-client.txt @@ -2,7 +2,6 @@ aiida-core==2.4.0 ase==3.22.1 emmet_core==0.68.0 jarvis-tools==2023.9.20 -jarvis-tools==2023.9.20 mp-api==0.36.1 numpy>=1.20 pymatgen==2023.9.10 From 8510ffa2362519f7702818ced26416a547b9403c Mon Sep 17 00:00:00 2001 From: Matthew Evans <7916000+ml-evs@users.noreply.github.com> Date: Sun, 15 Oct 2023 15:30:56 +0100 Subject: [PATCH 79/86] Modernize all Python 3.8 annotations (#1815) * Use Python 3.9 as the 'base' CI version for linting * Update pre-commit hooks * Run `pyupgrade --py39-plus` to upgrade legacy annotations * Add `--exit-non-zero-on-fix` for ruff Co-authored-by: Casper Welzel Andersen <43357585+CasperWA@users.noreply.github.com> * Use f-string over format Co-authored-by: Casper Welzel Andersen <43357585+CasperWA@users.noreply.github.com> --------- Co-authored-by: Casper Welzel Andersen <43357585+CasperWA@users.noreply.github.com> --- .github/workflows/ci.yml | 26 ++--- .pre-commit-config.yaml | 16 ++- optimade/adapters/base.py | 14 +-- optimade/adapters/references/adapter.py | 4 +- optimade/adapters/structures/adapter.py | 10 +- optimade/adapters/structures/aiida.py | 4 +- optimade/adapters/structures/ase.py | 3 +- optimade/adapters/structures/cif.py | 5 +- .../adapters/structures/proteindatabank.py | 5 +- optimade/adapters/structures/pymatgen.py | 8 +- optimade/adapters/structures/utils.py | 39 ++++---- optimade/client/cli.py | 14 +-- optimade/client/client.py | 98 +++++++++---------- optimade/client/utils.py | 20 ++-- optimade/exceptions.py | 6 +- optimade/filterparser/lark_parser.py | 10 +- .../filtertransformers/base_transformer.py | 18 ++-- optimade/filtertransformers/elasticsearch.py | 10 +- optimade/filtertransformers/mongo.py | 12 +-- optimade/models/baseinfo.py | 10 +- optimade/models/entries.py | 8 +- optimade/models/index_metadb.py | 4 +- optimade/models/jsonapi.py | 12 +-- optimade/models/optimade_json.py | 10 +- optimade/models/references.py | 6 +- optimade/models/responses.py | 22 ++--- optimade/models/structures.py | 34 +++---- optimade/models/utils.py | 4 +- optimade/server/config.py | 18 ++-- .../server/entry_collections/elasticsearch.py | 21 ++-- .../entry_collections/entry_collections.py | 39 ++++---- optimade/server/entry_collections/mongo.py | 14 +-- optimade/server/exception_handlers.py | 9 +- optimade/server/mappers/entries.py | 33 ++++--- optimade/server/middleware.py | 9 +- optimade/server/query_params.py | 6 +- optimade/server/routers/landing.py | 2 +- optimade/server/routers/links.py | 4 +- optimade/server/routers/references.py | 6 +- optimade/server/routers/structures.py | 6 +- optimade/server/routers/utils.py | 34 +++---- optimade/server/schemas.py | 7 +- optimade/utils.py | 5 +- optimade/validator/config.py | 25 ++--- optimade/validator/utils.py | 26 ++--- optimade/validator/validator.py | 68 ++++++------- tasks.py | 12 +-- tests/adapters/references/conftest.py | 4 +- tests/adapters/structures/conftest.py | 13 +-- tests/adapters/structures/utils.py | 2 +- tests/filterparser/test_filterparser.py | 3 +- tests/models/conftest.py | 2 +- tests/models/test_jsonapi.py | 2 +- tests/models/test_optimade_json.py | 2 +- tests/models/test_structures.py | 2 +- tests/models/test_utils.py | 6 +- tests/server/conftest.py | 7 +- tests/server/query_params/conftest.py | 8 +- tests/server/routers/test_utils.py | 5 +- tests/server/test_client.py | 18 ++-- tests/server/test_config.py | 2 +- tests/server/utils.py | 11 ++- 62 files changed, 430 insertions(+), 433 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7c62a743b..4e3225a89 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,10 +26,10 @@ jobs: with: submodules: true - - name: Set up Python 3.10 + - name: Set up Python 3.9 uses: actions/setup-python@v4 with: - python-version: '3.10' + python-version: '3.9' cache: 'pip' cache-dependency-path: | requirements*.txt @@ -56,10 +56,10 @@ jobs: with: submodules: true - - name: Set up Python 3.10 + - name: Set up Python 3.9 uses: actions/setup-python@v4 with: - python-version: '3.10' + python-version: '3.9' cache: 'pip' cache-dependency-path: | requirements*.txt @@ -227,27 +227,27 @@ jobs: run: pytest -rs -vvv --cov=./optimade/ --cov-report=xml --cov-append tests/adapters/ - name: Run tests for validator only to assess coverage (mongomock) - if: matrix.python-version == 3.10 + if: matrix.python-version == 3.9 run: pytest -rs --cov=./optimade/ --cov-report=xml:validator_cov.xml --cov-append tests/server/test_server_validation.py env: OPTIMADE_DATABASE_BACKEND: 'mongomock' - name: Run tests for validator only to assess coverage (Elasticsearch) - if: matrix.python-version == 3.10 + if: matrix.python-version == 3.9 run: pytest -rs --cov=./optimade/ --cov-report=xml:validator_cov.xml --cov-append tests/server/test_server_validation.py env: OPTIMADE_DATABASE_BACKEND: 'elastic' OPTIMADE_INSERT_TEST_DATA: false # Must be specified as previous steps will have already inserted the test data - name: Run tests for validator only to assess coverage (MongoDB) - if: matrix.python-version == 3.10 + if: matrix.python-version == 3.9 run: pytest -rs --cov=./optimade/ --cov-report=xml:validator_cov.xml --cov-append tests/server/test_server_validation.py env: OPTIMADE_DATABASE_BACKEND: 'mongodb' OPTIMADE_INSERT_TEST_DATA: false # Must be specified as previous steps will have already inserted the test data - name: Run the OPTIMADE Client CLI - if: matrix.python-version == 3.10 + if: matrix.python-version == 3.9 run: | coverage run --append --source optimade optimade/client/cli.py \ --filter 'nsites = 1' \ @@ -275,7 +275,7 @@ jobs: coverage xml - name: Upload coverage to Codecov - if: matrix.python-version == '3.10' && github.repository == 'Materials-Consortia/optimade-python-tools' + if: matrix.python-version == '3.9' && github.repository == 'Materials-Consortia/optimade-python-tools' uses: codecov/codecov-action@v3 with: name: project @@ -283,7 +283,7 @@ jobs: flags: project - name: Upload validator coverage to Codecov - if: matrix.python-version == '3.10' && github.repository == 'Materials-Consortia/optimade-python-tools' + if: matrix.python-version == '3.9' && github.repository == 'Materials-Consortia/optimade-python-tools' uses: codecov/codecov-action@v3 with: name: validator @@ -300,7 +300,7 @@ jobs: - uses: actions/setup-python@v4 with: - python-version: '3.10' + python-version: '3.9' cache: 'pip' cache-dependency-path: | requirements*.txt @@ -330,10 +330,10 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - name: Set up Python 3.10 + - name: Set up Python 3.9 uses: actions/setup-python@v4 with: - python-version: '3.10' + python-version: '3.9' cache: 'pip' cache-dependency-path: | requirements*.txt diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7e09132cd..5aa603b46 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,5 @@ default_language_version: - python: python3.10 + python: python3.9 repos: - repo: https://github.com/ambv/black @@ -9,7 +9,7 @@ repos: name: Blacken - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: check-symlinks - id: check-yaml @@ -23,11 +23,17 @@ repos: - id: trailing-whitespace args: [--markdown-linebreak-ext=md] + - repo: https://github.com/asottile/pyupgrade + rev: v3.15.0 + hooks: + - id: pyupgrade + args: ["--py39-plus"] + - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: 'v0.0.291' + rev: 'v0.0.292' hooks: - id: ruff - args: [--fix] + args: [--fix, --exit-non-zero-on-fix] - repo: local hooks: @@ -46,7 +52,7 @@ repos: description: Update the API Reference documentation whenever a Python file is touched in the code base. - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.5.1 + rev: v1.6.0 hooks: - id: mypy name: "MyPy" diff --git a/optimade/adapters/base.py b/optimade/adapters/base.py index f1d4bcb63..7d21d06dc 100644 --- a/optimade/adapters/base.py +++ b/optimade/adapters/base.py @@ -19,7 +19,7 @@ and [`StructureResource`][optimade.models.structures.StructureResource]s, respectively. """ import re -from typing import Any, Callable, Dict, List, Optional, Tuple, Type, Union +from typing import Any, Callable, Optional, Union from pydantic import BaseModel # pylint: disable=no-name-in-module @@ -42,10 +42,10 @@ class EntryAdapter: """ - ENTRY_RESOURCE: Type[EntryResource] = EntryResource - _type_converters: Dict[str, Callable] = {} - _type_ingesters: Dict[str, Callable] = {} - _type_ingesters_by_type: Dict[str, Type] = {} + ENTRY_RESOURCE: type[EntryResource] = EntryResource + _type_converters: dict[str, Callable] = {} + _type_ingesters: dict[str, Callable] = {} + _type_ingesters_by_type: dict[str, type] = {} def __init__(self, entry: dict) -> None: """ @@ -53,7 +53,7 @@ def __init__(self, entry: dict) -> None: entry (dict): A JSON OPTIMADE single resource entry. """ self._entry: Optional[EntryResource] = None - self._converted: Dict[str, Any] = {} + self._converted: dict[str, Any] = {} self.entry: EntryResource = entry # type: ignore[assignment] @@ -164,7 +164,7 @@ def ingest_from(cls, data: Any, format: Optional[str] = None) -> Any: @staticmethod def _get_model_attributes( - starting_instances: Union[Tuple[BaseModel, ...], List[BaseModel]], name: str + starting_instances: Union[tuple[BaseModel, ...], list[BaseModel]], name: str ) -> Any: """Helper method for retrieving the OPTIMADE model's attribute, supporting "."-nested attributes""" for res in starting_instances: diff --git a/optimade/adapters/references/adapter.py b/optimade/adapters/references/adapter.py index ef03e5396..cd3ebbae6 100644 --- a/optimade/adapters/references/adapter.py +++ b/optimade/adapters/references/adapter.py @@ -1,5 +1,3 @@ -from typing import Type - from optimade.adapters.base import EntryAdapter from optimade.models import ReferenceResource @@ -21,4 +19,4 @@ class Reference(EntryAdapter): """ - ENTRY_RESOURCE: Type[ReferenceResource] = ReferenceResource + ENTRY_RESOURCE: type[ReferenceResource] = ReferenceResource diff --git a/optimade/adapters/structures/adapter.py b/optimade/adapters/structures/adapter.py index 4ae622497..f7641c437 100644 --- a/optimade/adapters/structures/adapter.py +++ b/optimade/adapters/structures/adapter.py @@ -1,4 +1,4 @@ -from typing import Callable, Dict, Type +from typing import Callable from optimade.adapters.base import EntryAdapter from optimade.models import StructureResource @@ -44,8 +44,8 @@ class Structure(EntryAdapter): """ - ENTRY_RESOURCE: Type[StructureResource] = StructureResource - _type_converters: Dict[str, Callable] = { + ENTRY_RESOURCE: type[StructureResource] = StructureResource + _type_converters: dict[str, Callable] = { "aiida_structuredata": get_aiida_structure_data, "ase": get_ase_atoms, "cif": get_cif, @@ -55,12 +55,12 @@ class Structure(EntryAdapter): "jarvis": get_jarvis_atoms, } - _type_ingesters: Dict[str, Callable] = { + _type_ingesters: dict[str, Callable] = { "pymatgen": from_pymatgen, "ase": from_ase_atoms, } - _type_ingesters_by_type: Dict[str, Type] = { + _type_ingesters_by_type: dict[str, type] = { "pymatgen": PymatgenStructure, "ase": ASEAtoms, } diff --git a/optimade/adapters/structures/aiida.py b/optimade/adapters/structures/aiida.py index f65c0babb..8bfe1e297 100644 --- a/optimade/adapters/structures/aiida.py +++ b/optimade/adapters/structures/aiida.py @@ -7,7 +7,7 @@ This conversion function relies on the [`aiida-core`](https://github.com/aiidateam/aiida-core) package. """ -from typing import List, Optional +from typing import Optional from warnings import warn from optimade.adapters.structures.utils import pad_cell, species_from_species_at_sites @@ -48,7 +48,7 @@ def get_aiida_structure_data(optimade_structure: OptimadeStructure) -> Structure structure = StructureData(cell=lattice_vectors) # If species not provided, infer data from species_at_sites - species: Optional[List[OptimadeStructureSpecies]] = attributes.species + species: Optional[list[OptimadeStructureSpecies]] = attributes.species if not species: species = species_from_species_at_sites(attributes.species_at_sites) # type: ignore[arg-type] diff --git a/optimade/adapters/structures/ase.py b/optimade/adapters/structures/ase.py index e049663d6..5c8c09e65 100644 --- a/optimade/adapters/structures/ase.py +++ b/optimade/adapters/structures/ase.py @@ -7,7 +7,6 @@ For more information on the ASE code see [their documentation](https://wiki.fysik.dtu.dk/ase/). """ -from typing import Dict from optimade.adapters.exceptions import ConversionError from optimade.adapters.structures.utils import ( @@ -66,7 +65,7 @@ def get_ase_atoms(optimade_structure: OptimadeStructure) -> Atoms: if not species: species = species_from_species_at_sites(attributes.species_at_sites) # type: ignore[arg-type] - optimade_species: Dict[str, OptimadeStructureSpecies] = {_.name: _ for _ in species} + optimade_species: dict[str, OptimadeStructureSpecies] = {_.name: _ for _ in species} # Since we've made sure there are no species with more than 1 chemical symbol, # asking for index 0 will always work. diff --git a/optimade/adapters/structures/cif.py b/optimade/adapters/structures/cif.py index cbe35901c..295c6e9f7 100644 --- a/optimade/adapters/structures/cif.py +++ b/optimade/adapters/structures/cif.py @@ -16,7 +16,6 @@ This conversion function relies on the [NumPy](https://numpy.org/) library. """ -from typing import Dict from optimade.adapters.structures.utils import ( cell_to_cellpar, @@ -123,11 +122,11 @@ def get_cif( # pylint: disable=too-many-locals,too-many-branches else: sites = attributes.cartesian_site_positions - species: Dict[str, OptimadeStructureSpecies] = { + species: dict[str, OptimadeStructureSpecies] = { species.name: species for species in attributes.species # type: ignore[union-attr] } - symbol_occurences: Dict[str, int] = {} + symbol_occurences: dict[str, int] = {} for site_number in range(attributes.nsites): # type: ignore[arg-type] species_name = attributes.species_at_sites[site_number] # type: ignore[index] site = sites[site_number] diff --git a/optimade/adapters/structures/proteindatabank.py b/optimade/adapters/structures/proteindatabank.py index f2e699408..b6ae96549 100644 --- a/optimade/adapters/structures/proteindatabank.py +++ b/optimade/adapters/structures/proteindatabank.py @@ -21,7 +21,6 @@ Warning: Currently, the PDBx/mmCIF conversion function is not parsing as a complete PDBx/mmCIF file. """ -from typing import Dict try: import numpy as np @@ -164,7 +163,7 @@ def get_pdbx_mmcif( # pylint: disable=too-many-locals else: sites = attributes.cartesian_site_positions - species: Dict[str, OptimadeStructureSpecies] = { + species: dict[str, OptimadeStructureSpecies] = { species.name: species for species in attributes.species # type: ignore[union-attr] } @@ -240,7 +239,7 @@ def get_pdb( # pylint: disable=too-many-locals pdb += "MODEL 1\n" - species: Dict[str, OptimadeStructureSpecies] = { + species: dict[str, OptimadeStructureSpecies] = { species.name: species for species in attributes.species # type:ignore[union-attr] } diff --git a/optimade/adapters/structures/pymatgen.py b/optimade/adapters/structures/pymatgen.py index 8d46bf7fc..f6f62a7ad 100644 --- a/optimade/adapters/structures/pymatgen.py +++ b/optimade/adapters/structures/pymatgen.py @@ -7,7 +7,7 @@ For more information on the pymatgen code see [their documentation](https://pymatgen.org). """ -from typing import Dict, List, Optional, Union +from typing import Optional, Union from optimade.adapters.structures.utils import ( species_from_species_at_sites, @@ -105,9 +105,9 @@ def _get_molecule(optimade_structure: OptimadeStructure) -> Molecule: def _pymatgen_species( nsites: int, - species: Optional[List[OptimadeStructureSpecies]], - species_at_sites: List[str], -) -> List[Dict[str, float]]: + species: Optional[list[OptimadeStructureSpecies]], + species_at_sites: list[str], +) -> list[dict[str, float]]: """ Create list of {"symbol": "concentration"} per site for values to pymatgen species parameters. Remove vacancies, if they are present. diff --git a/optimade/adapters/structures/utils.py b/optimade/adapters/structures/utils.py index 2b36570bd..3461d49dd 100644 --- a/optimade/adapters/structures/utils.py +++ b/optimade/adapters/structures/utils.py @@ -3,7 +3,8 @@ Most of these functions rely on the [NumPy](https://numpy.org/) library. """ -from typing import Iterable, List, Optional, Tuple, Type +from collections.abc import Iterable +from typing import Optional from optimade.models.structures import Species as OptimadeStructureSpecies from optimade.models.structures import Vector3D @@ -19,7 +20,7 @@ NUMPY_NOT_FOUND = "NumPy not found, cannot convert structure to your desired format" -def valid_lattice_vector(lattice_vec: Tuple[Vector3D, Vector3D, Vector3D]): +def valid_lattice_vector(lattice_vec: tuple[Vector3D, Vector3D, Vector3D]): if len(lattice_vec) != 3: return False for vector in lattice_vec: @@ -31,8 +32,8 @@ def valid_lattice_vector(lattice_vec: Tuple[Vector3D, Vector3D, Vector3D]): def scaled_cell( - cell: Tuple[Vector3D, Vector3D, Vector3D] -) -> Tuple[Vector3D, Vector3D, Vector3D]: + cell: tuple[Vector3D, Vector3D, Vector3D] +) -> tuple[Vector3D, Vector3D, Vector3D]: """Return a scaled 3x3 cell from cartesian 3x3 cell (`lattice_vectors`). This 3x3 matrix can be used to calculate the fractional coordinates from the cartesian_site_positions. @@ -62,8 +63,8 @@ def scaled_cell( def fractional_coordinates( - cell: Tuple[Vector3D, Vector3D, Vector3D], cartesian_positions: List[Vector3D] -) -> List[Vector3D]: + cell: tuple[Vector3D, Vector3D, Vector3D], cartesian_positions: list[Vector3D] +) -> list[Vector3D]: """Returns fractional coordinates and wraps coordinates to `[0,1[`. Note: @@ -101,8 +102,8 @@ def fractional_coordinates( def cell_to_cellpar( - cell: Tuple[Vector3D, Vector3D, Vector3D], radians: bool = False -) -> List[float]: + cell: tuple[Vector3D, Vector3D, Vector3D], radians: bool = False +) -> list[float]: """Returns the cell parameters `[a, b, c, alpha, beta, gamma]`. Angles are in degrees unless `radian=True` is used. @@ -161,10 +162,10 @@ def unit_vector(x: Vector3D) -> Vector3D: def cellpar_to_cell( - cellpar: List[float], - ab_normal: Tuple[int, int, int] = (0, 0, 1), - a_direction: Optional[Tuple[int, int, int]] = None, -) -> List[Vector3D]: + cellpar: list[float], + ab_normal: tuple[int, int, int] = (0, 0, 1), + a_direction: Optional[tuple[int, int, int]] = None, +) -> list[Vector3D]: """Return a 3x3 cell matrix from `cellpar=[a,b,c,alpha,beta,gamma]`. Angles must be in degrees. @@ -277,9 +278,9 @@ def cellpar_to_cell( def _pad_iter_of_iters( iterable: Iterable[Iterable], padding: Optional[float] = None, - outer: Optional[Type] = None, - inner: Optional[Type] = None, -) -> Tuple[Iterable[Iterable], bool]: + outer: Optional[type] = None, + inner: Optional[type] = None, +) -> tuple[Iterable[Iterable], bool]: """Turn any null/None values into a float in given iterable of iterables""" try: padding = float(padding) # type: ignore[arg-type] @@ -306,7 +307,7 @@ def _pad_iter_of_iters( def pad_cell( - lattice_vectors: Tuple[Vector3D, Vector3D, Vector3D], + lattice_vectors: tuple[Vector3D, Vector3D, Vector3D], padding: Optional[float] = None, ) -> tuple: # Setting this properly makes MkDocs fail. """Turn any `null`/`None` values into a `float` in given `tuple` of @@ -333,8 +334,8 @@ def pad_cell( def species_from_species_at_sites( - species_at_sites: List[str], -) -> List[OptimadeStructureSpecies]: + species_at_sites: list[str], +) -> list[OptimadeStructureSpecies]: """When a list of species dictionaries is not provided, this function can be used to infer the species from the provided species_at_sites. @@ -357,7 +358,7 @@ def species_from_species_at_sites( ] -def elements_ratios_from_species_at_sites(species_at_sites: List[str]) -> List[float]: +def elements_ratios_from_species_at_sites(species_at_sites: list[str]) -> list[float]: """Compute the OPTIMADE `elements_ratios` field from `species_at_sites` in the case where `species_at_sites` refers to sites wholly occupied by the given elements, e.g., not arbitrary species labels or with partial/mixed occupancy. diff --git a/optimade/client/cli.py b/optimade/client/cli.py index d4f9a923c..2c02af6a4 100644 --- a/optimade/client/cli.py +++ b/optimade/client/cli.py @@ -166,13 +166,13 @@ def _get( base_urls=base_url, use_async=use_async, max_results_per_provider=max_results_per_provider, - include_providers=set(_.strip() for _ in include_providers.split(",")) + include_providers={_.strip() for _ in include_providers.split(",")} if include_providers else None, - exclude_providers=set(_.strip() for _ in exclude_providers.split(",")) + exclude_providers={_.strip() for _ in exclude_providers.split(",")} if exclude_providers else None, - exclude_databases=set(_.strip() for _ in exclude_databases.split(",")) + exclude_databases={_.strip() for _ in exclude_databases.split(",")} if exclude_databases else None, silent=silent, @@ -211,13 +211,15 @@ def _get( if not output_file: if pretty_print: - rich.print_json(data=results, indent=2, default=lambda _: _.dict()) + rich.print_json(data=results, indent=2, default=lambda _: _.asdict()) else: - sys.stdout.write(json.dumps(results, indent=2, default=lambda _: _.dict())) + sys.stdout.write( + json.dumps(results, indent=2, default=lambda _: _.asdict()) + ) if output_file: with open(output_file, "w") as f: - json.dump(results, f, indent=2, default=lambda _: _.dict()) + json.dump(results, f, indent=2, default=lambda _: _.asdict()) if __name__ == "__main__": diff --git a/optimade/client/client.py b/optimade/client/client.py index 1efacca15..3dc7d5fd6 100644 --- a/optimade/client/client.py +++ b/optimade/client/client.py @@ -10,18 +10,8 @@ import json import time from collections import defaultdict -from typing import ( - Any, - Callable, - Dict, - Iterable, - List, - Optional, - Set, - Tuple, - Type, - Union, -) +from collections.abc import Iterable +from typing import Any, Callable, Optional, Union from urllib.parse import urlparse # External deps that are only used in the client code @@ -70,12 +60,12 @@ class OptimadeClient: base_urls: Union[str, Iterable[str]] """A list (or any iterable) of OPTIMADE base URLs to query.""" - all_results: Dict[str, Dict[str, Dict[str, QueryResults]]] = defaultdict(dict) + all_results: dict[str, dict[str, dict[str, QueryResults]]] = defaultdict(dict) """A nested dictionary keyed by endpoint and OPTIMADE filter string that contains the results from each base URL for that particular filter. """ - count_results: Dict[str, Dict[str, Dict[str, int]]] = defaultdict(dict) + count_results: dict[str, dict[str, dict[str, int]]] = defaultdict(dict) """A nested dictionary keyed by endpoint and OPTIMADE filter string that contains the number of results from each base URL for that particular filter. """ @@ -85,12 +75,12 @@ class OptimadeClient: download all. """ - property_lists: Dict[str, Dict[str, List[str]]] = defaultdict(dict) + property_lists: dict[str, dict[str, list[str]]] = defaultdict(dict) """A dictionary containing list of properties served by each database, broken down by entry type, then database. """ - headers: Dict = {"User-Agent": f"optimade-python-tools/{__version__}"} + headers: dict = {"User-Agent": f"optimade-python-tools/{__version__}"} """Additional HTTP headers.""" http_timeout: httpx.Timeout = httpx.Timeout(10.0, read=1000.0) @@ -102,7 +92,7 @@ class OptimadeClient: use_async: bool """Whether or not to make all requests asynchronously using asyncio.""" - callbacks: Optional[List[Callable[[str, Dict], Union[None, Dict]]]] = None + callbacks: Optional[list[Callable[[str, dict], Union[None, dict]]]] = None """A list of callbacks to execute after each successful request, used to e.g., write to a file, add results to a database or perform additional filtering. @@ -121,13 +111,13 @@ class OptimadeClient: silent: bool """Whether to disable progress bar printing.""" - _excluded_providers: Optional[Set[str]] = None + _excluded_providers: Optional[set[str]] = None """A set of providers IDs excluded from future queries.""" - _included_providers: Optional[Set[str]] = None + _included_providers: Optional[set[str]] = None """A set of providers IDs included from future queries.""" - _excluded_databases: Optional[Set[str]] = None + _excluded_databases: Optional[set[str]] = None """A set of child database URLs excluded from future queries.""" __current_endpoint: Optional[str] = None @@ -135,7 +125,7 @@ class OptimadeClient: chosen endpoint. Should be reset to `None` outside of all `get()` calls.""" _http_client: Optional[ - Union[Type[httpx.AsyncClient], Type[requests.Session]] + Union[type[httpx.AsyncClient], type[requests.Session]] ] = None """Override the HTTP client class, primarily used for testing.""" @@ -148,18 +138,18 @@ def __init__( self, base_urls: Optional[Union[str, Iterable[str]]] = None, max_results_per_provider: int = 1000, - headers: Optional[Dict] = None, + headers: Optional[dict] = None, http_timeout: Optional[Union[httpx.Timeout, float]] = None, max_attempts: int = 5, use_async: bool = True, silent: bool = False, - exclude_providers: Optional[List[str]] = None, - include_providers: Optional[List[str]] = None, - exclude_databases: Optional[List[str]] = None, + exclude_providers: Optional[list[str]] = None, + include_providers: Optional[list[str]] = None, + exclude_databases: Optional[list[str]] = None, http_client: Optional[ - Union[Type[httpx.AsyncClient], Type[requests.Session]] + Union[type[httpx.AsyncClient], type[requests.Session]] ] = None, - callbacks: Optional[List[Callable[[str, Dict], Union[None, Dict]]]] = None, + callbacks: Optional[list[Callable[[str, dict], Union[None, dict]]]] = None, ): """Create the OPTIMADE client object. @@ -284,9 +274,9 @@ def get( self, filter: Optional[str] = None, endpoint: Optional[str] = None, - response_fields: Optional[List[str]] = None, + response_fields: Optional[list[str]] = None, sort: Optional[str] = None, - ) -> Dict[str, Dict[str, Dict[str, Dict]]]: + ) -> dict[str, dict[str, dict[str, dict]]]: """Gets the results from the endpoint and filter across the defined OPTIMADE APIs. @@ -338,11 +328,11 @@ def get( sort=sort, ) self.all_results[endpoint][filter] = results - return {endpoint: {filter: {k: results[k].dict() for k in results}}} + return {endpoint: {filter: {k: results[k].asdict() for k in results}}} def count( self, filter: Optional[str] = None, endpoint: Optional[str] = None - ) -> Dict[str, Dict[str, Dict[str, Optional[int]]]]: + ) -> dict[str, dict[str, dict[str, Optional[int]]]]: """Counts the number of results for the filter, requiring only 1 request per provider by making use of the `meta->data_returned` key. @@ -405,7 +395,7 @@ def count( def list_properties( self, entry_type: str, - ) -> Dict[str, List[str]]: + ) -> dict[str, list[str]]: """Returns the list of properties reported at `/info/` for the given entry type, for each database. @@ -437,7 +427,7 @@ def list_properties( ) return self.property_lists[entry_type] - def search_property(self, query: str, entry_type: str) -> Dict[str, List[str]]: + def search_property(self, query: str, entry_type: str) -> dict[str, list[str]]: """Searches for the query substring within the listed properties served by each database. @@ -453,7 +443,7 @@ def search_property(self, query: str, entry_type: str) -> Dict[str, List[str]]: if not self.property_lists: self.list_properties(entry_type=entry_type) - matching_properties: Dict[str, Dict[str, List[str]]] = { + matching_properties: dict[str, dict[str, list[str]]] = { entry_type: defaultdict(list) } if entry_type in self.property_lists: @@ -469,9 +459,9 @@ def _execute_queries( endpoint: str, page_limit: Optional[int], paginate: bool, - response_fields: Optional[List[str]], + response_fields: Optional[list[str]], sort: Optional[str], - ) -> Dict[str, QueryResults]: + ) -> dict[str, QueryResults]: """Executes the queries over the base URLs either asynchronously or serially, depending on the `self.use_async` setting. @@ -535,11 +525,11 @@ def get_one( endpoint: str, filter: str, base_url: str, - response_fields: Optional[List[str]] = None, + response_fields: Optional[list[str]] = None, sort: Optional[str] = None, page_limit: Optional[int] = None, paginate: bool = True, - ) -> Dict[str, QueryResults]: + ) -> dict[str, QueryResults]: """Executes the query synchronously on one API. Parameters: @@ -582,11 +572,11 @@ async def _get_all_async( self, endpoint: str, filter: str, - response_fields: Optional[List[str]] = None, + response_fields: Optional[list[str]] = None, sort: Optional[str] = None, page_limit: Optional[int] = None, paginate: bool = True, - ) -> Dict[str, QueryResults]: + ) -> dict[str, QueryResults]: """Executes the query asynchronously across all defined APIs. Parameters: @@ -626,10 +616,10 @@ def _get_all( endpoint: str, filter: str, page_limit: Optional[int] = None, - response_fields: Optional[List[str]] = None, + response_fields: Optional[list[str]] = None, sort: Optional[str] = None, paginate: bool = True, - ) -> Dict[str, QueryResults]: + ) -> dict[str, QueryResults]: """Executes the query synchronously across all defined APIs. Parameters: @@ -670,11 +660,11 @@ async def get_one_async( endpoint: str, filter: str, base_url: str, - response_fields: Optional[List[str]] = None, + response_fields: Optional[list[str]] = None, sort: Optional[str] = None, page_limit: Optional[int] = None, paginate: bool = True, - ) -> Dict[str, QueryResults]: + ) -> dict[str, QueryResults]: """Executes the query asynchronously on one API. !!! note @@ -725,11 +715,11 @@ async def _get_one_async( endpoint: str, filter: str, base_url: str, - response_fields: Optional[List[str]] = None, + response_fields: Optional[list[str]] = None, sort: Optional[str] = None, page_limit: Optional[int] = None, paginate: bool = True, - ) -> Dict[str, QueryResults]: + ) -> dict[str, QueryResults]: """See [`OptimadeClient.get_one_async`][optimade.client.OptimadeClient.get_one_async].""" next_url, _task = self._setup( endpoint=endpoint, @@ -785,9 +775,9 @@ def _get_one( base_url: str, sort: Optional[str] = None, page_limit: Optional[int] = None, - response_fields: Optional[List[str]] = None, + response_fields: Optional[list[str]] = None, paginate: bool = True, - ) -> Dict[str, QueryResults]: + ) -> dict[str, QueryResults]: """See [`OptimadeClient.get_one`][optimade.client.OptimadeClient.get_one].""" next_url, _task = self._setup( endpoint=endpoint, @@ -846,9 +836,9 @@ def _setup( base_url: str, filter: str, page_limit: Optional[int], - response_fields: Optional[List[str]], + response_fields: Optional[list[str]], sort: Optional[str], - ) -> Tuple[str, TaskID]: + ) -> tuple[str, TaskID]: """Constructs the first query URL and creates the progress bar task. Returns: @@ -876,7 +866,7 @@ def _build_url( endpoint: Optional[str] = "structures", version: Optional[str] = None, filter: Optional[str] = None, - response_fields: Optional[List[str]] = None, + response_fields: Optional[list[str]] = None, sort: Optional[str] = None, page_limit: Optional[int] = None, ) -> str: @@ -957,7 +947,7 @@ def _check_filter(self, filter: str, endpoint: str) -> None: def _handle_response( self, response: Union[httpx.Response, requests.Response], _task: TaskID - ) -> Tuple[Dict[str, Any], str]: + ) -> tuple[dict[str, Any], str]: """Handle the response from the server. Parameters: @@ -1036,8 +1026,8 @@ def _teardown(self, _task: TaskID, num_results: int) -> None: ) def _execute_callbacks( - self, results: Dict, response: Union[httpx.Response, requests.Response] - ) -> Union[None, Dict]: + self, results: dict, response: Union[httpx.Response, requests.Response] + ) -> Union[None, dict]: """Execute any callbacks registered with the client. Parameters: diff --git a/optimade/client/utils.py b/optimade/client/utils.py index bfa139b9d..6aa9f3d43 100644 --- a/optimade/client/utils.py +++ b/optimade/client/utils.py @@ -1,7 +1,7 @@ import sys from contextlib import contextmanager from dataclasses import asdict, dataclass, field -from typing import Dict, List, Set, Union +from typing import Union from rich.console import Console from rich.progress import ( @@ -34,22 +34,22 @@ class TooManyRequestsException(RecoverableHTTPError): class QueryResults: """A container dataclass for the results from a given query.""" - data: Union[Dict, List[Dict]] = field(default_factory=list, init=False) # type: ignore[assignment] - errors: List[str] = field(default_factory=list, init=False) - links: Dict = field(default_factory=dict, init=False) - included: List[Dict] = field(default_factory=list, init=False) - meta: Dict = field(default_factory=dict, init=False) + data: Union[dict, list[dict]] = field(default_factory=list, init=False) # type: ignore[assignment] + errors: list[str] = field(default_factory=list, init=False) + links: dict = field(default_factory=dict, init=False) + included: list[dict] = field(default_factory=list, init=False) + meta: dict = field(default_factory=dict, init=False) @property - def included_index(self) -> Set[str]: + def included_index(self) -> set[str]: if not getattr(self, "_included_index", None): - self._included_index: Set[str] = set() + self._included_index: set[str] = set() return self._included_index - def dict(self): + def asdict(self): return asdict(self) - def update(self, page_results: Dict) -> None: + def update(self, page_results: dict) -> None: """Combine the results from one page with the existing results for a given query. Parameters: diff --git a/optimade/exceptions.py b/optimade/exceptions.py index 2a75cfd5b..4274c1c8d 100644 --- a/optimade/exceptions.py +++ b/optimade/exceptions.py @@ -1,5 +1,5 @@ from abc import ABC -from typing import Any, Dict, Optional, Tuple, Type +from typing import Any, Optional __all__ = ( "OptimadeHTTPException", @@ -34,7 +34,7 @@ class OptimadeHTTPException(Exception, ABC): status_code: int title: str detail: Optional[str] = None - headers: Optional[Dict[str, Any]] = None + headers: Optional[dict[str, Any]] = None def __init__( self, detail: Optional[str] = None, headers: Optional[dict] = None @@ -104,7 +104,7 @@ class NotImplementedResponse(OptimadeHTTPException): """A tuple of the possible errors that can be returned by an OPTIMADE API.""" -POSSIBLE_ERRORS: Tuple[Type[OptimadeHTTPException], ...] = ( +POSSIBLE_ERRORS: tuple[type[OptimadeHTTPException], ...] = ( BadRequest, Forbidden, NotFound, diff --git a/optimade/filterparser/lark_parser.py b/optimade/filterparser/lark_parser.py index 01d5044cd..67d159916 100644 --- a/optimade/filterparser/lark_parser.py +++ b/optimade/filterparser/lark_parser.py @@ -5,7 +5,7 @@ """ from pathlib import Path -from typing import Dict, Optional, Tuple +from typing import Optional from lark import Lark, Tree @@ -20,7 +20,7 @@ class ParserError(Exception): """ -def get_versions() -> Dict[Tuple[int, int, int], Dict[str, Path]]: +def get_versions() -> dict[tuple[int, int, int], dict[str, Path]]: """Find grammar files within this package's grammar directory, returning a dictionary broken down by scraped grammar version (major, minor, patch) and variant (a string tag). @@ -29,10 +29,10 @@ def get_versions() -> Dict[Tuple[int, int, int], Dict[str, Path]]: A mapping from version, variant to grammar file name. """ - dct: Dict[Tuple[int, int, int], Dict[str, Path]] = {} + dct: dict[tuple[int, int, int], dict[str, Path]] = {} for filename in Path(__file__).parent.joinpath("../grammar").glob("*.lark"): tags = filename.stem.lstrip("v").split(".") - version: Tuple[int, int, int] = (int(tags[0]), int(tags[1]), int(tags[2])) + version: tuple[int, int, int] = (int(tags[0]), int(tags[1]), int(tags[2])) variant: str = "default" if len(tags) == 3 else str(tags[-1]) if version not in dct: dct[version] = {} @@ -50,7 +50,7 @@ class LarkParser: """ def __init__( - self, version: Optional[Tuple[int, int, int]] = None, variant: str = "default" + self, version: Optional[tuple[int, int, int]] = None, variant: str = "default" ): """For a given version and variant, try to load the corresponding grammar. diff --git a/optimade/filtertransformers/base_transformer.py b/optimade/filtertransformers/base_transformer.py index 2bb89086c..9e9928be0 100644 --- a/optimade/filtertransformers/base_transformer.py +++ b/optimade/filtertransformers/base_transformer.py @@ -7,7 +7,7 @@ import abc import warnings -from typing import Any, Dict, Optional, Type +from typing import Any, Optional from lark import Transformer, Tree, v_args @@ -79,8 +79,8 @@ class BaseTransformer(Transformer, abc.ABC): """ - mapper: Optional[Type[BaseResourceMapper]] = None - operator_map: Dict[str, Optional[str]] = { + mapper: Optional[type[BaseResourceMapper]] = None + operator_map: dict[str, Optional[str]] = { "<": None, "<=": None, ">": None, @@ -100,11 +100,11 @@ class BaseTransformer(Transformer, abc.ABC): "!=": "!=", } - _quantity_type: Type[Quantity] = Quantity + _quantity_type: type[Quantity] = Quantity _quantities = None def __init__( - self, mapper: Optional[Type[BaseResourceMapper]] = None + self, mapper: Optional[type[BaseResourceMapper]] = None ): # pylint: disable=super-init-not-called """Initialise the transformer object, optionally loading in a resource mapper for use when post-processing. @@ -113,7 +113,7 @@ def __init__( self.mapper = mapper @property - def backend_mapping(self) -> Dict[str, Quantity]: + def backend_mapping(self) -> dict[str, Quantity]: """A mapping between backend field names (aliases) and the corresponding [`Quantity`][optimade.filtertransformers.base_transformer.Quantity] object. """ @@ -122,7 +122,7 @@ def backend_mapping(self) -> Dict[str, Quantity]: } @property - def quantities(self) -> Dict[str, Quantity]: + def quantities(self) -> dict[str, Quantity]: """A mapping from the OPTIMADE field name to the corresponding [`Quantity`][optimade.filtertransformers.base_transformer.Quantity] objects. """ @@ -132,10 +132,10 @@ def quantities(self) -> Dict[str, Quantity]: return self._quantities @quantities.setter - def quantities(self, quantities: Dict[str, Quantity]) -> None: + def quantities(self, quantities: dict[str, Quantity]) -> None: self._quantities = quantities - def _build_quantities(self) -> Dict[str, Quantity]: + def _build_quantities(self) -> dict[str, Quantity]: """Creates a dictionary of field names mapped to [`Quantity`][optimade.filtertransformers.base_transformer.Quantity] objects from the fields registered by the mapper. diff --git a/optimade/filtertransformers/elasticsearch.py b/optimade/filtertransformers/elasticsearch.py index 195e77748..54d83d0fe 100644 --- a/optimade/filtertransformers/elasticsearch.py +++ b/optimade/filtertransformers/elasticsearch.py @@ -1,4 +1,4 @@ -from typing import Dict, Optional, Type, Union +from typing import Optional, Union from elasticsearch_dsl import Field, Integer, Keyword, Q, Text from lark import v_args @@ -97,12 +97,12 @@ class ElasticTransformer(BaseTransformer): ">=": "gte", } - _quantity_type: Type[ElasticsearchQuantity] = ElasticsearchQuantity + _quantity_type: type[ElasticsearchQuantity] = ElasticsearchQuantity def __init__( self, - mapper: Type[BaseResourceMapper], - quantities: Optional[Dict[str, Quantity]] = None, + mapper: type[BaseResourceMapper], + quantities: Optional[dict[str, Quantity]] = None, ): if quantities is not None: self.quantities = quantities @@ -143,7 +143,7 @@ def _field( return quantity if nested is not None: - return "%s.%s" % (nested.backend_field, quantity.name) # type: ignore[union-attr] + return f"{nested.backend_field}.{quantity.name}" # type: ignore[union-attr] return quantity.backend_field # type: ignore[union-attr, return-value] diff --git a/optimade/filtertransformers/mongo.py b/optimade/filtertransformers/mongo.py index 862e21f5e..1b9ec8324 100755 --- a/optimade/filtertransformers/mongo.py +++ b/optimade/filtertransformers/mongo.py @@ -7,7 +7,7 @@ import copy import itertools import warnings -from typing import Any, Dict, List, Union +from typing import Any, Union from lark import Token, v_args @@ -56,7 +56,7 @@ class MongoTransformer(BaseTransformer): "$nin": "$in", } - def postprocess(self, query: Dict[str, Any]): + def postprocess(self, query: dict[str, Any]): """Used to post-process the nested dictionary of the parsed query.""" query = self._apply_relationship_filtering(query) query = self._apply_length_operators(query) @@ -229,7 +229,7 @@ def property_zip_addon(self, arg): # property_zip_addon: ":" property (":" property)* raise NotImplementedError("Correlated list queries are not supported.") - def _recursive_expression_phrase(self, arg: List) -> Dict[str, Any]: + def _recursive_expression_phrase(self, arg: list) -> dict[str, Any]: """Helper function for parsing `expression_phrase`. Recursively sorts out the correct precedence for `$not`, `$and` and `$or`. @@ -242,7 +242,7 @@ def _recursive_expression_phrase(self, arg: List) -> Dict[str, Any]: """ - def handle_not_and(arg: Dict[str, List]) -> Dict[str, List]: + def handle_not_and(arg: dict[str, list]) -> dict[str, list]: """Handle the case of `~(A & B) -> (~A | ~B)`. We have to check for the special case in which the "and" was created @@ -271,7 +271,7 @@ def handle_not_and(arg: Dict[str, List]) -> Dict[str, List]: ] } - def handle_not_or(arg: Dict[str, List]) -> Dict[str, List]: + def handle_not_or(arg: dict[str, list]) -> dict[str, list]: """Handle the case of ~(A | B) -> (~A & ~B). !!! note @@ -568,7 +568,7 @@ def replace_str_date_with_datetime(subdict, prop, expr): ) -def recursive_postprocessing(filter_: Union[Dict, List], condition, replacement): +def recursive_postprocessing(filter_: Union[dict, list], condition, replacement): """Recursively descend into the query, checking each dictionary (contained in a list, or as an entry in another dictionary) for the condition passed. If the condition is true, apply the diff --git a/optimade/models/baseinfo.py b/optimade/models/baseinfo.py index 3374b6a02..36c47695e 100644 --- a/optimade/models/baseinfo.py +++ b/optimade/models/baseinfo.py @@ -1,6 +1,6 @@ # pylint: disable=no-self-argument,no-name-in-module import re -from typing import Dict, List, Optional +from typing import Optional from pydantic import AnyHttpUrl, BaseModel, Field, root_validator, validator @@ -64,18 +64,18 @@ class BaseInfoAttributes(BaseModel): The version number string MUST NOT be prefixed by, e.g., "v". Examples: `1.0.0`, `1.0.0-rc.2`.""", ) - available_api_versions: List[AvailableApiVersion] = StrictField( + available_api_versions: list[AvailableApiVersion] = StrictField( ..., description="A list of dictionaries of available API versions at other base URLs", ) - formats: List[str] = StrictField( + formats: list[str] = StrictField( default=["json"], description="List of available output formats." ) - available_endpoints: List[str] = StrictField( + available_endpoints: list[str] = StrictField( ..., description="List of available endpoints (i.e., the string to be appended to the versioned base URL).", ) - entry_types_by_format: Dict[str, List[str]] = StrictField( + entry_types_by_format: dict[str, list[str]] = StrictField( ..., description="Available entry endpoints as a function of output formats." ) is_index: Optional[bool] = StrictField( diff --git a/optimade/models/entries.py b/optimade/models/entries.py index 7850565ea..4bd10a29e 100644 --- a/optimade/models/entries.py +++ b/optimade/models/entries.py @@ -1,6 +1,6 @@ # pylint: disable=line-too-long,no-self-argument from datetime import datetime -from typing import Dict, List, Optional +from typing import Optional from pydantic import BaseModel, validator # pylint: disable=no-name-in-module @@ -184,18 +184,18 @@ class EntryInfoProperty(BaseModel): class EntryInfoResource(BaseModel): - formats: List[str] = StrictField( + formats: list[str] = StrictField( ..., description="List of output formats available for this type of entry." ) description: str = StrictField(..., description="Description of the entry.") - properties: Dict[str, EntryInfoProperty] = StrictField( + properties: dict[str, EntryInfoProperty] = StrictField( ..., description="A dictionary describing queryable properties for this entry type, where each key is a property name.", ) - output_fields_by_format: Dict[str, List[str]] = StrictField( + output_fields_by_format: dict[str, list[str]] = StrictField( ..., description="Dictionary of available output fields for this entry type, where the keys are the values of the `formats` list and the values are the keys of the `properties` dictionary.", ) diff --git a/optimade/models/index_metadb.py b/optimade/models/index_metadb.py index 7c48d666c..e2130bf9a 100644 --- a/optimade/models/index_metadb.py +++ b/optimade/models/index_metadb.py @@ -1,6 +1,6 @@ # pylint: disable=no-self-argument from enum import Enum -from typing import Dict, Union +from typing import Union from pydantic import BaseModel, Field # pylint: disable=no-name-in-module @@ -52,7 +52,7 @@ class IndexInfoResource(BaseInfoResource): attributes: IndexInfoAttributes = Field(...) relationships: Union[ - None, Dict[DefaultRelationship, IndexRelationship] + None, dict[DefaultRelationship, IndexRelationship] ] = StrictField( # type: ignore[assignment] ..., title="Relationships", diff --git a/optimade/models/jsonapi.py b/optimade/models/jsonapi.py index 23e0db241..a9022dc3b 100644 --- a/optimade/models/jsonapi.py +++ b/optimade/models/jsonapi.py @@ -1,7 +1,7 @@ """This module should reproduce JSON API v1.0 https://jsonapi.org/format/1.0/""" # pylint: disable=no-self-argument from datetime import datetime, timezone -from typing import Any, Dict, List, Optional, Type, Union +from typing import Any, Optional, Union from pydantic import ( # pylint: disable=no-name-in-module AnyUrl, @@ -168,7 +168,7 @@ class BaseResource(BaseModel): class Config: @staticmethod - def schema_extra(schema: Dict[str, Any], model: Type["BaseResource"]) -> None: + def schema_extra(schema: dict[str, Any], model: type["BaseResource"]) -> None: """Ensure `id` and `type` are the first two entries in the list required properties. Note: @@ -227,7 +227,7 @@ class Relationship(BaseModel): None, description="a links object containing at least one of the following: self, related", ) - data: Optional[Union[BaseResource, List[BaseResource]]] = StrictField( + data: Optional[Union[BaseResource, list[BaseResource]]] = StrictField( None, description="Resource linkage" ) meta: Optional[Meta] = StrictField( @@ -323,17 +323,17 @@ class Resource(BaseResource): class Response(BaseModel): """A top-level response""" - data: Optional[Union[None, Resource, List[Resource]]] = StrictField( + data: Optional[Union[None, Resource, list[Resource]]] = StrictField( None, description="Outputted Data", uniqueItems=True ) meta: Optional[Meta] = StrictField( None, description="A meta object containing non-standard information related to the Success", ) - errors: Optional[List[Error]] = StrictField( + errors: Optional[list[Error]] = StrictField( None, description="A list of unique errors", uniqueItems=True ) - included: Optional[List[Resource]] = StrictField( + included: Optional[list[Resource]] = StrictField( None, description="A list of unique included resources", uniqueItems=True ) links: Optional[ToplevelLinks] = StrictField( diff --git a/optimade/models/optimade_json.py b/optimade/models/optimade_json.py index bad738057..2af6ab53a 100644 --- a/optimade/models/optimade_json.py +++ b/optimade/models/optimade_json.py @@ -2,7 +2,7 @@ # pylint: disable=no-self-argument,no-name-in-module from datetime import datetime from enum import Enum -from typing import Any, Dict, List, Optional, Type, Union +from typing import Any, Optional, Union from pydantic import AnyHttpUrl, AnyUrl, BaseModel, EmailStr, root_validator @@ -43,7 +43,7 @@ class DataType(Enum): @classmethod def get_values(cls): """Get OPTIMADE data types (enum values) as a (sorted) list""" - return sorted((_.value for _ in cls)) + return sorted(_.value for _ in cls) @classmethod def from_python_type(cls, python_type: Union[type, str, object]): @@ -156,7 +156,7 @@ def status_must_not_be_specified(cls, values): class Config: @staticmethod - def schema_extra(schema: Dict[str, Any], model: Type["Warnings"]) -> None: + def schema_extra(schema: dict[str, Any], model: type["Warnings"]) -> None: """Update OpenAPI JSON schema model for `Warning`. * Ensure `type` is in the list required properties and in the correct place. @@ -317,7 +317,7 @@ class ResponseMeta(jsonapi.Meta): None, description="a dictionary describing the server implementation" ) - warnings: Optional[List[Warnings]] = StrictField( + warnings: Optional[list[Warnings]] = StrictField( None, description="""A list of warning resource objects representing non-critical errors or warnings. A warning resource object is defined similarly to a [JSON API error object](http://jsonapi.org/format/1.0/#error-objects), but MUST also include the field `type`, which MUST have the value `"warning"`. @@ -372,5 +372,5 @@ class Relationship(jsonapi.Relationship): """Similar to normal JSON API relationship, but with addition of OPTIONAL meta field for a resource.""" data: Optional[ - Union[BaseRelationshipResource, List[BaseRelationshipResource]] + Union[BaseRelationshipResource, list[BaseRelationshipResource]] ] = StrictField(None, description="Resource linkage", uniqueItems=True) diff --git a/optimade/models/references.py b/optimade/models/references.py index afdd2f48f..bde2d538a 100644 --- a/optimade/models/references.py +++ b/optimade/models/references.py @@ -1,5 +1,5 @@ # pylint: disable=line-too-long,no-self-argument -from typing import List, Optional +from typing import Optional from pydantic import AnyUrl, BaseModel, validator # pylint: disable=no-name-in-module @@ -42,14 +42,14 @@ class ReferenceResourceAttributes(EntryResourceAttributes): """ - authors: Optional[List[Person]] = OptimadeField( + authors: Optional[list[Person]] = OptimadeField( None, description="List of person objects containing the authors of the reference.", support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL, ) - editors: Optional[List[Person]] = OptimadeField( + editors: Optional[list[Person]] = OptimadeField( None, description="List of person objects containing the editors of the reference.", support=SupportLevel.OPTIONAL, diff --git a/optimade/models/responses.py b/optimade/models/responses.py index 01845dc82..c883a0093 100644 --- a/optimade/models/responses.py +++ b/optimade/models/responses.py @@ -1,5 +1,5 @@ # pylint: disable=no-self-argument -from typing import Any, Dict, List, Optional, Union +from typing import Any, Optional, Union from pydantic import Field, root_validator @@ -34,7 +34,7 @@ class ErrorResponse(Response): meta: ResponseMeta = StrictField( ..., description="A meta object containing non-standard information." ) - errors: List[OptimadeError] = StrictField( + errors: list[OptimadeError] = StrictField( ..., description="A list of OPTIMADE-specific JSON API error objects, where the field detail MUST be present.", uniqueItems=True, @@ -66,23 +66,23 @@ class InfoResponse(Success): class EntryResponseOne(Success): - data: Union[EntryResource, Dict[str, Any], None] = Field(...) # type: ignore[assignment] - included: Optional[Union[List[EntryResource], List[Dict[str, Any]]]] = Field( # type: ignore[assignment] + data: Union[EntryResource, dict[str, Any], None] = Field(...) # type: ignore[assignment] + included: Optional[Union[list[EntryResource], list[dict[str, Any]]]] = Field( # type: ignore[assignment] None, uniqueItems=True ) class EntryResponseMany(Success): - data: Union[List[EntryResource], List[Dict[str, Any]]] = Field( # type: ignore[assignment] + data: Union[list[EntryResource], list[dict[str, Any]]] = Field( # type: ignore[assignment] ..., uniqueItems=True ) - included: Optional[Union[List[EntryResource], List[Dict[str, Any]]]] = Field( # type: ignore[assignment] + included: Optional[Union[list[EntryResource], list[dict[str, Any]]]] = Field( # type: ignore[assignment] None, uniqueItems=True ) class LinksResponse(EntryResponseMany): - data: Union[List[LinksResource], List[Dict[str, Any]]] = StrictField( + data: Union[list[LinksResource], list[dict[str, Any]]] = StrictField( ..., description="List of unique OPTIMADE links resource objects.", uniqueItems=True, @@ -90,13 +90,13 @@ class LinksResponse(EntryResponseMany): class StructureResponseOne(EntryResponseOne): - data: Union[StructureResource, Dict[str, Any], None] = StrictField( + data: Union[StructureResource, dict[str, Any], None] = StrictField( ..., description="A single structures entry resource." ) class StructureResponseMany(EntryResponseMany): - data: Union[List[StructureResource], List[Dict[str, Any]]] = StrictField( + data: Union[list[StructureResource], list[dict[str, Any]]] = StrictField( ..., description="List of unique OPTIMADE structures entry resource objects.", uniqueItems=True, @@ -104,13 +104,13 @@ class StructureResponseMany(EntryResponseMany): class ReferenceResponseOne(EntryResponseOne): - data: Union[ReferenceResource, Dict[str, Any], None] = StrictField( + data: Union[ReferenceResource, dict[str, Any], None] = StrictField( ..., description="A single references entry resource." ) class ReferenceResponseMany(EntryResponseMany): - data: Union[List[ReferenceResource], List[Dict[str, Any]]] = StrictField( + data: Union[list[ReferenceResource], list[dict[str, Any]]] = StrictField( ..., description="List of unique OPTIMADE references entry resource objects.", uniqueItems=True, diff --git a/optimade/models/structures.py b/optimade/models/structures.py index aa89afe69..565c02362 100644 --- a/optimade/models/structures.py +++ b/optimade/models/structures.py @@ -2,7 +2,7 @@ import re import warnings from enum import Enum, IntEnum -from typing import List, Optional, Union +from typing import Optional, Union from pydantic import BaseModel, conlist, root_validator, validator @@ -84,7 +84,7 @@ class Species(BaseModel): queryable=SupportLevel.OPTIONAL, ) - chemical_symbols: List[str] = OptimadeField( + chemical_symbols: list[str] = OptimadeField( ..., description="""MUST be a list of strings of all chemical elements composing this species. Each item of the list MUST be one of the following: @@ -97,7 +97,7 @@ class Species(BaseModel): queryable=SupportLevel.OPTIONAL, ) - concentration: List[float] = OptimadeField( + concentration: list[float] = OptimadeField( ..., description="""MUST be a list of floats, with same length as `chemical_symbols`. The numbers represent the relative concentration of the corresponding chemical symbol in this species. The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories: @@ -109,7 +109,7 @@ class Species(BaseModel): queryable=SupportLevel.OPTIONAL, ) - mass: Optional[List[float]] = OptimadeField( + mass: Optional[list[float]] = OptimadeField( None, description="""If present MUST be a list of floats expressed in a.m.u. Elements denoting vacancies MUST have masses equal to 0.""", @@ -127,14 +127,14 @@ class Species(BaseModel): queryable=SupportLevel.OPTIONAL, ) - attached: Optional[List[str]] = OptimadeField( + attached: Optional[list[str]] = OptimadeField( None, description="""If provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or "X" for a non-chemical element.""", support=SupportLevel.OPTIONAL, queryable=SupportLevel.OPTIONAL, ) - nattached: Optional[List[int]] = OptimadeField( + nattached: Optional[list[int]] = OptimadeField( None, description="""If provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the :field:`attached` key.""", support=SupportLevel.OPTIONAL, @@ -210,7 +210,7 @@ class Assembly(BaseModel): """ - sites_in_groups: List[List[int]] = OptimadeField( + sites_in_groups: list[list[int]] = OptimadeField( ..., description="""Index of the sites (0-based) that belong to each group for each assembly. @@ -221,7 +221,7 @@ class Assembly(BaseModel): queryable=SupportLevel.OPTIONAL, ) - group_probabilities: List[float] = OptimadeField( + group_probabilities: list[float] = OptimadeField( ..., description="""Statistical probability of each group. It MUST have the same length as `sites_in_groups`. It SHOULD sum to one. @@ -263,7 +263,7 @@ def check_self_consistency(cls, v, values): class StructureResourceAttributes(EntryResourceAttributes): """This class contains the Field for the attributes used to represent a structure, e.g. unit cell, atoms, positions.""" - elements: Optional[List[str]] = OptimadeField( + elements: Optional[list[str]] = OptimadeField( ..., description="""The chemical symbols of the different elements present in the structure. @@ -311,7 +311,7 @@ class StructureResourceAttributes(EntryResourceAttributes): queryable=SupportLevel.MUST, ) - elements_ratios: Optional[List[float]] = OptimadeField( + elements_ratios: Optional[list[float]] = OptimadeField( ..., description="""Relative proportions of different elements in the structure. @@ -523,7 +523,7 @@ class StructureResourceAttributes(EntryResourceAttributes): queryable=SupportLevel.OPTIONAL, ) - cartesian_site_positions: Optional[List[Vector3D]] = OptimadeField( # type: ignore[valid-type] + cartesian_site_positions: Optional[list[Vector3D]] = OptimadeField( # type: ignore[valid-type] ..., description="""Cartesian positions of each site in the structure. A site is usually used to describe positions of atoms; what atoms can be encountered at a given site is conveyed by the `species_at_sites` property, and the species themselves are described in the `species` property. @@ -564,7 +564,7 @@ class StructureResourceAttributes(EntryResourceAttributes): support=SupportLevel.SHOULD, ) - species: Optional[List[Species]] = OptimadeField( + species: Optional[list[Species]] = OptimadeField( ..., description="""A list describing the species of the sites of this structure. Species can represent pure chemical elements, virtual-crystal atoms representing a statistical occupation of a given site by multiple chemical elements, and/or a location to which there are attached atoms, i.e., atoms whose precise location are unknown beyond that they are attached to that position (frequently used to indicate hydrogen atoms attached to another element, e.g., a carbon with three attached hydrogens might represent a methyl group, -CH3). @@ -633,7 +633,7 @@ class StructureResourceAttributes(EntryResourceAttributes): queryable=SupportLevel.OPTIONAL, ) - species_at_sites: Optional[List[str]] = OptimadeField( + species_at_sites: Optional[list[str]] = OptimadeField( ..., description="""Name of the species at each site (where values for sites are specified with the same order of the property `cartesian_site_positions`). The properties of the species are found in the property `species`. @@ -657,7 +657,7 @@ class StructureResourceAttributes(EntryResourceAttributes): queryable=SupportLevel.OPTIONAL, ) - assemblies: Optional[List[Assembly]] = OptimadeField( + assemblies: Optional[list[Assembly]] = OptimadeField( None, description="""A description of groups of sites that are statistically correlated. @@ -765,7 +765,7 @@ class StructureResourceAttributes(EntryResourceAttributes): queryable=SupportLevel.OPTIONAL, ) - structure_features: List[StructureFeatures] = OptimadeField( + structure_features: list[StructureFeatures] = OptimadeField( ..., title="Structure Features", description="""A list of strings that flag which special features are used by the structure. @@ -961,7 +961,7 @@ def null_values_for_whole_vector(cls, v): return v for vector in v: - if None in vector and any((isinstance(_, float) for _ in vector)): + if None in vector and any(isinstance(_, float) for _ in vector): raise ValueError( f"A lattice vector MUST be either all `null` or all numbers (vector: {vector}, all vectors: {v})" ) @@ -1020,7 +1020,7 @@ def validate_species(cls, v): @validator("structure_features", always=True) def validate_structure_features(cls, v, values): - if [StructureFeatures(value) for value in sorted((_.value for _ in v))] != v: + if [StructureFeatures(value) for value in sorted(_.value for _ in v)] != v: raise ValueError( f"structure_features MUST be sorted alphabetically, given value: {v}" ) diff --git a/optimade/models/utils.py b/optimade/models/utils.py index 7e04bac07..99a0c04c6 100644 --- a/optimade/models/utils.py +++ b/optimade/models/utils.py @@ -5,7 +5,7 @@ import warnings from enum import Enum from functools import reduce -from typing import TYPE_CHECKING, List, Optional, Union +from typing import TYPE_CHECKING, Optional, Union from pydantic import Field from pydantic.fields import FieldInfo @@ -237,7 +237,7 @@ def _reduce_or_anonymize_formula( import re import sys - numbers: List[int] = [ + numbers: list[int] = [ int(n.strip() or 1) for n in re.split(r"[A-Z][a-z]*", formula)[1:] ] # Need to remove leading 1 from split and convert to ints diff --git a/optimade/server/config.py b/optimade/server/config.py index 3a3e7cea7..4dc360f73 100644 --- a/optimade/server/config.py +++ b/optimade/server/config.py @@ -2,7 +2,7 @@ import warnings from enum import Enum from pathlib import Path -from typing import Any, Dict, List, Literal, Optional, Tuple, Union +from typing import Any, Literal, Optional, Union from pydantic import ( # pylint: disable=no-name-in-module AnyHttpUrl, @@ -67,7 +67,7 @@ class SupportedBackend(Enum): MONGOMOCK = "mongomock" -def config_file_settings(settings: BaseSettings) -> Dict[str, Any]: +def config_file_settings(settings: BaseSettings) -> dict[str, Any]: """Configuration file settings source. Based on the example in the @@ -153,7 +153,7 @@ class ServerConfig(BaseSettings): description="Which database backend to use out of the supported backends.", ) - elastic_hosts: Optional[Union[str, List[str], Dict, List[Dict]]] = Field( + elastic_hosts: Optional[Union[str, list[str], dict, list[dict]]] = Field( None, description="Host settings to pass through to the `Elasticsearch` class." ) @@ -224,9 +224,9 @@ class ServerConfig(BaseSettings): ), description="General information about the provider of this OPTIMADE implementation", ) - provider_fields: Dict[ + provider_fields: dict[ Literal["links", "references", "structures"], - List[Union[str, Dict[Literal["name", "type", "unit", "description"], str]]], + list[Union[str, dict[Literal["name", "type", "unit", "description"], str]]], ] = Field( {}, description=( @@ -234,15 +234,15 @@ class ServerConfig(BaseSettings): "broken down by endpoint." ), ) - aliases: Dict[Literal["links", "references", "structures"], Dict[str, str]] = Field( + aliases: dict[Literal["links", "references", "structures"], dict[str, str]] = Field( {}, description=( "A mapping between field names in the database with their corresponding OPTIMADE field" " names, broken down by endpoint." ), ) - length_aliases: Dict[ - Literal["links", "references", "structures"], Dict[str, str] + length_aliases: dict[ + Literal["links", "references", "structures"], dict[str, str] ] = Field( {}, description=( @@ -354,7 +354,7 @@ def customise_sources( init_settings: SettingsSourceCallable, env_settings: SettingsSourceCallable, file_secret_settings: SettingsSourceCallable, - ) -> Tuple[SettingsSourceCallable, ...]: + ) -> tuple[SettingsSourceCallable, ...]: """ **Priority of config settings sources**: diff --git a/optimade/server/entry_collections/elasticsearch.py b/optimade/server/entry_collections/elasticsearch.py index d14ad6499..7b0fffc7e 100644 --- a/optimade/server/entry_collections/elasticsearch.py +++ b/optimade/server/entry_collections/elasticsearch.py @@ -1,6 +1,7 @@ import json +from collections.abc import Iterable from pathlib import Path -from typing import Any, Dict, Iterable, List, Optional, Tuple, Type +from typing import Any, Optional from optimade.filtertransformers.elasticsearch import ElasticTransformer from optimade.models import EntryResource @@ -24,8 +25,8 @@ class ElasticCollection(EntryCollection): def __init__( self, name: str, - resource_cls: Type[EntryResource], - resource_mapper: Type[BaseResourceMapper], + resource_cls: type[EntryResource], + resource_mapper: type[BaseResourceMapper], client: Optional["Elasticsearch"] = None, ): """Initialize the ElasticCollection for the given parameters. @@ -78,7 +79,7 @@ def create_optimade_index(self) -> None: LOGGER.debug(f"Created Elastic index for {self.name!r} with parameters {body}") @property - def predefined_index(self) -> Dict[str, Any]: + def predefined_index(self) -> dict[str, Any]: """Loads and returns the default pre-defined index.""" with open(Path(__file__).parent.joinpath("elastic_indexes.json")) as f: index = json.load(f) @@ -86,8 +87,8 @@ def predefined_index(self) -> Dict[str, Any]: @staticmethod def create_elastic_index_from_mapper( - resource_mapper: Type[BaseResourceMapper], fields: Iterable[str] - ) -> Dict[str, Any]: + resource_mapper: type[BaseResourceMapper], fields: Iterable[str] + ) -> dict[str, Any]: """Create a fallback elastic index based on a resource mapper. Arguments: @@ -110,7 +111,7 @@ def __len__(self): """Returns the total number of entries in the collection.""" return Search(using=self.client, index=self.name).execute().hits.total.value - def insert(self, data: List[EntryResource]) -> None: + def insert(self, data: list[EntryResource]) -> None: """Add the given entries to the underlying database. Warning: @@ -123,7 +124,7 @@ def insert(self, data: List[EntryResource]) -> None: def get_id(item): if self.name == "links": - id_ = "%s-%s" % (item["id"], item["type"]) + id_ = f"{item['id']}-{item['type']}" elif "id" in item: id_ = item["id"] elif "_id" in item: @@ -148,8 +149,8 @@ def get_id(item): ) def _run_db_query( - self, criteria: Dict[str, Any], single_entry=False - ) -> Tuple[List[Dict[str, Any]], int, bool]: + self, criteria: dict[str, Any], single_entry=False + ) -> tuple[list[dict[str, Any]], int, bool]: """Run the query on the backend and collect the results. Arguments: diff --git a/optimade/server/entry_collections/entry_collections.py b/optimade/server/entry_collections/entry_collections.py index d59bb8713..cbaf59209 100644 --- a/optimade/server/entry_collections/entry_collections.py +++ b/optimade/server/entry_collections/entry_collections.py @@ -2,7 +2,8 @@ import re import warnings from abc import ABC, abstractmethod -from typing import Any, Dict, Iterable, List, Optional, Set, Tuple, Type, Union +from collections.abc import Iterable +from typing import Any, Optional, Union from lark import Transformer @@ -21,8 +22,8 @@ def create_collection( name: str, - resource_cls: Type[EntryResource], - resource_mapper: Type[BaseResourceMapper], + resource_cls: type[EntryResource], + resource_mapper: type[BaseResourceMapper], ) -> "EntryCollection": """Create an `EntryCollection` of the configured type, depending on the value of `CONFIG.database_backend`. @@ -83,8 +84,8 @@ class EntryCollection(ABC): def __init__( self, - resource_cls: Type[EntryResource], - resource_mapper: Type[BaseResourceMapper], + resource_cls: type[EntryResource], + resource_mapper: type[BaseResourceMapper], transformer: Transformer, ): """Initialize the collection for the given parameters. @@ -110,14 +111,14 @@ def __init__( for field in CONFIG.provider_fields.get(resource_mapper.ENDPOINT, []) ] - self._all_fields: Set[str] = set() + self._all_fields: set[str] = set() @abstractmethod def __len__(self) -> int: """Returns the total number of entries in the collection.""" @abstractmethod - def insert(self, data: List[EntryResource]) -> None: + def insert(self, data: list[EntryResource]) -> None: """Add the given entries to the underlying database. Arguments: @@ -137,7 +138,7 @@ def count(self, **kwargs: Any) -> Union[int, None]: def find( self, params: Union[EntryListingQueryParams, SingleEntryQueryParams] - ) -> Tuple[Union[None, Dict, List[Dict]], Optional[int], bool, Set[str], Set[str],]: + ) -> tuple[Union[None, dict, list[dict]], Optional[int], bool, set[str], set[str],]: """ Fetches results and indicates if more data is available. @@ -195,7 +196,7 @@ def find( detail=f"Unrecognised OPTIMADE field(s) in requested `response_fields`: {bad_optimade_fields}." ) - results: Union[None, List[Dict], Dict] = None + results: Union[None, list[dict], dict] = None if raw_results: results = [self.resource_mapper.map_back(doc) for doc in raw_results] @@ -224,8 +225,8 @@ def find( @abstractmethod def _run_db_query( - self, criteria: Dict[str, Any], single_entry: bool = False - ) -> Tuple[List[Dict[str, Any]], Optional[int], bool]: + self, criteria: dict[str, Any], single_entry: bool = False + ) -> tuple[list[dict[str, Any]], Optional[int], bool]: """Run the query on the backend and collect the results. Arguments: @@ -239,7 +240,7 @@ def _run_db_query( """ @property - def all_fields(self) -> Set[str]: + def all_fields(self) -> set[str]: """Get the set of all fields handled in this collection, from attribute fields in the schema, provider fields and top-level OPTIMADE fields. @@ -266,7 +267,7 @@ def all_fields(self) -> Set[str]: return self._all_fields - def get_attribute_fields(self) -> Set[str]: + def get_attribute_fields(self) -> set[str]: """Get the set of attribute fields Return only the _first-level_ attribute fields from the schema of the resource class, @@ -298,7 +299,7 @@ def get_attribute_fields(self) -> Set[str]: def handle_query_params( self, params: Union[EntryListingQueryParams, SingleEntryQueryParams] - ) -> Dict[str, Any]: + ) -> dict[str, Any]: """Parse and interpret the backend-agnostic query parameter models into a dictionary that can be used by the specific backend. @@ -404,7 +405,7 @@ def handle_query_params( return cursor_kwargs - def parse_sort_params(self, sort_params: str) -> Iterable[Tuple[str, int]]: + def parse_sort_params(self, sort_params: str) -> Iterable[tuple[str, int]]: """Handles any sort parameters passed to the collection, resolving aliases and dealing with any invalid fields. @@ -416,7 +417,7 @@ def parse_sort_params(self, sort_params: str) -> Iterable[Tuple[str, int]]: sort direction encoded as 1 (ascending) or -1 (descending). """ - sort_spec: List[Tuple[str, int]] = [] + sort_spec: list[tuple[str, int]] = [] for field in sort_params.split(","): sort_dir = 1 if field.startswith("-"): @@ -464,8 +465,8 @@ def parse_sort_params(self, sort_params: str) -> Iterable[Tuple[str, int]]: def get_next_query_params( self, params: EntryListingQueryParams, - results: Union[None, Dict, List[Dict]], - ) -> Dict[str, List[str]]: + results: Union[None, dict, list[dict]], + ) -> dict[str, list[str]]: """Provides url query pagination parameters that will be used in the next link. @@ -477,7 +478,7 @@ def get_next_query_params( A dictionary with the necessary query parameters. """ - query: Dict[str, List[str]] = dict() + query: dict[str, list[str]] = dict() if isinstance(results, list) and results: # If a user passed a particular pagination mechanism, keep using it # Otherwise, use the default pagination mechanism of the collection diff --git a/optimade/server/entry_collections/mongo.py b/optimade/server/entry_collections/mongo.py index b7bd20f9b..3036aa729 100644 --- a/optimade/server/entry_collections/mongo.py +++ b/optimade/server/entry_collections/mongo.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Optional, Tuple, Type, Union +from typing import Any, Optional, Union from optimade.filtertransformers.mongo import MongoTransformer from optimade.models import EntryResource @@ -38,8 +38,8 @@ class MongoCollection(EntryCollection): def __init__( self, name: str, - resource_cls: Type[EntryResource], - resource_mapper: Type[BaseResourceMapper], + resource_cls: type[EntryResource], + resource_mapper: type[BaseResourceMapper], database: str = CONFIG.mongo_database, ): """Initialize the MongoCollection for the given parameters. @@ -91,7 +91,7 @@ def count(self, **kwargs: Any) -> Union[int, None]: except ExecutionTimeout: return None - def insert(self, data: List[EntryResource]) -> None: + def insert(self, data: list[EntryResource]) -> None: """Add the given entries to the underlying database. Warning: @@ -105,7 +105,7 @@ def insert(self, data: List[EntryResource]) -> None: def handle_query_params( self, params: Union[EntryListingQueryParams, SingleEntryQueryParams] - ) -> Dict[str, Any]: + ) -> dict[str, Any]: """Parse and interpret the backend-agnostic query parameter models into a dictionary that can be used by MongoDB. @@ -142,8 +142,8 @@ def handle_query_params( return criteria def _run_db_query( - self, criteria: Dict[str, Any], single_entry: bool = False - ) -> Tuple[List[Dict[str, Any]], Optional[int], bool]: + self, criteria: dict[str, Any], single_entry: bool = False + ) -> tuple[list[dict[str, Any]], Optional[int], bool]: """Run the query on the backend and collect the results. Arguments: diff --git a/optimade/server/exception_handlers.py b/optimade/server/exception_handlers.py index 06fc083a8..c79eaa637 100644 --- a/optimade/server/exception_handlers.py +++ b/optimade/server/exception_handlers.py @@ -1,5 +1,6 @@ import traceback -from typing import Callable, Iterable, List, Optional, Tuple, Type, Union +from collections.abc import Iterable +from typing import Callable, Optional, Union from fastapi import Request from fastapi.encoders import jsonable_encoder @@ -18,7 +19,7 @@ def general_exception( request: Request, exc: Exception, status_code: int = 500, # A status_code in `exc` will take precedence - errors: Optional[List[OptimadeError]] = None, + errors: Optional[list[OptimadeError]] = None, ) -> JSONAPIResponse: """Handle an exception @@ -221,8 +222,8 @@ def general_exception_handler(request: Request, exc: Exception) -> JSONAPIRespon OPTIMADE_EXCEPTIONS: Iterable[ - Tuple[ - Type[Exception], + tuple[ + type[Exception], Callable[[Request, Exception], JSONAPIResponse], ] ] = [ diff --git a/optimade/server/mappers/entries.py b/optimade/server/mappers/entries.py index 103671d3a..b14edfc17 100644 --- a/optimade/server/mappers/entries.py +++ b/optimade/server/mappers/entries.py @@ -1,6 +1,7 @@ import warnings +from collections.abc import Iterable from functools import lru_cache -from typing import Any, Dict, Iterable, List, Optional, Set, Tuple, Type, Union +from typing import Any, Optional, Union from optimade.models.entries import EntryResource @@ -65,19 +66,19 @@ class BaseResourceMapper: except (ImportError, ModuleNotFoundError): PROVIDERS = {} - KNOWN_PROVIDER_PREFIXES: Set[str] = set( + KNOWN_PROVIDER_PREFIXES: set[str] = { prov["id"] for prov in PROVIDERS.get("data", []) - ) - ALIASES: Tuple[Tuple[str, str], ...] = () - LENGTH_ALIASES: Tuple[Tuple[str, str], ...] = () - PROVIDER_FIELDS: Tuple[str, ...] = () - ENTRY_RESOURCE_CLASS: Type[EntryResource] = EntryResource - RELATIONSHIP_ENTRY_TYPES: Set[str] = {"references", "structures"} - TOP_LEVEL_NON_ATTRIBUTES_FIELDS: Set[str] = {"id", "type", "relationships", "links"} + } + ALIASES: tuple[tuple[str, str], ...] = () + LENGTH_ALIASES: tuple[tuple[str, str], ...] = () + PROVIDER_FIELDS: tuple[str, ...] = () + ENTRY_RESOURCE_CLASS: type[EntryResource] = EntryResource + RELATIONSHIP_ENTRY_TYPES: set[str] = {"references", "structures"} + TOP_LEVEL_NON_ATTRIBUTES_FIELDS: set[str] = {"id", "type", "relationships", "links"} @classmethod @lru_cache(maxsize=NUM_ENTRY_TYPES) - def all_aliases(cls) -> Iterable[Tuple[str, str]]: + def all_aliases(cls) -> Iterable[tuple[str, str]]: """Returns all of the associated aliases for this entry type, including those defined by the server config. The first member of each tuple is the OPTIMADE-compliant field name, the second @@ -116,7 +117,7 @@ def all_aliases(cls) -> Iterable[Tuple[str, str]]: @classproperty @lru_cache(maxsize=1) - def SUPPORTED_PREFIXES(cls) -> Set[str]: + def SUPPORTED_PREFIXES(cls) -> set[str]: """A set of prefixes handled by this entry type. !!! note @@ -131,7 +132,7 @@ def SUPPORTED_PREFIXES(cls) -> Set[str]: return {CONFIG.provider.prefix} @classproperty - def ALL_ATTRIBUTES(cls) -> Set[str]: + def ALL_ATTRIBUTES(cls) -> set[str]: """Returns all attributes served by this entry.""" from optimade.server.config import CONFIG @@ -147,11 +148,11 @@ def ALL_ATTRIBUTES(cls) -> Set[str]: for field in CONFIG.provider_fields.get(cls.ENDPOINT, ()) if isinstance(field, dict) ) - .union(set(cls.get_optimade_field(field) for field in cls.PROVIDER_FIELDS)) + .union({cls.get_optimade_field(field) for field in cls.PROVIDER_FIELDS}) ) @classproperty - def ENTRY_RESOURCE_ATTRIBUTES(cls) -> Dict[str, Any]: + def ENTRY_RESOURCE_ATTRIBUTES(cls) -> dict[str, Any]: """Returns the dictionary of attributes defined by the underlying entry resource class.""" from optimade.server.schemas import retrieve_queryable_properties @@ -173,7 +174,7 @@ def ENDPOINT(cls) -> str: @classmethod @lru_cache(maxsize=NUM_ENTRY_TYPES) - def all_length_aliases(cls) -> Tuple[Tuple[str, str], ...]: + def all_length_aliases(cls) -> tuple[tuple[str, str], ...]: """Returns all of the associated length aliases for this class, including those defined by the server config. @@ -368,7 +369,7 @@ def map_back(cls, doc: dict) -> dict: @classmethod def deserialize( cls, results: Union[dict, Iterable[dict]] - ) -> Union[List[EntryResource], EntryResource]: + ) -> Union[list[EntryResource], EntryResource]: """Converts the raw database entries for this class into serialized models, mapping the data along the way. diff --git a/optimade/server/middleware.py b/optimade/server/middleware.py index 64cb44f90..79863dee8 100644 --- a/optimade/server/middleware.py +++ b/optimade/server/middleware.py @@ -9,7 +9,8 @@ import re import urllib.parse import warnings -from typing import Generator, Iterable, List, Optional, TextIO, Type, Union +from collections.abc import Generator, Iterable +from typing import Optional, TextIO, Union from starlette.datastructures import URL as StarletteURL from starlette.middleware.base import BaseHTTPMiddleware @@ -111,7 +112,7 @@ class HandleApiHint(BaseHTTPMiddleware): """Handle `api_hint` query parameter.""" @staticmethod - def handle_api_hint(api_hint: List[str]) -> Union[None, str]: + def handle_api_hint(api_hint: list[str]) -> Union[None, str]: """Handle `api_hint` parameter value. There are several scenarios that can play out, when handling the `api_hint` @@ -308,12 +309,12 @@ class AddWarnings(BaseHTTPMiddleware): """ - _warnings: List[Warnings] + _warnings: list[Warnings] def showwarning( self, message: Union[Warning, str], - category: Type[Warning], + category: type[Warning], filename: str, lineno: int, file: Optional[TextIO] = None, diff --git a/optimade/server/query_params.py b/optimade/server/query_params.py index a955d8ef7..fee57f5dc 100644 --- a/optimade/server/query_params.py +++ b/optimade/server/query_params.py @@ -1,5 +1,5 @@ from abc import ABC -from typing import Iterable, List +from collections.abc import Iterable from warnings import warn from fastapi import Query @@ -21,7 +21,7 @@ class BaseQueryParams(ABC): """ - unsupported_params: List[str] = [] + unsupported_params: list[str] = [] def check_params(self, query_params: Iterable[str]) -> None: """This method checks whether all the query parameters that are specified @@ -173,7 +173,7 @@ class EntryListingQueryParams(BaseQueryParams): """ # The reference server implementation only supports offset/number-based pagination - unsupported_params: List[str] = [ + unsupported_params: list[str] = [ "page_cursor", "page_below", ] diff --git a/optimade/server/routers/landing.py b/optimade/server/routers/landing.py index 3b51f2a1c..8860ae252 100644 --- a/optimade/server/routers/landing.py +++ b/optimade/server/routers/landing.py @@ -13,7 +13,7 @@ from optimade.server.routers.utils import get_base_url, meta_values -@lru_cache() +@lru_cache def render_landing_page(url: str) -> HTMLResponse: """Render and cache the landing page. diff --git a/optimade/server/routers/links.py b/optimade/server/routers/links.py index 025d9f76c..a01bcb797 100644 --- a/optimade/server/routers/links.py +++ b/optimade/server/routers/links.py @@ -1,4 +1,4 @@ -from typing import Any, Dict +from typing import Any from fastapi import APIRouter, Depends, Request @@ -21,7 +21,7 @@ @router.get( "/links", - response_model=LinksResponse if CONFIG.validate_api_response else Dict, + response_model=LinksResponse if CONFIG.validate_api_response else dict, response_model_exclude_unset=True, tags=["Links"], responses=ERROR_RESPONSES, diff --git a/optimade/server/routers/references.py b/optimade/server/routers/references.py index 2508c700a..b2b55dda5 100644 --- a/optimade/server/routers/references.py +++ b/optimade/server/routers/references.py @@ -1,4 +1,4 @@ -from typing import Any, Dict +from typing import Any from fastapi import APIRouter, Depends, Request @@ -25,7 +25,7 @@ @router.get( "/references", - response_model=ReferenceResponseMany if CONFIG.validate_api_response else Dict, + response_model=ReferenceResponseMany if CONFIG.validate_api_response else dict, response_model_exclude_unset=True, tags=["References"], responses=ERROR_RESPONSES, @@ -43,7 +43,7 @@ def get_references( @router.get( "/references/{entry_id:path}", - response_model=ReferenceResponseOne if CONFIG.validate_api_response else Dict, + response_model=ReferenceResponseOne if CONFIG.validate_api_response else dict, response_model_exclude_unset=True, tags=["References"], responses=ERROR_RESPONSES, diff --git a/optimade/server/routers/structures.py b/optimade/server/routers/structures.py index 00980b246..594285a07 100644 --- a/optimade/server/routers/structures.py +++ b/optimade/server/routers/structures.py @@ -1,4 +1,4 @@ -from typing import Any, Dict +from typing import Any from fastapi import APIRouter, Depends, Request @@ -25,7 +25,7 @@ @router.get( "/structures", - response_model=StructureResponseMany if CONFIG.validate_api_response else Dict, + response_model=StructureResponseMany if CONFIG.validate_api_response else dict, response_model_exclude_unset=True, tags=["Structures"], responses=ERROR_RESPONSES, @@ -43,7 +43,7 @@ def get_structures( @router.get( "/structures/{entry_id:path}", - response_model=StructureResponseOne if CONFIG.validate_api_response else Dict, + response_model=StructureResponseOne if CONFIG.validate_api_response else dict, response_model_exclude_unset=True, tags=["Structures"], responses=ERROR_RESPONSES, diff --git a/optimade/server/routers/utils.py b/optimade/server/routers/utils.py index cd85a76ed..16928ace8 100644 --- a/optimade/server/routers/utils.py +++ b/optimade/server/routers/utils.py @@ -2,7 +2,7 @@ import re import urllib.parse from datetime import datetime -from typing import Any, Dict, List, Optional, Set, Type, Union +from typing import Any, Optional, Union from fastapi import Request from fastapi.responses import JSONResponse @@ -91,10 +91,10 @@ def meta_values( def handle_response_fields( - results: Union[List[EntryResource], EntryResource, List[Dict], Dict], - exclude_fields: Set[str], - include_fields: Set[str], -) -> List[Dict[str, Any]]: + results: Union[list[EntryResource], EntryResource, list[dict], dict], + exclude_fields: set[str], + include_fields: set[str], +) -> list[dict[str, Any]]: """Handle query parameter `response_fields`. It is assumed that all fields are under `attributes`. @@ -137,10 +137,10 @@ def handle_response_fields( def get_included_relationships( - results: Union[EntryResource, List[EntryResource], Dict, List[Dict]], - ENTRY_COLLECTIONS: Dict[str, EntryCollection], - include_param: List[str], -) -> List[Union[EntryResource, Dict]]: + results: Union[EntryResource, list[EntryResource], dict, list[dict]], + ENTRY_COLLECTIONS: dict[str, EntryCollection], + include_param: list[str], +) -> list[Union[EntryResource, dict]]: """Filters the included relationships and makes the appropriate compound request to include them in the response. @@ -168,7 +168,7 @@ def get_included_relationships( f"Known relationship types: {sorted(ENTRY_COLLECTIONS.keys())}" ) - endpoint_includes: Dict[Any, Dict] = defaultdict(dict) + endpoint_includes: dict[Any, dict] = defaultdict(dict) for doc in results: # convert list of references into dict by ID to only included unique IDs if doc is None: @@ -197,12 +197,12 @@ def get_included_relationships( if ref["id"] not in endpoint_includes[entry_type]: endpoint_includes[entry_type][ref["id"]] = ref - included: Dict[ - str, Union[List[EntryResource], EntryResource, List[Dict], Dict] + included: dict[ + str, Union[list[EntryResource], EntryResource, list[dict], dict] ] = {} for entry_type in endpoint_includes: compound_filter = " OR ".join( - ['id="{}"'.format(ref_id) for ref_id in endpoint_includes[entry_type]] + [f'id="{ref_id}"' for ref_id in endpoint_includes[entry_type]] ) params = EntryListingQueryParams( filter=compound_filter, @@ -246,10 +246,10 @@ def get_base_url( def get_entries( collection: EntryCollection, - response: Type[EntryResponseMany], # noqa + response: type[EntryResponseMany], # noqa request: Request, params: EntryListingQueryParams, -) -> Dict: +) -> dict: """Generalized /{entry} endpoint getter""" from optimade.server.routers import ENTRY_COLLECTIONS @@ -304,10 +304,10 @@ def get_entries( def get_single_entry( collection: EntryCollection, entry_id: str, - response: Type[EntryResponseOne], + response: type[EntryResponseOne], request: Request, params: SingleEntryQueryParams, -) -> Dict: +) -> dict: from optimade.server.routers import ENTRY_COLLECTIONS params.check_params(request.query_params) diff --git a/optimade/server/schemas.py b/optimade/server/schemas.py index c01cc914e..fa900a9a3 100644 --- a/optimade/server/schemas.py +++ b/optimade/server/schemas.py @@ -1,4 +1,5 @@ -from typing import Callable, Dict, Iterable, Optional +from collections.abc import Iterable +from typing import Callable, Optional from optimade.models import ( DataType, @@ -9,7 +10,7 @@ __all__ = ("ENTRY_INFO_SCHEMAS", "ERROR_RESPONSES", "retrieve_queryable_properties") -ENTRY_INFO_SCHEMAS: Dict[str, Callable[[], Dict]] = { +ENTRY_INFO_SCHEMAS: dict[str, Callable[[], dict]] = { "structures": StructureResource.schema, "references": ReferenceResource.schema, } @@ -24,7 +25,7 @@ """ from optimade.exceptions import POSSIBLE_ERRORS - ERROR_RESPONSES: Optional[Dict[int, Dict]] = { + ERROR_RESPONSES: Optional[dict[int, dict]] = { err.status_code: {"model": ErrorResponse, "description": err.title} for err in POSSIBLE_ERRORS } diff --git a/optimade/utils.py b/optimade/utils.py index cb3039565..3540263ff 100644 --- a/optimade/utils.py +++ b/optimade/utils.py @@ -4,7 +4,8 @@ """ import json -from typing import Container, Iterable, List, Optional +from collections.abc import Container, Iterable +from typing import Optional from pydantic import ValidationError @@ -102,7 +103,7 @@ def get_providers(add_mongo_id: bool = False) -> list: def get_child_database_links( provider: LinksResource, obey_aggregate: bool = True -) -> List[LinksResource]: +) -> list[LinksResource]: """For a provider, return a list of available child databases. Arguments: diff --git a/optimade/validator/config.py b/optimade/validator/config.py index 390a3f7b8..69a3ed0a6 100644 --- a/optimade/validator/config.py +++ b/optimade/validator/config.py @@ -7,7 +7,8 @@ """ -from typing import Any, Container, Dict, List, Set +from collections.abc import Container +from typing import Any from pydantic import BaseSettings, Field @@ -122,26 +123,26 @@ class ValidatorConfig(BaseSettings): """ - response_classes: Dict[str, Any] = Field( + response_classes: dict[str, Any] = Field( _RESPONSE_CLASSES, description="Dictionary containing the mapping between endpoints and response classes for the main database", ) - response_classes_index: Dict[str, Any] = Field( + response_classes_index: dict[str, Any] = Field( _RESPONSE_CLASSES_INDEX, description="Dictionary containing the mapping between endpoints and response classes for the index meta-database", ) - entry_schemas: Dict[str, Any] = Field( + entry_schemas: dict[str, Any] = Field( _ENTRY_SCHEMAS, description="The entry listing endpoint schemas" ) - entry_endpoints: Set[str] = Field( + entry_endpoints: set[str] = Field( _ENTRY_ENDPOINTS, description="The entry endpoints to validate, if present in the API's `/info` response `entry_types_by_format['json']`", ) - unique_properties: Set[str] = Field( + unique_properties: set[str] = Field( _UNIQUE_PROPERTIES, description=( "Fields that should be treated as unique indexes for all endpoints, " @@ -149,7 +150,7 @@ class ValidatorConfig(BaseSettings): ), ) - inclusive_operators: Dict[DataType, Set[str]] = Field( + inclusive_operators: dict[DataType, set[str]] = Field( _INCLUSIVE_OPERATORS, description=( "Dictionary mapping OPTIMADE `DataType`s to a list of operators that are 'inclusive', " @@ -157,7 +158,7 @@ class ValidatorConfig(BaseSettings): ), ) - exclusive_operators: Dict[DataType, Set[str]] = Field( + exclusive_operators: dict[DataType, set[str]] = Field( _EXCLUSIVE_OPERATORS, description=( "Dictionary mapping OPTIMADE `DataType`s to a list of operators that are 'exclusive', " @@ -165,7 +166,7 @@ class ValidatorConfig(BaseSettings): ), ) - field_specific_overrides: Dict[str, Dict[SupportLevel, Container[str]]] = Field( + field_specific_overrides: dict[str, dict[SupportLevel, Container[str]]] = Field( _FIELD_SPECIFIC_OVERRIDES, description=( "Some fields do not require all type comparison operators to be supported. " @@ -181,16 +182,16 @@ class ValidatorConfig(BaseSettings): ) info_endpoint: str = Field("info", description="The name of the info endpoint") - non_entry_endpoints: Set[str] = Field( + non_entry_endpoints: set[str] = Field( _NON_ENTRY_ENDPOINTS, description="The list specification-mandated endpoint names that do not contain entries", ) - top_level_non_attribute_fields: Set[str] = Field( + top_level_non_attribute_fields: set[str] = Field( BaseResourceMapper.TOP_LEVEL_NON_ATTRIBUTES_FIELDS, description="Field names to treat as top-level", ) - enum_fallback_values: Dict[str, Dict[str, List[str]]] = Field( + enum_fallback_values: dict[str, dict[str, list[str]]] = Field( _ENUM_DUMMY_VALUES, description="Provide fallback values for enum fields to use when validating filters.", ) diff --git a/optimade/validator/utils.py b/optimade/validator/utils.py index 81e5e11b7..94db640bf 100644 --- a/optimade/validator/utils.py +++ b/optimade/validator/utils.py @@ -18,7 +18,7 @@ import time import traceback as tb import urllib.parse -from typing import Any, Callable, Dict, List, Optional, Tuple +from typing import Any, Callable, Optional import requests from pydantic import Field, ValidationError @@ -80,11 +80,11 @@ class ValidatorResults: internal_failure_count: int = 0 optional_success_count: int = 0 optional_failure_count: int = 0 - failure_messages: List[Tuple[str, str]] = dataclasses.field(default_factory=list) - internal_failure_messages: List[Tuple[str, str]] = dataclasses.field( + failure_messages: list[tuple[str, str]] = dataclasses.field(default_factory=list) + internal_failure_messages: list[tuple[str, str]] = dataclasses.field( default_factory=list ) - optional_failure_messages: List[Tuple[str, str]] = dataclasses.field( + optional_failure_messages: list[tuple[str, str]] = dataclasses.field( default_factory=list ) verbosity: int = 0 @@ -146,7 +146,7 @@ def add_failure( self.optional_failure_count += 1 self.optional_failure_messages.append((summary, message)) - pprint_types: Dict[str, Tuple[Callable, Callable]] = { + pprint_types: dict[str, tuple[Callable, Callable]] = { "internal": (print_notify, print_warning), "optional": (print, print), } @@ -168,7 +168,7 @@ def __init__( self, base_url: str, max_retries: int = 5, - headers: Optional[Dict[str, str]] = None, + headers: Optional[dict[str, str]] = None, timeout: Optional[float] = DEFAULT_CONN_TIMEOUT, read_timeout: Optional[float] = DEFAULT_READ_TIMEOUT, ) -> None: @@ -267,7 +267,7 @@ def get(self, request: str): raise ResponseError(message) -def test_case(test_fn: Callable[..., Tuple[Any, str]]): +def test_case(test_fn: Callable[..., tuple[Any, str]]): """Wrapper for test case functions, which pretty-prints any errors depending on verbosity level, collates the number and severity of test failures, returns the response and summary string to the caller. @@ -404,19 +404,19 @@ def wrapper( class ValidatorLinksResponse(Success): meta: ResponseMeta = Field(...) - data: List[LinksResource] = Field(...) + data: list[LinksResource] = Field(...) class ValidatorEntryResponseOne(Success): meta: ResponseMeta = Field(...) data: EntryResource = Field(...) - included: Optional[List[Dict[str, Any]]] = Field(None) # type: ignore[assignment] + included: Optional[list[dict[str, Any]]] = Field(None) # type: ignore[assignment] class ValidatorEntryResponseMany(Success): meta: ResponseMeta = Field(...) - data: List[EntryResource] = Field(...) - included: Optional[List[Dict[str, Any]]] = Field(None) # type: ignore[assignment] + data: list[EntryResource] = Field(...) + included: Optional[list[dict[str, Any]]] = Field(None) # type: ignore[assignment] class ValidatorReferenceResponseOne(ValidatorEntryResponseOne): @@ -424,7 +424,7 @@ class ValidatorReferenceResponseOne(ValidatorEntryResponseOne): class ValidatorReferenceResponseMany(ValidatorEntryResponseMany): - data: List[ReferenceResource] = Field(...) + data: list[ReferenceResource] = Field(...) class ValidatorStructureResponseOne(ValidatorEntryResponseOne): @@ -432,4 +432,4 @@ class ValidatorStructureResponseOne(ValidatorEntryResponseOne): class ValidatorStructureResponseMany(ValidatorEntryResponseMany): - data: List[StructureResource] = Field(...) + data: list[StructureResource] = Field(...) diff --git a/optimade/validator/validator.py b/optimade/validator/validator.py index 82f4b583a..84ca8ae92 100644 --- a/optimade/validator/validator.py +++ b/optimade/validator/validator.py @@ -13,7 +13,7 @@ class that can be pointed at an OPTIMADE implementation and validated import re import sys import urllib.parse -from typing import Any, Dict, List, Optional, Set, Tuple, Union +from typing import Any, Optional, Union import requests @@ -72,7 +72,7 @@ def __init__( # pylint: disable=too-many-arguments as_type: Optional[str] = None, index: bool = False, minimal: bool = False, - http_headers: Optional[Dict[str, str]] = None, + http_headers: Optional[dict[str, str]] = None, timeout: float = DEFAULT_CONN_TIMEOUT, read_timeout: float = DEFAULT_READ_TIMEOUT, ): @@ -176,8 +176,8 @@ def __init__( # pylint: disable=too-many-arguments self.valid = None - self._test_id_by_type: Dict[str, Any] = {} - self._entry_info_by_type: Dict[str, Any] = {} + self._test_id_by_type: dict[str, Any] = {} + self._entry_info_by_type: dict[str, Any] = {} self.results = ValidatorResults(verbosity=self.verbosity) @@ -353,7 +353,7 @@ def validate_implementation(self): self.print_summary() @test_case - def _recurse_through_endpoint(self, endp: str) -> Tuple[Optional[bool], str]: + def _recurse_through_endpoint(self, endp: str) -> tuple[Optional[bool], str]: """For a given endpoint (`endp`), get the entry type and supported fields, testing that all mandatory fields are supported, then test queries on every property according @@ -450,8 +450,8 @@ def _test_unknown_provider_property(self, endp): ) def _check_entry_info( - self, entry_info: Dict[str, Any], endp: str - ) -> Dict[str, Dict[str, Any]]: + self, entry_info: dict[str, Any], endp: str + ) -> dict[str, dict[str, Any]]: """Checks that `entry_info` contains all the required properties, and returns the property list for the endpoint. @@ -473,8 +473,8 @@ def _check_entry_info( @test_case def _test_must_properties( - self, properties: List[str], endp: str - ) -> Tuple[bool, str]: + self, properties: list[str], endp: str + ) -> tuple[bool, str]: """Check that the entry info lists all properties with the "MUST" support level for this endpoint. @@ -486,13 +486,13 @@ def _test_must_properties( `True` if the properties were found, and a string summary. """ - must_props = set( + must_props = { prop for prop in CONF.entry_schemas.get(endp, {}) if CONF.entry_schemas[endp].get(prop, {}).get("support") == SupportLevel.MUST - ) - must_props_supported = set(prop for prop in properties if prop in must_props) + } + must_props_supported = {prop for prop in properties if prop in must_props} missing = must_props - must_props_supported if len(missing) != 0: raise ResponseError( @@ -503,8 +503,8 @@ def _test_must_properties( @test_case def _get_archetypal_entry( - self, endp: str, properties: List[str] - ) -> Tuple[Optional[Dict[str, Any]], str]: + self, endp: str, properties: list[str] + ) -> tuple[Optional[dict[str, Any]], str]: """Get a random entry from the first page of results for this endpoint. @@ -544,8 +544,8 @@ def _get_archetypal_entry( @test_case def _check_response_fields( - self, endp: str, fields: List[str] - ) -> Tuple[Optional[bool], str]: + self, endp: str, fields: list[str] + ) -> tuple[Optional[bool], str]: """Check that the response field query parameter is obeyed. Parameters: @@ -593,8 +593,8 @@ def _construct_queries_for_property( prop_type: DataType, sortable: bool, endp: str, - chosen_entry: Dict[str, Any], - ) -> Tuple[Optional[bool], str]: + chosen_entry: dict[str, Any], + ) -> tuple[Optional[bool], str]: """For the given property, property type and chose entry, this method runs a series of queries for each field in the entry, testing that the initial document is returned where expected. @@ -704,9 +704,9 @@ def _construct_single_property_filters( prop_type: DataType, sortable: bool, endp: str, - chosen_entry: Dict[str, Any], + chosen_entry: dict[str, Any], query_optional: bool, - ) -> Tuple[Optional[bool], str]: + ) -> tuple[Optional[bool], str]: """This method constructs appropriate queries using all operators for a certain field and applies some tests: @@ -847,7 +847,7 @@ def _construct_single_property_filters( # if we have all results on this page, check that the blessed ID is in the response if excluded and ( chosen_entry.get("id", "") - in set(entry.get("id") for entry in response["data"]) + in {entry.get("id") for entry in response["data"]} ): raise ResponseError( f"Entry {chosen_entry['id']} with value {prop!r}: {test_value} was not excluded by {query!r}" @@ -1060,7 +1060,7 @@ def _test_multi_entry_endpoint(self, endp: str) -> None: @test_case def _test_data_available_matches_data_returned( self, deserialized: Any - ) -> Tuple[Optional[bool], str]: + ) -> tuple[Optional[bool], str]: """In the case where no query is requested, `data_available` must equal `data_returned` in the meta response, which is tested here. @@ -1126,7 +1126,7 @@ def _test_versions_endpoint(self): @test_case def _test_versions_endpoint_content( self, response: requests.Response - ) -> Tuple[requests.Response, str]: + ) -> tuple[requests.Response, str]: """Checks that the response from the versions endpoint complies with the specification and that its 'Content-Type' header complies with [RFC 4180](https://tools.ietf.org/html/rfc4180.html). @@ -1186,9 +1186,9 @@ def _test_versions_endpoint_content( @test_case def _test_versions_headers( self, - content_type: Dict[str, Any], - expected_parameter: Union[str, List[str]], - ) -> Tuple[Dict[str, Any], str]: + content_type: dict[str, Any], + expected_parameter: Union[str, list[str]], + ) -> tuple[dict[str, Any], str]: """Tests that the `Content-Type` field of the `/versions` header contains the passed parameter. @@ -1270,8 +1270,8 @@ def _test_page_limit( self, response: requests.models.Response, check_next_link: int = 5, - previous_links: Optional[Set[str]] = None, - ) -> Tuple[Optional[bool], str]: + previous_links: Optional[set[str]] = None, + ) -> tuple[Optional[bool], str]: """Test that a multi-entry endpoint obeys the page limit by following pagination links up to a depth of `check_next_link`. @@ -1387,7 +1387,7 @@ def _deserialize_response( response: requests.models.Response, response_cls: Any, request: Optional[str] = None, - ) -> Tuple[Any, str]: + ) -> tuple[Any, str]: """Try to create the appropriate pydantic model from the response. Parameters: @@ -1416,13 +1416,13 @@ def _deserialize_response( return ( response_cls(**json_response), - "deserialized correctly as object of type {}".format(response_cls), + f"deserialized correctly as object of type {response_cls}", ) @test_case def _get_available_endpoints( - self, base_info: Union[Any, Dict[str, Any]] - ) -> Tuple[Optional[List[str]], str]: + self, base_info: Union[Any, dict[str, Any]] + ) -> tuple[Optional[list[str]], str]: """Tries to get `entry_types_by_format` from base info response even if it could not be deserialized. @@ -1478,8 +1478,8 @@ def _get_available_endpoints( @test_case def _get_endpoint( - self, request_str: str, expected_status_code: Union[List[int], int] = 200 - ) -> Tuple[Optional[requests.Response], str]: + self, request_str: str, expected_status_code: Union[list[int], int] = 200 + ) -> tuple[Optional[requests.Response], str]: """Gets the response from the endpoint specified by `request_str`. function is wrapped by the `test_case` decorator diff --git a/tasks.py b/tasks.py index 216cdd753..1c78a2150 100644 --- a/tasks.py +++ b/tasks.py @@ -3,7 +3,7 @@ import re import sys from pathlib import Path -from typing import TYPE_CHECKING, Optional, Tuple +from typing import TYPE_CHECKING, Optional from invoke import task from jsondiff import diff @@ -15,9 +15,9 @@ TOP_DIR = Path(__file__).parent.resolve() -def update_file(filename: str, sub_line: Tuple[str, str], strip: Optional[str] = None): +def update_file(filename: str, sub_line: tuple[str, str], strip: Optional[str] = None): """Utility function for tasks to read, update, and write files""" - with open(filename, "r") as handle: + with open(filename) as handle: lines = [ re.sub(sub_line[0], sub_line[1], line.rstrip(strip)) for line in handle ] @@ -119,7 +119,7 @@ def setver(_, ver=""): (r'"version": ".*",', f'"version": "{ver}",'), ) - print("Bumped version to {}".format(ver)) + print(f"Bumped version to {ver}") @task(help={"ver": "OPTIMADE API version to set"}, post=[update_openapijson]) @@ -191,7 +191,7 @@ def create_api_reference_docs(context, pre_clean=False, pre_commit=False): def write_file(full_path: Path, content: str): """Write file with `content` to `full_path`""" if full_path.exists(): - with open(full_path, "r") as handle: + with open(full_path) as handle: cached_content = handle.read() if content == cached_content: del cached_content @@ -306,7 +306,7 @@ def print_error(string): print(f"\033[31m{line}\033[0m") swagger_url = "https://validator.swagger.io/validator/debug" - with open(fname, "r") as f: + with open(fname) as f: schema = json.load(f) response = requests.post(swagger_url, json=schema) diff --git a/tests/adapters/references/conftest.py b/tests/adapters/references/conftest.py index 52fbe3f36..2afe72cc7 100644 --- a/tests/adapters/references/conftest.py +++ b/tests/adapters/references/conftest.py @@ -10,9 +10,7 @@ @pytest.fixture def RAW_REFERENCES(): """Read and return raw_references.json""" - with open( - Path(__file__).parent.joinpath("raw_test_references.json"), "r" - ) as raw_data: + with open(Path(__file__).parent.joinpath("raw_test_references.json")) as raw_data: return json.load(raw_data) diff --git a/tests/adapters/structures/conftest.py b/tests/adapters/structures/conftest.py index 44afd7e47..c151f962e 100644 --- a/tests/adapters/structures/conftest.py +++ b/tests/adapters/structures/conftest.py @@ -1,7 +1,6 @@ import json from pathlib import Path from random import choice -from typing import List import pytest @@ -9,18 +8,16 @@ @pytest.fixture -def RAW_STRUCTURES() -> List[dict]: +def RAW_STRUCTURES() -> list[dict]: """Read and return raw_structures.json""" - with open( - Path(__file__).parent.joinpath("raw_test_structures.json"), "r" - ) as raw_data: + with open(Path(__file__).parent.joinpath("raw_test_structures.json")) as raw_data: return json.load(raw_data) @pytest.fixture -def SPECIAL_SPECIES_STRUCTURES() -> List[dict]: +def SPECIAL_SPECIES_STRUCTURES() -> list[dict]: """Read and return special_species.json""" - with open(Path(__file__).parent.joinpath("special_species.json"), "r") as raw_data: + with open(Path(__file__).parent.joinpath("special_species.json")) as raw_data: return json.load(raw_data) @@ -37,7 +34,7 @@ def structure(raw_structure) -> Structure: @pytest.fixture -def structures(RAW_STRUCTURES) -> List[Structure]: +def structures(RAW_STRUCTURES) -> list[Structure]: """Create and return list of adapters.Structure""" return [Structure(_) for _ in RAW_STRUCTURES] diff --git a/tests/adapters/structures/utils.py b/tests/adapters/structures/utils.py index b11df3182..ae7fc9ed3 100644 --- a/tests/adapters/structures/utils.py +++ b/tests/adapters/structures/utils.py @@ -5,7 +5,7 @@ def get_min_ver(dependency: str) -> str: """Retrieve version of `dependency` from setup.py, raise if not found.""" pyproject_toml = Path(__file__).parent.joinpath("../../../pyproject.toml") - with open(pyproject_toml, "r") as setup_file: + with open(pyproject_toml) as setup_file: for line in setup_file.readlines(): min_ver = re.findall(rf'"{dependency}((=|!|<|>|~)=|>|<)(.+)"', line) if min_ver: diff --git a/tests/filterparser/test_filterparser.py b/tests/filterparser/test_filterparser.py index 5296bf797..5a01130b9 100644 --- a/tests/filterparser/test_filterparser.py +++ b/tests/filterparser/test_filterparser.py @@ -1,5 +1,4 @@ import abc -from typing import Tuple import pytest from lark import Tree @@ -11,7 +10,7 @@ class BaseTestFilterParser(abc.ABC): """Base class for parsing different versions of the grammar using `LarkParser`.""" - version: Tuple[int, int, int] + version: tuple[int, int, int] variant: str = "default" @pytest.fixture(autouse=True) diff --git a/tests/models/conftest.py b/tests/models/conftest.py index a6cc48564..461c8d5a3 100644 --- a/tests/models/conftest.py +++ b/tests/models/conftest.py @@ -12,7 +12,7 @@ def load_test_data(filename: str) -> list: if not json_file_path.exists(): raise RuntimeError(f"Could not find {filename!r} in 'tests.models.test_data'") - with open(json_file_path, "r") as handle: + with open(json_file_path) as handle: data = json.load(handle) return data diff --git a/tests/models/test_jsonapi.py b/tests/models/test_jsonapi.py index b2e09cb4a..4623e79e5 100644 --- a/tests/models/test_jsonapi.py +++ b/tests/models/test_jsonapi.py @@ -8,7 +8,7 @@ def test_hashability(): from optimade.models.jsonapi import Error error = Error(id="test") - assert set([error]) + assert {error} def test_toplevel_links(): diff --git a/tests/models/test_optimade_json.py b/tests/models/test_optimade_json.py index 49c91d985..f96b88aa1 100644 --- a/tests/models/test_optimade_json.py +++ b/tests/models/test_optimade_json.py @@ -31,7 +31,7 @@ def test_convert_python_types(): test_none = None python_types_as_objects = [ - str("Test"), + "Test", 42, 42.42, ["Test", 42], diff --git a/tests/models/test_structures.py b/tests/models/test_structures.py index 213dfd5eb..c36e0ca1c 100644 --- a/tests/models/test_structures.py +++ b/tests/models/test_structures.py @@ -194,7 +194,7 @@ def test_structure_fatal_deformities(good_structure, deformity): minor_deformities = ( - {f: None} for f in set(f for _ in CORRELATED_STRUCTURE_FIELDS for f in _) + {f: None} for f in {f for _ in CORRELATED_STRUCTURE_FIELDS for f in _} ) diff --git a/tests/models/test_utils.py b/tests/models/test_utils.py index e5b3f7502..57de92030 100644 --- a/tests/models/test_utils.py +++ b/tests/models/test_utils.py @@ -1,4 +1,4 @@ -from typing import Callable, List +from typing import Callable import pytest from pydantic import BaseModel, Field, ValidationError @@ -46,7 +46,7 @@ def test_compatible_strict_optimade_field() -> None: class CorrectModelWithStrictField(BaseModel): # check that unit and uniqueItems are passed through - good_field: List[str] = StrictField( + good_field: list[str] = StrictField( ..., support=SupportLevel.MUST, queryable=SupportLevel.OPTIONAL, @@ -58,7 +58,7 @@ class CorrectModelWithStrictField(BaseModel): ) class CorrectModelWithOptimadeField(BaseModel): - good_field: List[str] = OptimadeField( + good_field: list[str] = OptimadeField( ..., # Only difference here is that OptimadeField allows case-insensitive # strings to be passed instead of support levels directly diff --git a/tests/server/conftest.py b/tests/server/conftest.py index a943b36ca..4fd7567c5 100644 --- a/tests/server/conftest.py +++ b/tests/server/conftest.py @@ -1,4 +1,4 @@ -from typing import Dict, Optional, Union +from typing import Optional, Union import pytest @@ -124,7 +124,6 @@ def check_response(get_good_response): server: The type of server to test, or the actual test client class. """ - from typing import List from optimade.server.config import CONFIG @@ -132,11 +131,11 @@ def check_response(get_good_response): def inner( request: str, - expected_ids: Union[str, List[str]], + expected_ids: Union[str, list[str]], page_limit: int = CONFIG.page_limit, expected_return: Optional[int] = None, expected_as_is: bool = False, - expected_warnings: Optional[List[Dict[str, str]]] = None, + expected_warnings: Optional[list[dict[str, str]]] = None, server: Union[str, OptimadeTestClient] = "regular", ): if expected_warnings: diff --git a/tests/server/query_params/conftest.py b/tests/server/query_params/conftest.py index ce11819be..b7f0e59f0 100644 --- a/tests/server/query_params/conftest.py +++ b/tests/server/query_params/conftest.py @@ -12,13 +12,13 @@ def structures(): @pytest.fixture def check_include_response(get_good_response): """Fixture to check "good" `include` response""" - from typing import List, Optional, Set, Union + from typing import Optional, Union def inner( request: str, - expected_included_types: Union[List, Set], - expected_included_resources: Union[List, Set], - expected_relationship_types: Optional[Union[List, Set]] = None, + expected_included_types: Union[list, set], + expected_included_resources: Union[list, set], + expected_relationship_types: Optional[Union[list, set]] = None, server: str = "regular", ): response = get_good_response(request, server) diff --git a/tests/server/routers/test_utils.py b/tests/server/routers/test_utils.py index 37b1f8ee5..7a94bfc2d 100644 --- a/tests/server/routers/test_utils.py +++ b/tests/server/routers/test_utils.py @@ -1,5 +1,6 @@ """Tests specifically for optimade.servers.routers.utils.""" -from typing import Mapping, Optional, Tuple, Union +from collections.abc import Mapping +from typing import Optional, Union from unittest import mock import pytest @@ -8,7 +9,7 @@ def mocked_providers_list_response( url: Union[str, bytes] = "", - param: Optional[Union[Mapping[str, str], Tuple[str, str]]] = None, + param: Optional[Union[Mapping[str, str], tuple[str, str]]] = None, **kwargs, ): """This function will be used to mock requests.get diff --git a/tests/server/test_client.py b/tests/server/test_client.py index 732385bcc..406366f84 100644 --- a/tests/server/test_client.py +++ b/tests/server/test_client.py @@ -5,7 +5,7 @@ import warnings from functools import partial from pathlib import Path -from typing import Dict, Optional +from typing import Optional import httpx import pytest @@ -337,7 +337,7 @@ def test_command_line_client_write_to_file( assert 'Performing query structures/?filter=elements HAS "Ag"' in captured.err assert not captured.out assert Path(test_filename).is_file() - with open(test_filename, "r") as f: + with open(test_filename) as f: results = json.load(f) for url in TEST_URLS: assert len(results["structures"]['elements HAS "Ag"'][url]["data"]) == 11 @@ -360,9 +360,9 @@ def test_strict_async(async_http_client, http_client, use_async): @pytest.mark.parametrize("use_async", [True, False]) def test_client_global_data_callback(async_http_client, http_client, use_async): - container: Dict[str, str] = {} + container: dict[str, str] = {} - def global_database_callback(_: str, results: Dict): + def global_database_callback(_: str, results: dict): """A test callback that creates a flat dictionary of results via global state""" for structure in results["data"]: @@ -386,7 +386,7 @@ def global_database_callback(_: str, results: Dict): @pytest.mark.parametrize("use_async", [True, False]) def test_client_page_skip_callback(async_http_client, http_client, use_async): - def page_skip_callback(_: str, results: Dict) -> Optional[Dict]: + def page_skip_callback(_: str, results: dict) -> Optional[dict]: """A test callback that skips to the final page of results.""" if len(results["data"]) > 16: return {"next": f"{TEST_URL}/structures?page_offset=16"} @@ -407,10 +407,10 @@ def page_skip_callback(_: str, results: Dict) -> Optional[Dict]: @pytest.mark.parametrize("use_async", [True, False]) def test_client_mutable_data_callback(async_http_client, http_client, use_async): - container: Dict[str, str] = {} + container: dict[str, str] = {} def mutable_database_callback( - _: str, results: Dict, db: Optional[Dict[str, str]] = None + _: str, results: dict, db: Optional[dict[str, str]] = None ) -> None: """A test callback that creates a flat dictionary of results via mutable args.""" @@ -436,7 +436,7 @@ def mutable_database_callback( def test_client_asynchronous_write_callback( async_http_client, http_client, use_async, tmp_path ): - def write_to_file(_: str, results: Dict): + def write_to_file(_: str, results: dict): """A test callback that creates a flat dictionary of results via global state""" with open(tmp_path / "formulae.csv", "a") as f: @@ -458,7 +458,7 @@ def write_to_file(_: str, results: Dict): cli.get(response_fields=["chemical_formula_reduced"]) - with open(tmp_path / "formulae.csv", "r") as f: + with open(tmp_path / "formulae.csv") as f: lines = f.readlines() assert len(lines) == 17 * len(TEST_URLS) + 1 diff --git a/tests/server/test_config.py b/tests/server/test_config.py index db0175b3f..6c8d70b28 100644 --- a/tests/server/test_config.py +++ b/tests/server/test_config.py @@ -33,7 +33,7 @@ def test_default_config_path(top_dir): org_env_var = os.getenv("OPTIMADE_CONFIG_FILE") - with open(top_dir.joinpath("tests/test_config.json"), "r") as config_file: + with open(top_dir.joinpath("tests/test_config.json")) as config_file: config = json.load(config_file) different_base_url = "http://something_you_will_never_think_of.com" diff --git a/tests/server/utils.py b/tests/server/utils.py index 6a046c36b..49b7b9f92 100644 --- a/tests/server/utils.py +++ b/tests/server/utils.py @@ -1,7 +1,8 @@ import json import re import warnings -from typing import Iterable, Optional, Type, Union +from collections.abc import Iterable +from typing import Optional, Union from urllib.parse import urlparse import httpx @@ -31,7 +32,7 @@ def __init__( root_path: str = "", version: str = "", ) -> None: - super(OptimadeTestClient, self).__init__( + super().__init__( app=app, base_url=base_url, raise_server_exceptions=raise_server_exceptions, @@ -64,7 +65,7 @@ def request( # pylint: disable=too-many-locals while url.startswith("/"): url = url[1:] url = f"{self.version}/{url}" - return super(OptimadeTestClient, self).request( + return super().request( method=method, url=url, **kwargs, @@ -75,7 +76,7 @@ class BaseEndpointTests: """Base class for common tests of endpoints""" request_str: Optional[str] = None - response_cls: Optional[Type[jsonapi.Response]] = None + response_cls: Optional[type[jsonapi.Response]] = None response: Optional[httpx.Response] = None json_response: Optional[dict] = None @@ -223,7 +224,7 @@ class NoJsonEndpointTests: """A simplified mixin class for tests on non-JSON endpoints.""" request_str: Optional[str] = None - response_cls: Optional[Type] = None + response_cls: Optional[type] = None response: Optional[httpx.Response] = None From 4fe22a24815813289ad33dfeaf47c58c52a0a618 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Wed, 18 Oct 2023 18:00:07 +0200 Subject: [PATCH 80/86] backporting bugfixes from of the JPBergsma/trajectory_endpoint branch. --- optimade/server/config.py | 2 +- optimade/server/entry_collections/mongo.py | 35 ++++++++------- optimade/server/main.py | 2 +- optimade/server/routers/utils.py | 52 +++++++++++++--------- 4 files changed, 53 insertions(+), 38 deletions(-) diff --git a/optimade/server/config.py b/optimade/server/config.py index 6abc86d84..da1e6ffe7 100644 --- a/optimade/server/config.py +++ b/optimade/server/config.py @@ -332,7 +332,7 @@ class ServerConfig(BaseSettings): description="""A list of the response formats that are supported by this server. Must include the "json" format.""", ) max_response_size: dict[SupportedResponseFormats, int] = Field( - {"json": 10, "jsonlines": 40}, + {"json": 10, "jsonlines": 10}, description="""This dictionary contains the approximate maximum size for a trajectory response in megabytes for the different response_formats. The keys indicate the response_format and the values the maximum size.""", ) diff --git a/optimade/server/entry_collections/mongo.py b/optimade/server/entry_collections/mongo.py index 2a82a2455..c49ebb6b4 100644 --- a/optimade/server/entry_collections/mongo.py +++ b/optimade/server/entry_collections/mongo.py @@ -195,9 +195,11 @@ def _run_db_query( # TODO handle case where the type does not have a fixed width. For example strings or dictionaries. response_format = criteria.pop("response_format") - max_return_size = CONFIG.max_response_size[ - SupportedResponseFormats(response_format) - ] # todo adjust for different output formats(take into account that the number of numbers to read is larger for a text based output format than for a binary format. + max_return_size = ( + CONFIG.max_response_size[SupportedResponseFormats(response_format)] + * 1024 + * 1024 + ) # todo adjust for different output formats(take into account that the number of numbers to read is larger for a text based output format than for a binary format. results = [] filterdict = criteria.pop("filter", {}) @@ -215,19 +217,20 @@ def _run_db_query( metadata = file_obj.metadata property_ranges = self.parse_property_ranges( criteria.pop("property_ranges", None), - metadata["sliceobj"], + metadata["slice_obj"], metadata["dim_names"], ) item_size = metadata["dtype"]["itemsize"] dim_sizes = [ - (i["stop"] - i["start"] + 1) // i["step"] for i in metadata["sliceobj"] + (i["stop"] - i["start"] + 1) // i["step"] for i in metadata["slice_obj"] ] top_stepsize = 1 for i in dim_sizes[1:]: top_stepsize *= i offset = (property_ranges[0]["start"] - 1) * item_size * top_stepsize + np_header = file_obj.readline() file_obj.seek( - offset + offset + len(np_header) ) # set the correct starting point fo the read from the gridfs file system. if (max_return_size / item_size) < ( 1 + property_ranges[0]["stop"] - property_ranges[0]["start"] @@ -241,11 +244,13 @@ def _run_db_query( shape = [n_outer] + dim_sizes[1:] else: read_size = ( + (1 + property_ranges[0]["stop"] - property_ranges[0]["start"]) + * top_stepsize + * item_size + ) + shape = [ 1 + property_ranges[0]["stop"] - property_ranges[0]["start"] - ) * top_stepsize - shape = ( - 1 + property_ranges[0]["stop"] - property_ranges[0]["start"] - ) + dim_sizes[1:] + ] + dim_sizes[1:] values = file_obj.read(read_size) entry = { @@ -276,7 +281,7 @@ def _run_db_query( return results, nresults, more_data_available def parse_property_ranges( - self, property_range_str: str, attribute_sliceobj: list, dim_names: list + self, property_range_str: str, attribute_slice_obj: list, dim_names: list ) -> list[dict]: property_range_dict = {} if property_range_str: @@ -286,17 +291,17 @@ def parse_property_ranges( property_range_dict[subrange[0]] = { "start": int(subrange[1]) if subrange[1] - else attribute_sliceobj[dim_names.index(subrange[0])]["start"], + else attribute_slice_obj[dim_names.index(subrange[0])]["start"], "stop": int(subrange[2]) if subrange[2] - else attribute_sliceobj[dim_names.index(subrange[0])]["stop"], + else attribute_slice_obj[dim_names.index(subrange[0])]["stop"], "step": int(subrange[3]) if subrange[3] - else attribute_sliceobj[dim_names.index(subrange[0])]["step"], + else attribute_slice_obj[dim_names.index(subrange[0])]["step"], } for i, dim in enumerate(dim_names): if dim not in property_range_dict: - property_range_dict[dim] = attribute_sliceobj[i] + property_range_dict[dim] = attribute_slice_obj[i] return [property_range_dict[dim] for dim in dim_names] diff --git a/optimade/server/main.py b/optimade/server/main.py index 5f75db972..a1f0f98db 100644 --- a/optimade/server/main.py +++ b/optimade/server/main.py @@ -95,7 +95,7 @@ def read_array_header(fobj): slice_obj = [ {"start": 1, "stop": i, "step": 1} for i in numpy_meta[0] ] - metadata["sliceobj"] = slice_obj + metadata["slice_obj"] = slice_obj if "dtype" not in metadata: metadata["dtype"] = { "name": numpy_meta[2].name, diff --git a/optimade/server/routers/utils.py b/optimade/server/routers/utils.py index b7580b580..868bc20c5 100644 --- a/optimade/server/routers/utils.py +++ b/optimade/server/routers/utils.py @@ -11,7 +11,6 @@ from starlette.datastructures import URL as StarletteURL from optimade import __api_version__ -from optimade.adapters.jsonl import to_jsonl from optimade.exceptions import BadRequest, InternalServerError, NotFound from optimade.models import ( # type: ignore[attr-defined] EntryResource, # type: ignore[attr-defined] @@ -37,6 +36,7 @@ "get_base_url", "get_entries", "get_single_entry", + "get_partial_entry", "mongo_id_for_database", "get_providers", "PROVIDER_LIST_URLS", @@ -268,18 +268,20 @@ def generate_links_partial_data( if entry.get("meta", {}) and entry["meta"].get("partial_data_links", {}): for property in entry["meta"]["partial_data_links"]: for response_format in CONFIG.partial_data_formats: - entry["meta"]["partial_data_links"][property].append( - { - "format": str(response_format.value), - "link": get_base_url(parsed_url_request) - + "/partial_data/" - + entry["id"] - + "?response_fields=" - + property - + "&response_format=" - + str(response_format.value), - } - ) + link = { + "format": str(response_format.value), + "link": get_base_url(parsed_url_request) + + "/partial_data/" + + entry["id"] + + "?response_fields=" + + property + + "&response_format=" + + str(response_format.value), + } + if isinstance(entry["meta"]["partial_data_links"][property], list): + entry["meta"]["partial_data_links"][property].append(link) + else: + entry["meta"]["partial_data_links"][property] = [link] def get_entries( @@ -399,8 +401,9 @@ def get_partial_entry( entry_id: str, request: Request, params: Union[PartialDataQueryParams], -) -> dict: +) -> Union[dict, Response]: # from optimade.server.routers import ENTRY_COLLECTIONS + from optimade.adapters.jsonl import to_jsonl params.check_params(request.query_params) params.filter = f'parent_id="{entry_id}"' @@ -443,16 +446,23 @@ def get_partial_entry( if fields or include_fields: results = handle_response_fields(results, fields, include_fields)[0] # type: ignore[assignment] - # todo make the implementation of these formats more universal. i.e. allow them for other endpoint as well, - - sliceobj = [] - for i in array.shape: - sliceobj.append({"start": 1, "stop": i, "step": 1}) + slice_obj = [] + for i, size in enumerate(array.shape): + slice_obj.append( + { + "start": property_ranges[i]["start"], + "stop": min( + size * property_ranges[i]["step"] + property_ranges[i]["start"] - 1, + property_ranges[i]["stop"], + ), + "step": property_ranges[i]["step"], + } + ) header = { "optimade-partial-data": {"format": "1.2.0"}, "layout": "dense", "property_name": params.response_fields, - "returned_ranges": sliceobj, + "returned_ranges": slice_obj, # "entry": {"id": entry_id, "type": None}, #Todo add type information to metadata entry "has_references": False, } # Todo: add support for non_dense data @@ -480,7 +490,7 @@ def get_partial_entry( # included=included, ) - jsonl_content = [header, array.tolist()] + jsonl_content = [header] + [array[i].tolist() for i in range(array.shape[0])] if more_data_available: jsonl_content.append(next_link) return Response( From 870018d3f4424a5a54e11718f9e98dfa936c08b8 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Sun, 24 Sep 2023 14:46:22 +0200 Subject: [PATCH 81/86] Expanded model partial_data. From da113327d8e4b2a57474ef721e060b304e9ab0b6 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Sun, 24 Sep 2023 14:46:22 +0200 Subject: [PATCH 82/86] Expanded model partial_data. From 61361b836ab0221bd26c87a6879e1ac18528e42d Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Sun, 24 Sep 2023 19:28:18 +0200 Subject: [PATCH 83/86] Clarified why field is named describedby and not described_by. --- optimade/models/partial_data.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/optimade/models/partial_data.py b/optimade/models/partial_data.py index 26e440d01..8630545ff 100644 --- a/optimade/models/partial_data.py +++ b/optimade/models/partial_data.py @@ -13,8 +13,9 @@ class LinksObject(BaseModel): None, description="""The base URL of the implementation serving the database to which this property belongs.""", ) - # todo should it not be item_described_by? check with json Api they may have defined this field name. - item_describedby: Optional[str] = OptimadeField( + item_describedby: Optional[ + str + ] = OptimadeField( # The term describedby is used in the json Api, therefore we do not place an underscore between described and by. None, description="""A URL to an external JSON Schema that validates the data lines of the response. The format and requirements on this schema are the same as for the inline schema field :field:`item_schema`. From e4c22930a0cc91545c53dac394702a4d0713b8cb Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Thu, 19 Oct 2023 11:09:56 +0200 Subject: [PATCH 84/86] Removed unneccesary empty line --- openapi/openapi.json | 50 ++++++++++++++++++++--------------------- optimade/server/main.py | 1 - 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/openapi/openapi.json b/openapi/openapi.json index 443ff9e36..24f5e8a35 100644 --- a/openapi/openapi.json +++ b/openapi/openapi.json @@ -2,7 +2,7 @@ "openapi": "3.1.0", "info": { "title": "OPTIMADE API", - "description": "The [Open Databases Integration for Materials Design (OPTIMADE) consortium](https://www.optimade.org/) aims to make materials databases interoperational by developing a common REST API.\n\nThis specification is generated using [`optimade-python-tools`](https://github.com/Materials-Consortia/optimade-python-tools/tree/v0.25.3) v0.25.3.", + "description": "The [Open Databases Integration for Materials Design (OPTIMADE) consortium](https://www.optimade.org/) aims to make materials databases interoperational by developing a common REST API.\nThis specification is generated using [`optimade-python-tools`](https://github.com/Materials-Consortia/optimade-python-tools/tree/v0.25.3) v0.25.3.", "version": "1.1.0" }, "paths": { @@ -3586,8 +3586,8 @@ "title": "Mass", "description": "If present MUST be a list of floats expressed in a.m.u.\nElements denoting vacancies MUST have masses equal to 0.", "x-optimade-support": "optional", - "x-optimade-unit": "a.m.u.", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "x-optimade-unit": "a.m.u." }, "original_name": { "type": "string", @@ -3749,9 +3749,9 @@ "format": "date-time", "title": "Last Modified", "description": "Date and time representing when the entry was last modified.\n\n- **Type**: timestamp.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - **Response**: REQUIRED in the response unless the query parameter `response_fields` is present and does not include this property.\n\n- **Example**:\n - As part of JSON response format: `\"2007-04-05T14:30:20Z\"` (i.e., encoded as an [RFC 3339 Internet Date/Time Format](https://tools.ietf.org/html/rfc3339#section-5.6) string.)", - "nullable": true, "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "nullable": true }, "elements": { "items": { @@ -3760,17 +3760,17 @@ "type": "array", "title": "Elements", "description": "The chemical symbols of the different elements present in the structure.\n\n- **Type**: list of strings.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The strings are the chemical symbols, i.e., either a single uppercase letter or an uppercase letter followed by a number of lowercase letters.\n - The order MUST be alphabetical.\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements_ratios`, if the latter is provided.\n - Note: This property SHOULD NOT contain the string \"X\" to indicate non-chemical elements or \"vacancy\" to indicate vacancies (in contrast to the field `chemical_symbols` for the `species` property).\n\n- **Examples**:\n - `[\"Si\"]`\n - `[\"Al\",\"O\",\"Si\"]`\n\n- **Query examples**:\n - A filter that matches all records of structures that contain Si, Al **and** O, and possibly other elements: `elements HAS ALL \"Si\", \"Al\", \"O\"`.\n - To match structures with exactly these three elements, use `elements HAS ALL \"Si\", \"Al\", \"O\" AND elements LENGTH 3`.\n - Note: length queries on this property can be equivalently formulated by filtering on the `nelements`_ property directly.", - "nullable": true, "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "nullable": true }, "nelements": { "type": "integer", "title": "Nelements", "description": "Number of different elements in the structure as an integer.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - MUST be equal to the lengths of the list properties `elements` and `elements_ratios`, if they are provided.\n\n- **Examples**:\n - `3`\n\n- **Querying**:\n - Note: queries on this property can equivalently be formulated using `elements LENGTH`.\n - A filter that matches structures that have exactly 4 elements: `nelements=4`.\n - A filter that matches structures that have between 2 and 7 elements: `nelements>=2 AND nelements<=7`.", - "nullable": true, "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "nullable": true }, "elements_ratios": { "items": { @@ -3779,26 +3779,26 @@ "type": "array", "title": "Elements Ratios", "description": "Relative proportions of different elements in the structure.\n\n- **Type**: list of floats\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - Composed by the proportions of elements in the structure as a list of floating point numbers.\n - The sum of the numbers MUST be 1.0 (within floating point accuracy)\n - MUST refer to the same elements in the same order, and therefore be of the same length, as `elements`, if the latter is provided.\n\n- **Examples**:\n - `[1.0]`\n - `[0.3333333333333333, 0.2222222222222222, 0.4444444444444444]`\n\n- **Query examples**:\n - Note: Useful filters can be formulated using the set operator syntax for correlated values.\n However, since the values are floating point values, the use of equality comparisons is generally inadvisable.\n - OPTIONAL: a filter that matches structures where approximately 1/3 of the atoms in the structure are the element Al is: `elements:elements_ratios HAS ALL \"Al\":>0.3333, \"Al\":<0.3334`.", - "nullable": true, "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "nullable": true }, "chemical_formula_descriptive": { "type": "string", "title": "Chemical Formula Descriptive", "description": "The chemical formula for a structure as a string in a form chosen by the API implementation.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The chemical formula is given as a string consisting of properly capitalized element symbols followed by integers or decimal numbers, balanced parentheses, square, and curly brackets `(`,`)`, `[`,`]`, `{`, `}`, commas, the `+`, `-`, `:` and `=` symbols. The parentheses are allowed to be followed by a number. Spaces are allowed anywhere except within chemical symbols. The order of elements and any groupings indicated by parentheses or brackets are chosen freely by the API implementation.\n - The string SHOULD be arithmetically consistent with the element ratios in the `chemical_formula_reduced` property.\n - It is RECOMMENDED, but not mandatory, that symbols, parentheses and brackets, if used, are used with the meanings prescribed by [IUPAC's Nomenclature of Organic Chemistry](https://www.qmul.ac.uk/sbcs/iupac/bibliog/blue.html).\n\n- **Examples**:\n - `\"(H2O)2 Na\"`\n - `\"NaCl\"`\n - `\"CaCO3\"`\n - `\"CCaO3\"`\n - `\"(CH3)3N+ - [CH2]2-OH = Me3N+ - CH2 - CH2OH\"`\n\n- **Query examples**:\n - Note: the free-form nature of this property is likely to make queries on it across different databases inconsistent.\n - A filter that matches an exactly given formula: `chemical_formula_descriptive=\"(H2O)2 Na\"`.\n - A filter that does a partial match: `chemical_formula_descriptive CONTAINS \"H2O\"`.", - "nullable": true, "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "nullable": true }, "chemical_formula_reduced": { "type": "string", "pattern": "(^$)|^([A-Z][a-z]?([2-9]|[1-9]\\d+)?)+$", "title": "Chemical Formula Reduced", "description": "The reduced chemical formula for a structure as a string with element symbols and integer chemical proportion numbers.\nThe proportion number MUST be omitted if it is 1.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property.\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\n Intricate queries on formula components are instead suggested to be formulated using set-type filter operators on the multi valued `elements` and `elements_ratios` properties.\n - Element symbols MUST have proper capitalization (e.g., `\"Si\"`, not `\"SI\"` for \"silicon\").\n - Elements MUST be placed in alphabetical order, followed by their integer chemical proportion number.\n - For structures with no partial occupation, the chemical proportion numbers are the smallest integers for which the chemical proportion is exactly correct.\n - For structures with partial occupation, the chemical proportion numbers are integers that within reasonable approximation indicate the correct chemical proportions. The precise details of how to perform the rounding is chosen by the API implementation.\n - No spaces or separators are allowed.\n\n- **Examples**:\n - `\"H2NaO\"`\n - `\"ClNa\"`\n - `\"CCaO3\"`\n\n- **Query examples**:\n - A filter that matches an exactly given formula is `chemical_formula_reduced=\"H2NaO\"`.", - "nullable": true, "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "nullable": true }, "chemical_formula_hill": { "type": "string", @@ -3813,9 +3813,9 @@ "pattern": "(^$)|^([A-Z][a-z]?([2-9]|[1-9]\\d+)?)+$", "title": "Chemical Formula Anonymous", "description": "The anonymous formula is the `chemical_formula_reduced`, but where the elements are instead first ordered by their chemical proportion number, and then, in order left to right, replaced by anonymous symbols A, B, C, ..., Z, Aa, Ba, ..., Za, Ab, Bb, ... and so on.\n\n- **Type**: string\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property.\n However, support for filters using partial string matching with this property is OPTIONAL (i.e., BEGINS WITH, ENDS WITH, and CONTAINS).\n\n- **Examples**:\n - `\"A2B\"`\n - `\"A42B42C16D12E10F9G5\"`\n\n- **Querying**:\n - A filter that matches an exactly given formula is `chemical_formula_anonymous=\"A2B\"`.", - "nullable": true, "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "nullable": true }, "dimension_types": { "items": { @@ -3834,9 +3834,9 @@ "type": "integer", "title": "Nperiodic Dimensions", "description": "An integer specifying the number of periodic dimensions in the structure, equivalent to the number of non-zero entries in `dimension_types`.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n - The integer value MUST be between 0 and 3 inclusive and MUST be equal to the sum of the items in the `dimension_types` property.\n - This property only reflects the treatment of the lattice vectors provided for the structure, and not any physical interpretation of the dimensionality of its contents.\n\n- **Examples**:\n - `2` should be indicated in cases where `dimension_types` is any of `[1, 1, 0]`, `[1, 0, 1]`, `[0, 1, 1]`.\n\n- **Query examples**:\n - Match only structures with exactly 3 periodic dimensions: `nperiodic_dimensions=3`\n - Match all structures with 2 or fewer periodic dimensions: `nperiodic_dimensions<=2`", - "nullable": true, "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "nullable": true }, "lattice_vectors": { "items": { @@ -3878,9 +3878,9 @@ "type": "integer", "title": "Nsites", "description": "An integer specifying the length of the `cartesian_site_positions` property.\n\n- **Type**: integer\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: MUST be a queryable property with support for all mandatory filter features.\n\n- **Examples**:\n - `42`\n\n- **Query examples**:\n - Match only structures with exactly 4 sites: `nsites=4`\n - Match structures that have between 2 and 7 sites: `nsites>=2 AND nsites<=7`", - "nullable": true, "x-optimade-support": "should", - "x-optimade-queryable": "must" + "x-optimade-queryable": "must", + "nullable": true }, "species": { "items": { @@ -3889,9 +3889,9 @@ "type": "array", "title": "Species", "description": "A list describing the species of the sites of this structure.\nSpecies can represent pure chemical elements, virtual-crystal atoms representing a statistical occupation of a given site by multiple chemical elements, and/or a location to which there are attached atoms, i.e., atoms whose precise location are unknown beyond that they are attached to that position (frequently used to indicate hydrogen atoms attached to another element, e.g., a carbon with three attached hydrogens might represent a methyl group, -CH3).\n\n- **Type**: list of dictionary with keys:\n - `name`: string (REQUIRED)\n - `chemical_symbols`: list of strings (REQUIRED)\n - `concentration`: list of float (REQUIRED)\n - `attached`: list of strings (REQUIRED)\n - `nattached`: list of integers (OPTIONAL)\n - `mass`: list of floats (OPTIONAL)\n - `original_name`: string (OPTIONAL).\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - Each list member MUST be a dictionary with the following keys:\n - **name**: REQUIRED; gives the name of the species; the **name** value MUST be unique in the `species` list;\n - **chemical_symbols**: REQUIRED; MUST be a list of strings of all chemical elements composing this species.\n Each item of the list MUST be one of the following:\n - a valid chemical-element symbol, or\n - the special value `\"X\"` to represent a non-chemical element, or\n - the special value `\"vacancy\"` to represent that this site has a non-zero probability of having a vacancy (the respective probability is indicated in the `concentration` list, see below).\n\n If any one entry in the `species` list has a `chemical_symbols` list that is longer than 1 element, the correct flag MUST be set in the list `structure_features`.\n\n - **concentration**: REQUIRED; MUST be a list of floats, with same length as `chemical_symbols`.\n The numbers represent the relative concentration of the corresponding chemical symbol in this species.\n The numbers SHOULD sum to one. Cases in which the numbers do not sum to one typically fall only in the following two categories:\n\n - Numerical errors when representing float numbers in fixed precision, e.g. for two chemical symbols with concentrations `1/3` and `2/3`, the concentration might look something like `[0.33333333333, 0.66666666666]`. If the client is aware that the sum is not one because of numerical precision, it can renormalize the values so that the sum is exactly one.\n - Experimental errors in the data present in the database. In this case, it is the responsibility of the client to decide how to process the data.\n\n Note that concentrations are uncorrelated between different sites (even of the same species).\n\n - **attached**: OPTIONAL; if provided MUST be a list of length 1 or more of strings of chemical symbols for the elements attached to this site, or \"X\" for a non-chemical element.\n\n - **nattached**: OPTIONAL; if provided MUST be a list of length 1 or more of integers indicating the number of attached atoms of the kind specified in the value of the `attached` key.\n\n The implementation MUST include either both or none of the `attached` and `nattached` keys, and if they are provided, they MUST be of the same length.\n Furthermore, if they are provided, the `structure_features` property MUST include the string `site_attachments`.\n\n - **mass**: OPTIONAL. If present MUST be a list of floats, with the same length as `chemical_symbols`, providing element masses expressed in a.m.u.\n Elements denoting vacancies MUST have masses equal to 0.\n\n - **original_name**: OPTIONAL. Can be any valid Unicode string, and SHOULD contain (if specified) the name of the species that is used internally in the source database.\n\n Note: With regards to \"source database\", we refer to the immediate source being queried via the OPTIMADE API implementation.\n\n The main use of this field is for source databases that use species names, containing characters that are not allowed (see description of the list property `species_at_sites`).\n\n - For systems that have only species formed by a single chemical symbol, and that have at most one species per chemical symbol, SHOULD use the chemical symbol as species name (e.g., `\"Ti\"` for titanium, `\"O\"` for oxygen, etc.)\n However, note that this is OPTIONAL, and client implementations MUST NOT assume that the key corresponds to a chemical symbol, nor assume that if the species name is a valid chemical symbol, that it represents a species with that chemical symbol.\n This means that a species `{\"name\": \"C\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]}` is valid and represents a titanium species (and *not* a carbon species).\n - It is NOT RECOMMENDED that a structure includes species that do not have at least one corresponding site.\n\n- **Examples**:\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\"], \"concentration\": [1.0]} ]`: any site with this species is occupied by a Ti atom.\n - `[ {\"name\": \"Ti\", \"chemical_symbols\": [\"Ti\", \"vacancy\"], \"concentration\": [0.9, 0.1]} ]`: any site with this species is occupied by a Ti atom with 90 % probability, and has a vacancy with 10 % probability.\n - `[ {\"name\": \"BaCa\", \"chemical_symbols\": [\"vacancy\", \"Ba\", \"Ca\"], \"concentration\": [0.05, 0.45, 0.5], \"mass\": [0.0, 137.327, 40.078]} ]`: any site with this species is occupied by a Ba atom with 45 % probability, a Ca atom with 50 % probability, and by a vacancy with 5 % probability. The mass of this site is (on average) 88.5 a.m.u.\n - `[ {\"name\": \"C12\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [12.0]} ]`: any site with this species is occupied by a carbon isotope with mass 12.\n - `[ {\"name\": \"C13\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"mass\": [13.0]} ]`: any site with this species is occupied by a carbon isotope with mass 13.\n - `[ {\"name\": \"CH3\", \"chemical_symbols\": [\"C\"], \"concentration\": [1.0], \"attached\": [\"H\"], \"nattached\": [3]} ]`: any site with this species is occupied by a methyl group, -CH3, which is represented without specifying precise positions of the hydrogen atoms.", - "nullable": true, "x-optimade-support": "should", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "nullable": true }, "species_at_sites": { "items": { @@ -3900,9 +3900,9 @@ "type": "array", "title": "Species At Sites", "description": "Name of the species at each site (where values for sites are specified with the same order of the property `cartesian_site_positions`).\nThe properties of the species are found in the property `species`.\n\n- **Type**: list of strings.\n\n- **Requirements/Conventions**:\n - **Support**: SHOULD be supported by all implementations, i.e., SHOULD NOT be `null`.\n - **Query**: Support for queries on this property is OPTIONAL.\n If supported, filters MAY support only a subset of comparison operators.\n - MUST have length equal to the number of sites in the structure (first dimension of the list property `cartesian_site_positions`).\n - Each species name mentioned in the `species_at_sites` list MUST be described in the list property `species` (i.e. for each value in the `species_at_sites` list there MUST exist exactly one dictionary in the `species` list with the `name` attribute equal to the corresponding `species_at_sites` value).\n - Each site MUST be associated only to a single species.\n **Note**: However, species can represent mixtures of atoms, and multiple species MAY be defined for the same chemical element.\n This latter case is useful when different atoms of the same type need to be grouped or distinguished, for instance in simulation codes to assign different initial spin states.\n\n- **Examples**:\n - `[\"Ti\",\"O2\"]` indicates that the first site is hosting a species labeled `\"Ti\"` and the second a species labeled `\"O2\"`.\n - `[\"Ac\", \"Ac\", \"Ag\", \"Ir\"]` indicating the first two sites contains the `\"Ac\"` species, while the third and fourth sites contain the `\"Ag\"` and `\"Ir\"` species, respectively.", - "nullable": true, "x-optimade-support": "should", - "x-optimade-queryable": "optional" + "x-optimade-queryable": "optional", + "nullable": true }, "assemblies": { "items": { diff --git a/optimade/server/main.py b/optimade/server/main.py index a1f0f98db..a13184e10 100644 --- a/optimade/server/main.py +++ b/optimade/server/main.py @@ -50,7 +50,6 @@ title="OPTIMADE API", description=( f"""The [Open Databases Integration for Materials Design (OPTIMADE) consortium](https://www.optimade.org/) aims to make materials databases interoperational by developing a common REST API. - This specification is generated using [`optimade-python-tools`](https://github.com/Materials-Consortia/optimade-python-tools/tree/v{__version__}) v{__version__}.""" ), version=__api_version__, From e04387fe9dad1860e427cd296e38899c96f61b8b Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Thu, 19 Oct 2023 11:49:36 +0200 Subject: [PATCH 85/86] Somehow the line setting the filepointer to 0 has gone missing. --- optimade/server/main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/optimade/server/main.py b/optimade/server/main.py index a13184e10..f6acf9c7c 100644 --- a/optimade/server/main.py +++ b/optimade/server/main.py @@ -100,6 +100,7 @@ def read_array_header(fobj): "name": numpy_meta[2].name, "itemsize": numpy_meta[2].itemsize, } + f.seek(0) partial_data_coll.insert([{"data": f, "filename": filename, "metadata": metadata}]) # type: ignore[list-item] # Todo : Perhaps this can be reduced to a single insert statement. def load_entries(endpoint_name: str, endpoint_collection: EntryCollection): From bf2945916f97e2efcd0895e2101884ebe9692f08 Mon Sep 17 00:00:00 2001 From: Johan Bergsma <29785380+JPBergsma@users.noreply.github.com> Date: Thu, 19 Oct 2023 14:38:27 +0200 Subject: [PATCH 86/86] Skip partial_data tests for elastic search for which it has not been implemented yet. --- tests/server/routers/test_partial_data.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/tests/server/routers/test_partial_data.py b/tests/server/routers/test_partial_data.py index 5f0773d85..01e44a477 100644 --- a/tests/server/routers/test_partial_data.py +++ b/tests/server/routers/test_partial_data.py @@ -1,8 +1,15 @@ +import pytest + from optimade.models import PartialDataResponse +from optimade.server.config import CONFIG from ..utils import NoJsonEndpointTests +@pytest.mark.skipif( + CONFIG.database_backend.value not in ("mongomock", "mongodb"), + reason="At the moment partial data is only supported for the MongoDB backend", +) class TestPartialDataEndpoint(NoJsonEndpointTests): """Tests for /partial_data/""" @@ -12,6 +19,10 @@ class TestPartialDataEndpoint(NoJsonEndpointTests): response_cls = PartialDataResponse +@pytest.mark.skipif( + CONFIG.database_backend.value not in ("mongomock", "mongodb"), + reason="At the moment partial data is only supported for the MongoDB backend", +) def test_property_ranges_link(get_good_response, client): test_id = "mpf_551" params = "response_fields=cartesian_site_positions&property_ranges=dim_sites:2:74:1,dim_cartesian_dimensions:1:3:1&response_format=json" @@ -21,9 +32,12 @@ def test_property_ranges_link(get_good_response, client): ) # todo expand test to check content better. +@pytest.mark.skipif( + CONFIG.database_backend.value not in ("mongomock", "mongodb"), + reason="At the moment partial data is only supported for the MongoDB backend", +) def test_wrong_id_partial_data(check_error_response, client): - """If a non-supported versioned base URL is passed, `553 Version Not Supported` should be returned - + """ A specific JSON response should also occur. """ test_id = "mpf_486"