-
Notifications
You must be signed in to change notification settings - Fork 7
Update TableConfigCollection to add GemdQuery config generator #967
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| __version__ = "3.7.1" | ||
| __version__ = "3.8.0" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -72,7 +72,7 @@ def get_type(cls, data) -> Type[Serializable]: | |
| raise ValueError("Can only get types from dicts with a 'type' key") | ||
| types: List[Type[Serializable]] = [ | ||
| TerminalMaterialInfo, AttributeByTemplate, AttributeByTemplateAfterProcessTemplate, | ||
| AttributeByTemplateAndObjectTemplate, LocalAttribute, | ||
| AttributeByTemplateAndObjectTemplate, LocalAttribute, LocalAttributeAndObject, | ||
| IngredientIdentifierByProcessTemplateAndName, IngredientLabelByProcessAndName, | ||
| IngredientLabelsSetByProcessAndName, IngredientQuantityByProcessAndName, | ||
| TerminalMaterialIdentifier, AttributeInOutput, | ||
|
|
@@ -339,6 +339,63 @@ def __init__(self, | |
| self.type_selector = type_selector | ||
|
|
||
|
|
||
| class LocalAttributeAndObject(Serializable['LocalAttributeAndObject'], Variable): | ||
| """[ALPHA] Attribute marked by an attribute template for the root of a material history tree. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| name: str | ||
| a short human-readable name to use when referencing the variable | ||
| headers: list[str] | ||
| sequence of column headers | ||
| template: Union[UUID, str, LinkByUID, AttributeTemplate] | ||
| attribute template that identifies the attribute to assign to the variable | ||
| object_template: Union[UUID, str, LinkByUID, AttributeTemplate] | ||
| attribute template that identifies the attribute to assign to the variable | ||
| attribute_constraints: List[Tuple[Union[UUID, str, LinkByUID, AttributeTemplate], Bounds]] | ||
| Optional | ||
| constraints on object attributes in the target object that must be satisfied. Constraints | ||
| are expressed as Bounds. Attributes are expressed with links. The attribute that the | ||
| variable is being set to may be the target of a constraint as well. | ||
| type_selector: DataObjectTypeSelector | ||
| strategy for selecting data object types to consider when matching, defaults to PREFER_RUN | ||
|
|
||
| """ | ||
|
|
||
| name = properties.String('name') | ||
| headers = properties.List(properties.String, 'headers') | ||
| template = properties.Object(LinkByUID, 'template') | ||
| object_template = properties.Object(LinkByUID, 'object_template') | ||
| attribute_constraints = properties.Optional( | ||
| properties.List( | ||
|
Comment on lines
+369
to
+370
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't disagree, but every existing Variable in the SDK uses this pattern. |
||
| properties.SpecifiedMixedList( | ||
| [properties.Object(LinkByUID), properties.Object(BaseBounds)] | ||
| ) | ||
| ), 'attribute_constraints') | ||
| type_selector = properties.Enumeration(DataObjectTypeSelector, "type_selector") | ||
| typ = properties.String('type', default="local_attribute_and_object", deserializable=False) | ||
|
|
||
| attribute_type = Union[UUID, str, LinkByUID, AttributeTemplate] | ||
| object_type = Union[UUID, str, LinkByUID, BaseTemplate] | ||
| constraint_type = Tuple[attribute_type, BaseBounds] | ||
|
|
||
| def __init__(self, | ||
| name: str, | ||
| *, | ||
| headers: List[str], | ||
| template: attribute_type, | ||
| object_template: object_type, | ||
| attribute_constraints: Optional[List[constraint_type]] = None, | ||
| type_selector: DataObjectTypeSelector = DataObjectTypeSelector.PREFER_RUN): | ||
| self.name = name | ||
| self.headers = headers | ||
| self.template = _make_link_by_uid(template) | ||
| self.object_template = _make_link_by_uid(object_template) | ||
| self.attribute_constraints = None if attribute_constraints is None \ | ||
| else [(_make_link_by_uid(x[0]), x[1]) for x in attribute_constraints] | ||
| self.type_selector = type_selector | ||
|
|
||
|
|
||
| class IngredientIdentifierByProcessTemplateAndName( | ||
| Serializable['IngredientIdentifierByProcessAndName'], Variable): | ||
| """Ingredient identifier associated with a process template and a name. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,16 +13,18 @@ | |
| from citrine._serialization import properties | ||
| from citrine._session import Session | ||
| from citrine._utils.functions import format_escaped_url, _pad_positional_args | ||
| from citrine.resources.dataset import DatasetCollection | ||
| from citrine.resources.data_concepts import CITRINE_SCOPE, _make_link_by_uid | ||
| from citrine.resources.process_template import ProcessTemplate | ||
| from citrine.gemd_queries.gemd_query import GemdQuery | ||
| from citrine.gemtables.columns import Column, MeanColumn, IdentityColumn, OriginalUnitsColumn, \ | ||
| ConcatColumn | ||
| from citrine.gemtables.rows import Row | ||
| from citrine.gemtables.variables import Variable, IngredientIdentifierByProcessTemplateAndName, \ | ||
| IngredientQuantityByProcessAndName, IngredientQuantityDimension, \ | ||
| IngredientIdentifierInOutput, IngredientQuantityInOutput, \ | ||
| from citrine.gemtables.variables import ( | ||
| Variable, IngredientIdentifierByProcessTemplateAndName, IngredientQuantityByProcessAndName, | ||
| IngredientQuantityDimension, IngredientIdentifierInOutput, IngredientQuantityInOutput, | ||
| IngredientLabelsSetByProcessAndName, IngredientLabelsSetInOutput | ||
| ) | ||
|
|
||
| from typing import TYPE_CHECKING | ||
| if TYPE_CHECKING: # pragma: no cover | ||
|
|
@@ -53,6 +55,17 @@ class TableConfigInitiator(BaseEnumeration): | |
| UI = "UI" | ||
|
|
||
|
|
||
| class TableFromGemdQueryAlgorithm(BaseEnumeration): | ||
| """The algorithm to use in automatically building a Table Configuration. | ||
|
|
||
| * UNSPECIFIED corresponds to initial default state; includes bubbling up process attributes | ||
| * MULTISTEP_MATERIALS corresponds keeping all attributes local to a material node / row | ||
| """ | ||
|
|
||
| UNSPECIFIED = "unspecified" | ||
| MULTISTEP_MATERIALS = "multistep_materials" | ||
|
|
||
|
|
||
| class TableConfig(Resource["TableConfig"]): | ||
| """ | ||
| The Table Configuration used to build GEM Tables. | ||
|
|
@@ -73,7 +86,8 @@ class TableConfig(Resource["TableConfig"]): | |
| Column definitions, which describe how the variables are shaped into the table | ||
| gemd_query: Optional[GemdQuery] | ||
| The query used to define the materials underpinning this table | ||
|
|
||
| generation_algorithm: TableFromGemdQueryAlgorithm | ||
| Which algorithm was used to generate the config based on the GemdQuery results | ||
| """ | ||
|
|
||
| # FIXME (DML): rename this (this is dependent on the server side) | ||
|
|
@@ -100,15 +114,27 @@ def _get_dups(lst: List) -> List: | |
| rows = properties.List(properties.Object(Row), "rows") | ||
| columns = properties.List(properties.Object(Column), "columns") | ||
| gemd_query = properties.Optional(properties.Object(GemdQuery), "gemd_query") | ||
|
|
||
| def __init__(self, name: str, *, description: str, datasets: List[UUID], | ||
| variables: List[Variable], rows: List[Row], columns: List[Column]): | ||
| generation_algorithm = properties.Optional( | ||
| properties.Enumeration(TableFromGemdQueryAlgorithm), "generation_algorithm" | ||
| ) | ||
|
|
||
| def __init__(self, name: str, | ||
| *, | ||
| description: str, | ||
| datasets: List[UUID], | ||
| variables: List[Variable], | ||
| rows: List[Row], | ||
| columns: List[Column], | ||
| gemd_query: GemdQuery = None, | ||
| generation_algorithm: Optional[TableFromGemdQueryAlgorithm] = None): | ||
| self.name = name | ||
| self.description = description | ||
| self.datasets = datasets | ||
| self.rows = rows | ||
| self.variables = variables | ||
| self.columns = columns | ||
| self.gemd_query = gemd_query | ||
| self.generation_algorithm = generation_algorithm | ||
|
|
||
| # Note that these validations only apply at construction time. The current intended usage | ||
| # is for this object to be created holistically; if changed, then these will need | ||
|
|
@@ -461,8 +487,9 @@ def get_for_table(self, table: "GemTable") -> TableConfig: # noqa: F821 | |
|
|
||
| """ | ||
| # the route to fetch the config is built off the display table route tree | ||
| path = (f'projects/{self.project_id}/display-tables/{table.uid}/versions/{table.version}' | ||
| '/definition') | ||
| path = format_escaped_url( | ||
| 'projects/{}/display-tables/{}/versions/{}/definition', | ||
| self.project_id, table.uid, table.version) | ||
| data = self.session.get_resource(path) | ||
| return self.build(data) | ||
|
|
||
|
|
@@ -538,6 +565,65 @@ def default_for_material( | |
| ambiguous = [(Variable.build(v), Column.build(c)) for v, c in data['ambiguous']] | ||
| return config, ambiguous | ||
|
|
||
| def from_query( | ||
| self, | ||
| gemd_query: GemdQuery, | ||
| *, | ||
| name: str = None, | ||
| description: str = None, | ||
| algorithm: Optional[TableFromGemdQueryAlgorithm] = None, | ||
| register_config: bool = False | ||
| ) -> Tuple[TableConfig, List[Tuple[Variable, Column]]]: | ||
| """ | ||
| Build a TableConfig based on the results of a database query. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| gemd_query: GemdQuery | ||
| What content should end up in the table | ||
| name: str, optional | ||
| The name for the table config. Defaults to autogenerated message. | ||
| description: str, optional | ||
| The description of the table config. Defaults to autogenerated message. | ||
| algorithm: TableBuildAlgorithm, optional | ||
| The algorithm to use in generating a Table Configuration from the sample material | ||
| history. If unspecified, uses the webservice's default. | ||
| register_config: bool, optional | ||
| Whether to register the config | ||
|
|
||
| Returns | ||
| ------- | ||
| List[Tuple[Variable, Column]] | ||
| A table config as well as addition variables/columns which would result in | ||
| ambiguous matches if included in the config. | ||
|
|
||
| """ | ||
| if name is None: | ||
| collection = DatasetCollection( | ||
| session=self.session, | ||
| team_id=self.team_id | ||
| ) | ||
| name = (f"Automatic Table for Dataset: " | ||
| f"{', '.join([collection.get(x).name for x in gemd_query.datasets])}") | ||
|
|
||
| params = {"name": name} | ||
| if description is not None: | ||
| params['description'] = description | ||
| if algorithm is not None: | ||
| params['algorithm'] = algorithm | ||
| data = self.session.post_resource( | ||
| format_escaped_url('teams/{}/table-configs/from-query', self.team_id), | ||
| params=params, | ||
| json=gemd_query.dump() | ||
| ) | ||
| config = TableConfig.build(data['config']) | ||
| ambiguous = [(Variable.build(v), Column.build(c)) for v, c in data['ambiguous']] | ||
|
|
||
| if register_config: | ||
| return self.register(config), ambiguous | ||
| else: | ||
| return config, ambiguous | ||
|
|
||
| def preview(self, *, | ||
| table_config: TableConfig, | ||
| preview_materials: List[LinkByUID] = None | ||
|
|
@@ -552,7 +638,7 @@ def preview(self, *, | |
| List of links to the material runs to use as terminal materials in the preview | ||
|
|
||
| """ | ||
| path = path = format_escaped_url( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice catch |
||
| path = format_escaped_url( | ||
| "teams/{}/ara-definitions/preview", | ||
| self.team_id | ||
| ) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just double-checking, Here we are not mapping
attribute_template_type, I do see it in the openapi spec, is this because we are not going to use it on the SDK?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is in the openapi spec, but that field is not populated by any of the existing Variable objects in the SDK. For the life of me, I don't see what impact that field is supposed to have, based on digging through platform-backed.