From 97078e0459b34a82e0e31024541d559e97abbe36 Mon Sep 17 00:00:00 2001 From: Kevin Kim Date: Tue, 9 Aug 2022 13:13:34 -0700 Subject: [PATCH 01/26] Add global_key to data_row --- labelbox/schema/data_row.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/labelbox/schema/data_row.py b/labelbox/schema/data_row.py index fa5ace8f9..4c7bb8287 100644 --- a/labelbox/schema/data_row.py +++ b/labelbox/schema/data_row.py @@ -17,6 +17,7 @@ class DataRow(DbObject, Updateable, BulkDeletable): Attributes: external_id (str): User-generated file name or identifier + global_key (str): User-generated globally unique identifier row_data (str): Paths to local files are uploaded to Labelbox's server. Otherwise, it's treated as an external URL. updated_at (datetime) @@ -33,6 +34,7 @@ class DataRow(DbObject, Updateable, BulkDeletable): attachments (Relationship) `ToMany` relationship with AssetAttachment """ external_id = Field.String("external_id") + global_key = Field.String("global_key") row_data = Field.String("row_data") updated_at = Field.DateTime("updated_at") created_at = Field.DateTime("created_at") From 44b892dddaaa51a153d1d8cf6f716f477c202009 Mon Sep 17 00:00:00 2001 From: Matt Sokoloff Date: Mon, 15 Aug 2022 14:32:45 -0400 Subject: [PATCH 02/26] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23ee0e72d..4f079ddee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -# Version 3.26.0 (2022-08-12) +# Version 3.26.0 (2022-08-15) ## Added * `Batch.delete()` which will delete an existing `Batch` * `Batch.delete_labels()` which will delete all `Label`’s created after a `Project`’s mode has been set to batch. From d35b8679384f579b8e356cb1aed08a43bb56bd12 Mon Sep 17 00:00:00 2001 From: Mateo Marin Date: Wed, 10 Aug 2022 18:03:55 +0800 Subject: [PATCH 03/26] Update config [AL-3061] --- labelbox/schema/model_run.py | 39 +++++++++++++++++++ .../integration/annotation_import/conftest.py | 12 ++++++ .../annotation_import/test_model_run.py | 18 +++++++++ 3 files changed, 69 insertions(+) diff --git a/labelbox/schema/model_run.py b/labelbox/schema/model_run.py index 39e22548b..f0c903702 100644 --- a/labelbox/schema/model_run.py +++ b/labelbox/schema/model_run.py @@ -262,6 +262,45 @@ def update_status(self, }, experimental=True) + @experimental + def update_config(self, + config: Dict[str, Any]): + data: Dict[str, Any] = {'config': config} + res = self.client.execute( + """mutation updateModelRunConfigPyApi($modelRunId: ID!, $data: UpdateModelRunConfigInput!){ + updateModelRunConfig(modelRun: {id : $modelRunId}, data: $data){trainingMetadata} + } + """, { + 'modelRunId': self.uid, + 'data': data + }, + experimental=True) + return res["updateModelRunConfig"] + + @experimental + def reset_config(self): + res = self.client.execute( + """mutation resetModelRunConfigPyApi($modelRunId: ID!){ + resetModelRunConfig(modelRun: {id : $modelRunId}){trainingMetadata} + } + """, { + 'modelRunId': self.uid + }, + experimental=True) + return res["resetModelRunConfig"] + + @experimental + def config(self): + res = self.client.execute( + """query ModelRunPyApi($modelRunId: ID!){ + modelRun(where: {id : $modelRunId}){trainingMetadata} + } + """, { + 'modelRunId': self.uid + }, + experimental=True) + return res["modelRun"] + @experimental def export_labels( self, diff --git a/tests/integration/annotation_import/conftest.py b/tests/integration/annotation_import/conftest.py index c00df1ce3..bfec487ca 100644 --- a/tests/integration/annotation_import/conftest.py +++ b/tests/integration/annotation_import/conftest.py @@ -359,6 +359,18 @@ def model_run(rand_gen, model): # Already was deleted by the test pass +@pytest.fixture +def model_run_with_training_metadata(rand_gen, model): + name = rand_gen(str) + training_metadata = {"batch_size": 1000} + model_run = model.create_model_run(name, training_metadata) + yield model_run + try: + model_run.delete() + except: + # Already was deleted by the test + pass + @pytest.fixture def model_run_with_model_run_data_rows(client, configured_project, diff --git a/tests/integration/annotation_import/test_model_run.py b/tests/integration/annotation_import/test_model_run.py index e99490f0d..ffa367a24 100644 --- a/tests/integration/annotation_import/test_model_run.py +++ b/tests/integration/annotation_import/test_model_run.py @@ -56,6 +56,24 @@ def test_model_run_delete(client, model_run): assert len(before) == len(after) + 1 +def test_model_run_update_config(model_run_with_training_metadata): + new_config = {"batch_size": 2000} + res = model_run_with_training_metadata.update_config(new_config) + assert res["trainingMetadata"]["batchSize"] == new_config["batch_size"] + + +def test_model_run_reset_config(model_run_with_training_metadata): + res = model_run_with_training_metadata.reset_config() + assert res["trainingMetadata"] == None + + +def test_model_run_fetch_config(model_run_with_training_metadata): + new_config = {"batch_size": 2000} + model_run_with_training_metadata.update_config(new_config) + res = model_run_with_training_metadata.config() + assert res["trainingMetadata"]["batchSize"] == new_config["batch_size"] + + def test_model_run_data_rows_delete(client, model_run_with_model_run_data_rows): models = list(client.get_models()) model = models[0] From 6ce03667fad4bdbc9e1ee786de66f3474eb696d5 Mon Sep 17 00:00:00 2001 From: Mateo Marin Date: Thu, 11 Aug 2022 01:50:18 +0800 Subject: [PATCH 04/26] Changelog update --- labelbox/schema/model_run.py | 2 +- tests/integration/annotation_import/test_model_run.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/labelbox/schema/model_run.py b/labelbox/schema/model_run.py index f0c903702..1fd2f3baa 100644 --- a/labelbox/schema/model_run.py +++ b/labelbox/schema/model_run.py @@ -290,7 +290,7 @@ def reset_config(self): return res["resetModelRunConfig"] @experimental - def config(self): + def fetch_config(self): res = self.client.execute( """query ModelRunPyApi($modelRunId: ID!){ modelRun(where: {id : $modelRunId}){trainingMetadata} diff --git a/tests/integration/annotation_import/test_model_run.py b/tests/integration/annotation_import/test_model_run.py index ffa367a24..9ee02fb85 100644 --- a/tests/integration/annotation_import/test_model_run.py +++ b/tests/integration/annotation_import/test_model_run.py @@ -70,7 +70,7 @@ def test_model_run_reset_config(model_run_with_training_metadata): def test_model_run_fetch_config(model_run_with_training_metadata): new_config = {"batch_size": 2000} model_run_with_training_metadata.update_config(new_config) - res = model_run_with_training_metadata.config() + res = model_run_with_training_metadata.fetch_config() assert res["trainingMetadata"]["batchSize"] == new_config["batch_size"] From 9b1a5f01c8de1b9b61519bd6374aee8fc66873d0 Mon Sep 17 00:00:00 2001 From: Mateo Marin Date: Thu, 11 Aug 2022 01:51:18 +0800 Subject: [PATCH 05/26] formatting --- labelbox/schema/model_run.py | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/labelbox/schema/model_run.py b/labelbox/schema/model_run.py index 1fd2f3baa..d3ebb3eb9 100644 --- a/labelbox/schema/model_run.py +++ b/labelbox/schema/model_run.py @@ -263,8 +263,7 @@ def update_status(self, experimental=True) @experimental - def update_config(self, - config: Dict[str, Any]): + def update_config(self, config: Dict[str, Any]): data: Dict[str, Any] = {'config': config} res = self.client.execute( """mutation updateModelRunConfigPyApi($modelRunId: ID!, $data: UpdateModelRunConfigInput!){ @@ -275,7 +274,7 @@ def update_config(self, 'data': data }, experimental=True) - return res["updateModelRunConfig"] + return res["updateModelRunConfig"] @experimental def reset_config(self): @@ -283,23 +282,18 @@ def reset_config(self): """mutation resetModelRunConfigPyApi($modelRunId: ID!){ resetModelRunConfig(modelRun: {id : $modelRunId}){trainingMetadata} } - """, { - 'modelRunId': self.uid - }, + """, {'modelRunId': self.uid}, experimental=True) - return res["resetModelRunConfig"] - + return res["resetModelRunConfig"] + @experimental def fetch_config(self): - res = self.client.execute( - """query ModelRunPyApi($modelRunId: ID!){ + res = self.client.execute("""query ModelRunPyApi($modelRunId: ID!){ modelRun(where: {id : $modelRunId}){trainingMetadata} } - """, { - 'modelRunId': self.uid - }, - experimental=True) - return res["modelRun"] + """, {'modelRunId': self.uid}, + experimental=True) + return res["modelRun"] @experimental def export_labels( From 87103dfbfd050c9bdc683b6289cdbda18b57032f Mon Sep 17 00:00:00 2001 From: Mateo Marin Date: Thu, 11 Aug 2022 01:56:34 +0800 Subject: [PATCH 06/26] more formatting --- tests/integration/annotation_import/conftest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/integration/annotation_import/conftest.py b/tests/integration/annotation_import/conftest.py index bfec487ca..108c55910 100644 --- a/tests/integration/annotation_import/conftest.py +++ b/tests/integration/annotation_import/conftest.py @@ -359,6 +359,7 @@ def model_run(rand_gen, model): # Already was deleted by the test pass + @pytest.fixture def model_run_with_training_metadata(rand_gen, model): name = rand_gen(str) From cd49e9b81ffeef1ccd0230cb0e7908abf942840c Mon Sep 17 00:00:00 2001 From: Mateo Marin Date: Mon, 15 Aug 2022 13:11:53 +0800 Subject: [PATCH 07/26] uses is None --- tests/integration/annotation_import/test_model_run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/annotation_import/test_model_run.py b/tests/integration/annotation_import/test_model_run.py index 9ee02fb85..8b3a8aa97 100644 --- a/tests/integration/annotation_import/test_model_run.py +++ b/tests/integration/annotation_import/test_model_run.py @@ -64,7 +64,7 @@ def test_model_run_update_config(model_run_with_training_metadata): def test_model_run_reset_config(model_run_with_training_metadata): res = model_run_with_training_metadata.reset_config() - assert res["trainingMetadata"] == None + assert res["trainingMetadata"] is None def test_model_run_fetch_config(model_run_with_training_metadata): From fd82d25d3c1f9dc2157bce2bfdc0654fa18f7082 Mon Sep 17 00:00:00 2001 From: Mateo Marin Date: Wed, 17 Aug 2022 15:27:21 +0800 Subject: [PATCH 08/26] Addresses review comments --- CHANGELOG.md | 17 ++++++++++---- labelbox/schema/model_run.py | 23 ++++++++++++++++--- .../annotation_import/test_model_run.py | 4 ++-- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f079ddee..3c91d3c5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,19 @@ * `RAW_TEXT` and `TEXT_FILE` attachment types to replace the `TEXT` type. # Version 3.25.3 (2022-08-10) + +### Added +* `ModelRun.update_config()` + * Updates model run training metadata +* `ModelRun.reset_config()` + * Resets model run training metadata +* `ModelRun.get_config()` + * Fetches model run training metadata + +### Changed +* `Model.create_model_run()` + * Add training metadata config as a model run creation param + ## Fixed * Label export will continue polling if the downloadUrl is None @@ -663,7 +676,3 @@ a `Label`. Default value is 0.0. ## Version 2.2 (2019-10-18) Changelog not maintained before version 2.2. - -### Changed -* `Model.create_model_run()` - * Add training metadata config as a model run creation param diff --git a/labelbox/schema/model_run.py b/labelbox/schema/model_run.py index d3ebb3eb9..64f87cd38 100644 --- a/labelbox/schema/model_run.py +++ b/labelbox/schema/model_run.py @@ -263,7 +263,14 @@ def update_status(self, experimental=True) @experimental - def update_config(self, config: Dict[str, Any]): + def update_config(self, config: Dict[str, Any]) -> Dict[str, Any]: + """ + Updates the model run's training metadata config + Args: + config (dict): A dictionary of keys and values + Returns: + model run id and updated training metadata + """ data: Dict[str, Any] = {'config': config} res = self.client.execute( """mutation updateModelRunConfigPyApi($modelRunId: ID!, $data: UpdateModelRunConfigInput!){ @@ -277,7 +284,12 @@ def update_config(self, config: Dict[str, Any]): return res["updateModelRunConfig"] @experimental - def reset_config(self): + def reset_config(self) -> Dict[str, Any]: + """ + Resets model run's training metadata config + Returns: + Model run id and reset training metadata + """ res = self.client.execute( """mutation resetModelRunConfigPyApi($modelRunId: ID!){ resetModelRunConfig(modelRun: {id : $modelRunId}){trainingMetadata} @@ -287,7 +299,12 @@ def reset_config(self): return res["resetModelRunConfig"] @experimental - def fetch_config(self): + def get_config(self) -> Dict[str, Any]: + """ + Gets model run's training metadata + Returns: + training metadata as a dictionary + """ res = self.client.execute("""query ModelRunPyApi($modelRunId: ID!){ modelRun(where: {id : $modelRunId}){trainingMetadata} } diff --git a/tests/integration/annotation_import/test_model_run.py b/tests/integration/annotation_import/test_model_run.py index 8b3a8aa97..096f09ab1 100644 --- a/tests/integration/annotation_import/test_model_run.py +++ b/tests/integration/annotation_import/test_model_run.py @@ -67,10 +67,10 @@ def test_model_run_reset_config(model_run_with_training_metadata): assert res["trainingMetadata"] is None -def test_model_run_fetch_config(model_run_with_training_metadata): +def test_model_run_get_config(model_run_with_training_metadata): new_config = {"batch_size": 2000} model_run_with_training_metadata.update_config(new_config) - res = model_run_with_training_metadata.fetch_config() + res = model_run_with_training_metadata.get_config() assert res["trainingMetadata"]["batchSize"] == new_config["batch_size"] From 6682046590dccca39ab6f4fde8acd1e4efdf8d4f Mon Sep 17 00:00:00 2001 From: Mateo Marin Date: Wed, 17 Aug 2022 15:32:56 +0800 Subject: [PATCH 09/26] linting --- labelbox/schema/model_run.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/labelbox/schema/model_run.py b/labelbox/schema/model_run.py index 64f87cd38..25737d1ac 100644 --- a/labelbox/schema/model_run.py +++ b/labelbox/schema/model_run.py @@ -270,7 +270,7 @@ def update_config(self, config: Dict[str, Any]) -> Dict[str, Any]: config (dict): A dictionary of keys and values Returns: model run id and updated training metadata - """ + """ data: Dict[str, Any] = {'config': config} res = self.client.execute( """mutation updateModelRunConfigPyApi($modelRunId: ID!, $data: UpdateModelRunConfigInput!){ @@ -289,7 +289,7 @@ def reset_config(self) -> Dict[str, Any]: Resets model run's training metadata config Returns: Model run id and reset training metadata - """ + """ res = self.client.execute( """mutation resetModelRunConfigPyApi($modelRunId: ID!){ resetModelRunConfig(modelRun: {id : $modelRunId}){trainingMetadata} @@ -304,7 +304,7 @@ def get_config(self) -> Dict[str, Any]: Gets model run's training metadata Returns: training metadata as a dictionary - """ + """ res = self.client.execute("""query ModelRunPyApi($modelRunId: ID!){ modelRun(where: {id : $modelRunId}){trainingMetadata} } From 539c300c154412cd59f929ad57f1ee9c390e2437 Mon Sep 17 00:00:00 2001 From: Mateo Marin Date: Thu, 18 Aug 2022 01:01:46 +0800 Subject: [PATCH 10/26] changelog section + capitalizes model run in comments --- CHANGELOG.md | 20 ++++++++++---------- labelbox/schema/model_run.py | 20 ++++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c91d3c5f..9edad2901 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,6 @@ # Changelog -# Version 3.26.0 (2022-08-15) -## Added -* `Batch.delete()` which will delete an existing `Batch` -* `Batch.delete_labels()` which will delete all `Label`’s created after a `Project`’s mode has been set to batch. - * Note: Does not include labels that were imported via model-assisted labeling or label imports -* Support for creating model config when creating a model run -* `RAW_TEXT` and `TEXT_FILE` attachment types to replace the `TEXT` type. - -# Version 3.25.3 (2022-08-10) - +# Version 3.26.1 (2022-08-18) ### Added * `ModelRun.update_config()` * Updates model run training metadata @@ -22,6 +13,15 @@ * `Model.create_model_run()` * Add training metadata config as a model run creation param +# Version 3.26.0 (2022-08-15) +## Added +* `Batch.delete()` which will delete an existing `Batch` +* `Batch.delete_labels()` which will delete all `Label`’s created after a `Project`’s mode has been set to batch. + * Note: Does not include labels that were imported via model-assisted labeling or label imports +* Support for creating model config when creating a model run +* `RAW_TEXT` and `TEXT_FILE` attachment types to replace the `TEXT` type. + +# Version 3.25.3 (2022-08-10) ## Fixed * Label export will continue polling if the downloadUrl is None diff --git a/labelbox/schema/model_run.py b/labelbox/schema/model_run.py index 25737d1ac..d9f2fcf33 100644 --- a/labelbox/schema/model_run.py +++ b/labelbox/schema/model_run.py @@ -42,7 +42,7 @@ class Status(Enum): FAILED = "FAILED" def upsert_labels(self, label_ids, timeout_seconds=60): - """ Adds data rows and labels to a model run + """ Adds data rows and labels to a Model Run Args: label_ids (list): label ids to insert timeout_seconds (float): Max waiting time, in seconds. @@ -75,7 +75,7 @@ def upsert_labels(self, label_ids, timeout_seconds=60): timeout_seconds=timeout_seconds) def upsert_data_rows(self, data_row_ids, timeout_seconds=60): - """ Adds data rows to a model run without any associated labels + """ Adds data rows to a Model Run without any associated labels Args: data_row_ids (list): data row ids to add to mea timeout_seconds (float): Max waiting time, in seconds. @@ -167,7 +167,7 @@ def model_run_data_rows(self): ['annotationGroups', 'pageInfo', 'endCursor']) def delete(self): - """ Deletes specified model run. + """ Deletes specified Model Run. Returns: Query execution success. @@ -178,10 +178,10 @@ def delete(self): self.client.execute(query_str, {ids_param: str(self.uid)}) def delete_model_run_data_rows(self, data_row_ids: List[str]): - """ Deletes data rows from model runs. + """ Deletes data rows from Model Runs. Args: - data_row_ids (list): List of data row ids to delete from the model run. + data_row_ids (list): List of data row ids to delete from the Model Run. Returns: Query execution success. """ @@ -265,11 +265,11 @@ def update_status(self, @experimental def update_config(self, config: Dict[str, Any]) -> Dict[str, Any]: """ - Updates the model run's training metadata config + Updates the Model Run's training metadata config Args: config (dict): A dictionary of keys and values Returns: - model run id and updated training metadata + Model Run id and updated training metadata """ data: Dict[str, Any] = {'config': config} res = self.client.execute( @@ -286,9 +286,9 @@ def update_config(self, config: Dict[str, Any]) -> Dict[str, Any]: @experimental def reset_config(self) -> Dict[str, Any]: """ - Resets model run's training metadata config + Resets Model Run's training metadata config Returns: - Model run id and reset training metadata + Model Run id and reset training metadata """ res = self.client.execute( """mutation resetModelRunConfigPyApi($modelRunId: ID!){ @@ -301,7 +301,7 @@ def reset_config(self) -> Dict[str, Any]: @experimental def get_config(self) -> Dict[str, Any]: """ - Gets model run's training metadata + Gets Model Run's training metadata Returns: training metadata as a dictionary """ From ef5c4503a9469639399a678f935dd8c08db7c01b Mon Sep 17 00:00:00 2001 From: jtsodapop <67922677+jtsodapop@users.noreply.github.com> Date: Wed, 17 Aug 2022 15:39:18 -0400 Subject: [PATCH 11/26] initial pass at creation of conversion from export to a labelbox format --- .../data/serialization/labelbox_v1/objects.py | 53 +++++++++++-------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/labelbox/data/serialization/labelbox_v1/objects.py b/labelbox/data/serialization/labelbox_v1/objects.py index 77fed2cd6..1896b01ca 100644 --- a/labelbox/data/serialization/labelbox_v1/objects.py +++ b/labelbox/data/serialization/labelbox_v1/objects.py @@ -259,33 +259,40 @@ def from_common(cls, text_entity: TextEntity, **extra) +class LBV1DocumentRectangle(LBV1Rectangle): + unit: str + page: int + + class LBV1Objects(BaseModel): - objects: List[Union[LBV1Line, LBV1Point, LBV1Polygon, LBV1Rectangle, - LBV1TextEntity, LBV1Mask, LBV1TIPoint, LBV1TILine, - LBV1TIPolygon, LBV1TIRectangle]] + objects: List[Union[LBV1DocumentRectangle, LBV1Line, LBV1Point, LBV1Polygon, + LBV1Rectangle, LBV1TextEntity, LBV1Mask, LBV1TIPoint, + LBV1TILine, LBV1TIPolygon, LBV1TIRectangle,]] def to_common(self) -> List[ObjectAnnotation]: objects = [ - ObjectAnnotation(value=obj.to_common(), - classifications=[ - ClassificationAnnotation( - value=cls.to_common(), - feature_schema_id=cls.schema_id, - name=cls.title, - extra={ - 'feature_id': cls.feature_id, - 'title': cls.title, - 'value': cls.value - }) for cls in obj.classifications - ], - name=obj.title, - feature_schema_id=obj.schema_id, - extra={ - 'instanceURI': obj.instanceURI, - 'color': obj.color, - 'feature_id': obj.feature_id, - 'value': obj.value, - }) for obj in self.objects + ObjectAnnotation( + value=obj.to_common(), + classifications=[ + ClassificationAnnotation(value=cls.to_common(), + feature_schema_id=cls.schema_id, + name=cls.title, + extra={ + 'feature_id': cls.feature_id, + 'title': cls.title, + 'value': cls.value + }) for cls in obj.classifications + ], + name=obj.title, + feature_schema_id=obj.schema_id, + extra={ + 'instanceURI': obj.instanceURI, + 'color': obj.color, + 'feature_id': obj.feature_id, + 'value': obj.value, + 'page': obj.page if hasattr(obj, 'page') else None, + 'unit': obj.unit if hasattr(obj, 'unit') else None, + }) for obj in self.objects ] return objects From dc39c79d85130f0ded0dfa4933467e1baaf696ce Mon Sep 17 00:00:00 2001 From: Mateo Marin Date: Thu, 18 Aug 2022 11:46:01 +0800 Subject: [PATCH 12/26] template date --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9edad2901..0ec1e4505 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -# Version 3.26.1 (2022-08-18) +# Version 0.0.0 (YYYY-MM-DD) ### Added * `ModelRun.update_config()` * Updates model run training metadata From 59b05c2480bfbc4b0bd58a4b1131bf8b2b0536cf Mon Sep 17 00:00:00 2001 From: Mateo Marin Date: Thu, 18 Aug 2022 12:44:25 +0800 Subject: [PATCH 13/26] no more camelcase in config --- tests/integration/annotation_import/test_model_run.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/integration/annotation_import/test_model_run.py b/tests/integration/annotation_import/test_model_run.py index 096f09ab1..a9964c42e 100644 --- a/tests/integration/annotation_import/test_model_run.py +++ b/tests/integration/annotation_import/test_model_run.py @@ -17,7 +17,7 @@ def test_model_run(client, configured_project_with_label, rand_gen): config = {"batch_size": 100, "reruns": None} model_run = model.create_model_run(name, config) assert model_run.name == name - assert model_run.training_metadata["batchSize"] == config["batch_size"] + assert model_run.training_metadata["batch_size"] == config["batch_size"] assert model_run.training_metadata["reruns"] == config["reruns"] assert model_run.model_id == model.uid assert model_run.created_by_id == client.get_user().uid @@ -59,7 +59,7 @@ def test_model_run_delete(client, model_run): def test_model_run_update_config(model_run_with_training_metadata): new_config = {"batch_size": 2000} res = model_run_with_training_metadata.update_config(new_config) - assert res["trainingMetadata"]["batchSize"] == new_config["batch_size"] + assert res["trainingMetadata"]["batch_size"] == new_config["batch_size"] def test_model_run_reset_config(model_run_with_training_metadata): @@ -71,7 +71,7 @@ def test_model_run_get_config(model_run_with_training_metadata): new_config = {"batch_size": 2000} model_run_with_training_metadata.update_config(new_config) res = model_run_with_training_metadata.get_config() - assert res["trainingMetadata"]["batchSize"] == new_config["batch_size"] + assert res["trainingMetadata"]["batch_size"] == new_config["batch_size"] def test_model_run_data_rows_delete(client, model_run_with_model_run_data_rows): From cc2462ca3b31ea877be1d9b43f15597ae951145c Mon Sep 17 00:00:00 2001 From: Mateo Marin Date: Thu, 18 Aug 2022 14:03:08 +0800 Subject: [PATCH 14/26] test fix --- tests/integration/annotation_import/test_model_run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/annotation_import/test_model_run.py b/tests/integration/annotation_import/test_model_run.py index a9964c42e..167d294a3 100644 --- a/tests/integration/annotation_import/test_model_run.py +++ b/tests/integration/annotation_import/test_model_run.py @@ -17,7 +17,7 @@ def test_model_run(client, configured_project_with_label, rand_gen): config = {"batch_size": 100, "reruns": None} model_run = model.create_model_run(name, config) assert model_run.name == name - assert model_run.training_metadata["batch_size"] == config["batch_size"] + assert model_run.training_metadata["batchSize"] == config["batch_size"] assert model_run.training_metadata["reruns"] == config["reruns"] assert model_run.model_id == model.uid assert model_run.created_by_id == client.get_user().uid From 392806cbcd6dc7e36f8e877781fc2d4a88fc87b7 Mon Sep 17 00:00:00 2001 From: jtsodapop <67922677+jtsodapop@users.noreply.github.com> Date: Thu, 18 Aug 2022 10:12:05 -0400 Subject: [PATCH 15/26] refactor to move two new fields into LBV1ObjectBase --- .../data/serialization/labelbox_v1/objects.py | 57 +++++++++---------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/labelbox/data/serialization/labelbox_v1/objects.py b/labelbox/data/serialization/labelbox_v1/objects.py index 1896b01ca..62a5bd485 100644 --- a/labelbox/data/serialization/labelbox_v1/objects.py +++ b/labelbox/data/serialization/labelbox_v1/objects.py @@ -22,6 +22,8 @@ class LBV1ObjectBase(LBV1Feature): instanceURI: Optional[str] = None classifications: List[Union[LBV1Text, LBV1Radio, LBV1Dropdown, LBV1Checklist]] = [] + page: Optional[int] = None + unit: Optional[str] = None def dict(self, *args, **kwargs) -> Dict[str, Any]: res = super().dict(*args, **kwargs) @@ -259,40 +261,35 @@ def from_common(cls, text_entity: TextEntity, **extra) -class LBV1DocumentRectangle(LBV1Rectangle): - unit: str - page: int - - class LBV1Objects(BaseModel): - objects: List[Union[LBV1DocumentRectangle, LBV1Line, LBV1Point, LBV1Polygon, - LBV1Rectangle, LBV1TextEntity, LBV1Mask, LBV1TIPoint, - LBV1TILine, LBV1TIPolygon, LBV1TIRectangle,]] + objects: List[Union[LBV1Line, LBV1Point, LBV1Polygon, LBV1Rectangle, + LBV1TextEntity, LBV1Mask, LBV1TIPoint, LBV1TILine, + LBV1TIPolygon, LBV1TIRectangle,]] def to_common(self) -> List[ObjectAnnotation]: objects = [ - ObjectAnnotation( - value=obj.to_common(), - classifications=[ - ClassificationAnnotation(value=cls.to_common(), - feature_schema_id=cls.schema_id, - name=cls.title, - extra={ - 'feature_id': cls.feature_id, - 'title': cls.title, - 'value': cls.value - }) for cls in obj.classifications - ], - name=obj.title, - feature_schema_id=obj.schema_id, - extra={ - 'instanceURI': obj.instanceURI, - 'color': obj.color, - 'feature_id': obj.feature_id, - 'value': obj.value, - 'page': obj.page if hasattr(obj, 'page') else None, - 'unit': obj.unit if hasattr(obj, 'unit') else None, - }) for obj in self.objects + ObjectAnnotation(value=obj.to_common(), + classifications=[ + ClassificationAnnotation( + value=cls.to_common(), + feature_schema_id=cls.schema_id, + name=cls.title, + extra={ + 'feature_id': cls.feature_id, + 'title': cls.title, + 'value': cls.value + }) for cls in obj.classifications + ], + name=obj.title, + feature_schema_id=obj.schema_id, + extra={ + 'instanceURI': obj.instanceURI, + 'color': obj.color, + 'feature_id': obj.feature_id, + 'value': obj.value, + 'page': obj.page, + 'unit': obj.unit, + }) for obj in self.objects ] return objects From 294f14d504c97b2593f8963922bb079915701227 Mon Sep 17 00:00:00 2001 From: jtsodapop <67922677+jtsodapop@users.noreply.github.com> Date: Thu, 18 Aug 2022 11:29:01 -0400 Subject: [PATCH 16/26] addition of new test case for pdf documents --- .../data/serialization/labelbox_v1/label.py | 1 + test.py | 117 ++++++++++++++++ tests/data/assets/labelbox_v1/pdf_export.json | 130 ++++++++++++++++++ .../labelbox_v1/test_document.py | 49 +++++++ 4 files changed, 297 insertions(+) create mode 100644 test.py create mode 100644 tests/data/assets/labelbox_v1/pdf_export.json create mode 100644 tests/data/serialization/labelbox_v1/test_document.py diff --git a/labelbox/data/serialization/labelbox_v1/label.py b/labelbox/data/serialization/labelbox_v1/label.py index 1e976deae..b143c3636 100644 --- a/labelbox/data/serialization/labelbox_v1/label.py +++ b/labelbox/data/serialization/labelbox_v1/label.py @@ -146,6 +146,7 @@ class LBV1Label(BaseModel): skipped: Optional[bool] = Extra('Skipped') media_type: Optional[str] = Extra('media_type') data_split: Optional[str] = Extra('Data Split') + global_key: Optional[str] = Extra('Global Key') def to_common(self) -> Label: if isinstance(self.label, list): diff --git a/test.py b/test.py new file mode 100644 index 000000000..7a0ed70a3 --- /dev/null +++ b/test.py @@ -0,0 +1,117 @@ +import base64 +import time +import os +from datetime import datetime, timezone +import uuid +import json +import logging +# import cuid +import random +from pprint import pprint + +import requests + +from labelbox import Client, Project, DataRow, Label +from labelbox.data.serialization.labelbox_v1 import LBV1Converter +from labelbox.schema.annotation_import import LabelImport, MALPredictionImport +from labelbox.schema.labeling_frontend import LabelingFrontend +from labelbox.schema.ontology import Classification, OntologyBuilder, Ontology, Tool, Option + +# logging.basicConfig(level=logging.DEBUG) + +# ____________________________________________________________________________________ +"""HELPER FUNCTIONS""" + + +def cleanup_my_org(): + from datetime import datetime, timezone + date = datetime.strptime("2022-07-15", + "%Y-%m-%d").replace(tzinfo=timezone.utc) + + for project in client.get_projects(): + if project.created_at > date: + print(project.name) + project.delete() + for dataset in client.get_datasets(): + if dataset.created_at > date: + print(dataset.name) + dataset.delete() + # for model in client.get_models(): + # model.delete() + + +def get_lb_client(environment: str = "prod"): + if environment == "prod": + API_KEY = os.environ.get('apikey') + client = Client(API_KEY) + elif environment == "staging": + API_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbDN1OXJ3NzIwMDlvMHl4a2ViOHhkdml0Iiwib3JnYW5pemF0aW9uSWQiOiJjbDN1OXJ3Nm4wMDluMHl4azYzczVnNjZwIiwiYXBpS2V5SWQiOiJjbDZldGg2Zm0wZnBtMHkxZWJpazY2bTVlIiwic2VjcmV0IjoiYjhmNjcwZTFkYjdkODNhYzdkYzYzYjQzMjE5MTBkODQiLCJpYXQiOjE2NTk2MDQxNzYsImV4cCI6MjI5MDc1NjE3Nn0.zCYnfXQEQl8PwsJbsBvP3s_cDA-hQbiFcNgIy82uOrQ" + client = Client(API_KEY, endpoint="https://api.lb-stage.xyz/graphql") + elif environment == "local": + API_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbDI5M2N2OWUwMDBicnRvdTBneWgyZ3RvIiwib3JnYW5pemF0aW9uSWQiOiJjbDI5M2N2NjAwMDBhcnRvdTl0M3JhMGpzIiwiYXBpS2V5SWQiOiJjbDN2c21icWgwMDBhbjdvdWFldWMwMmcyIiwic2VjcmV0IjoiYTQ4MmZjODU4OGU5YmI0NmJhZDU2YjljZDBhZDcyZTUiLCJpYXQiOjE2NTQxMDAzMTUsImV4cCI6MjI4NTI1MjMxNX0.UJC2uF8Cu6WwBSIZGUZUY8UznP7RCRsG4ns616OFXjI" + client = Client(API_KEY, endpoint='http://localhost:8080/graphql') + else: + print("Invalid environment") + exit(1) + return client + + +# ____________________________________________________________________________________ + +os.system('clear') + +client = get_lb_client("prod") + +# ____________________________________________________________________________________ + +project = client.get_project("cl6xntneb7t28072bggdydv7a") +# organization_id = client.get_organization().uid +# dataset = client.create_dataset(name="hello world") +# dataset = client.get_dataset("cl6xy8bcw0g7v07068u9t5hiv") +# print(dataset.uid) +# file_path = "/Users/jonathantso/Downloads/sample_batch.txt" +# dataset.create_data_rows(file_path) +# ____________________________________________________________________________________ +# annotations = [] + +# rows = list(project.batches())[0].export_data_rows() + +# for row in project.export_queued_data_rows(include_metadata=True): +# print(f"row: {row['id']}, {row['externalId']}") +# for i in range(4): +# annotations.append({ +# "uuid": str(uuid.uuid4()), +# "name": "boxy", +# "dataRow": {"id": row['id']}, +# "bbox": {"top": round(random.uniform(0,300),2), "left": round(random.uniform(0,300),2), "height": round(random.uniform(200,500),2), "width": round(random.uniform(0,200),2)}, +# "unit": "POINTS", +# "page": random.randint(0,9) +# }) + +# import_annotations = MALPredictionImport.create_from_objects(client=client, project_id = project.uid, name=f"import {str(uuid.uuid4())}", predictions=annotations) +# import_annotations.wait_until_done() + +# print(f"\nerrors: {import_annotations.errors}") +#assert should be that import_annotations.errors == [] +#____________________________________________________________________________________ +# labels = project.label_generator() +with open("/Users/jonathantso/Downloads/export-2022-08-17T18_37_30.233Z.json", + "r") as f: + labels = json.load(f) + print("\nnow deserializing..\n") + labels = LBV1Converter.deserialize(labels) + # for label in labels: + # if label.annotations: + # for obj in label.annotations: + # print(f"\n\t{obj}") + + print("\nnow serializing..\n") + labels = LBV1Converter.serialize(labels) + # for label in labels: + # print("serialized") + # print(label, "\n") + labels = LBV1Converter.deserialize(labels) + for label in labels: + if label.annotations: + for obj in label.annotations: + print(f"\n\t{obj}") diff --git a/tests/data/assets/labelbox_v1/pdf_export.json b/tests/data/assets/labelbox_v1/pdf_export.json new file mode 100644 index 000000000..0fc253e63 --- /dev/null +++ b/tests/data/assets/labelbox_v1/pdf_export.json @@ -0,0 +1,130 @@ +[{ + "ID": "cl6xnzi4a7ldn0729381g7104", + "DataRow ID": "cl6xnv9h61fv0085yhtoq06ht", + "Labeled Data": "https://storage.labelbox.com/ckcz6bubudyfi0855o1dt1g9s%2F4cef4e08-e13d-8a5e-fbbf-c7624babb490-Airbnb_%20Labelbox%20-%20Focus%20on%20Workforce%20-%20Labelbox%20Labeling%20Operations%20(1).pdf?Expires=1661971050348&KeyName=labelbox-assets-key-3&Signature=JK6ral5CXF7T9Q5LaQqKvJy5A2A", + "Label": { + "objects": [{ + "featureId": "cl6xnzjpq0dmr07yocs2vfot8", + "schemaId": "cl6xnuwt95lqq07330tbb3mfd", + "color": "#1CE6FF", + "title": "boxy", + "value": "boxy", + "bbox": { + "top": 144.68, + "left": 107.84, + "height": 441.6, + "width": 9.48 + }, + "page": 0, + "unit": "POINTS", + "instanceURI": "https://api.labelbox.com/masks/feature/cl6xnzjpq0dmr07yocs2vfot8?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJja2NjOWZtbXc0aGNkMDczOHFpeWM2YW54Iiwib3JnYW5pemF0aW9uSWQiOiJja2N6NmJ1YnVkeWZpMDg1NW8xZHQxZzlzIiwiaWF0IjoxNjYwNzYxNDUwLCJleHAiOjE2NjMzNTM0NTB9.X4-j6zee8o685PUrL9C6oC2m6TayKuJQHhN8iLgG8kI" + }, { + "featureId": "cl6xnzjpq0dms07yobwv68gxf", + "schemaId": "cl6xnuwt95lqq07330tbb3mfd", + "color": "#1CE6FF", + "title": "boxy", + "value": "boxy", + "bbox": { + "top": 162.73, + "left": 32.45, + "height": 388.17, + "width": 101.66 + }, + "page": 4, + "unit": "POINTS", + "instanceURI": "https://api.labelbox.com/masks/feature/cl6xnzjpq0dms07yobwv68gxf?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJja2NjOWZtbXc0aGNkMDczOHFpeWM2YW54Iiwib3JnYW5pemF0aW9uSWQiOiJja2N6NmJ1YnVkeWZpMDg1NW8xZHQxZzlzIiwiaWF0IjoxNjYwNzYxNDUwLCJleHAiOjE2NjMzNTM0NTB9.X4-j6zee8o685PUrL9C6oC2m6TayKuJQHhN8iLgG8kI" + }, { + "featureId": "cl6xnzjpq0dmt07yo8pp45gru", + "schemaId": "cl6xnuwt95lqq07330tbb3mfd", + "color": "#1CE6FF", + "title": "boxy", + "value": "boxy", + "bbox": { + "top": 223.26, + "left": 251.42, + "height": 457.04, + "width": 186.78 + }, + "page": 7, + "unit": "POINTS", + "instanceURI": "https://api.labelbox.com/masks/feature/cl6xnzjpq0dmt07yo8pp45gru?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJja2NjOWZtbXc0aGNkMDczOHFpeWM2YW54Iiwib3JnYW5pemF0aW9uSWQiOiJja2N6NmJ1YnVkeWZpMDg1NW8xZHQxZzlzIiwiaWF0IjoxNjYwNzYxNDUwLCJleHAiOjE2NjMzNTM0NTB9.X4-j6zee8o685PUrL9C6oC2m6TayKuJQHhN8iLgG8kI" + }, { + "featureId": "cl6xnzjpq0dmu07yo2qik0en4", + "schemaId": "cl6xnuwt95lqq07330tbb3mfd", + "color": "#1CE6FF", + "title": "boxy", + "value": "boxy", + "bbox": { + "top": 32.52, + "left": 218.17, + "height": 231.73, + "width": 110.56 + }, + "page": 6, + "unit": "POINTS", + "instanceURI": "https://api.labelbox.com/masks/feature/cl6xnzjpq0dmu07yo2qik0en4?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJja2NjOWZtbXc0aGNkMDczOHFpeWM2YW54Iiwib3JnYW5pemF0aW9uSWQiOiJja2N6NmJ1YnVkeWZpMDg1NW8xZHQxZzlzIiwiaWF0IjoxNjYwNzYxNDUwLCJleHAiOjE2NjMzNTM0NTB9.X4-j6zee8o685PUrL9C6oC2m6TayKuJQHhN8iLgG8kI" + }, { + "featureId": "cl6xnzjpq0dmv07yo7phz7ofz", + "schemaId": "cl6xnuwt95lqq07330tbb3mfd", + "color": "#1CE6FF", + "title": "boxy", + "value": "boxy", + "bbox": { + "top": 117.39, + "left": 4.25, + "height": 456.92, + "width": 164.83 + }, + "page": 7, + "unit": "POINTS", + "instanceURI": "https://api.labelbox.com/masks/feature/cl6xnzjpq0dmv07yo7phz7ofz?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJja2NjOWZtbXc0aGNkMDczOHFpeWM2YW54Iiwib3JnYW5pemF0aW9uSWQiOiJja2N6NmJ1YnVkeWZpMDg1NW8xZHQxZzlzIiwiaWF0IjoxNjYwNzYxNDUwLCJleHAiOjE2NjMzNTM0NTB9.X4-j6zee8o685PUrL9C6oC2m6TayKuJQHhN8iLgG8kI" + }, { + "featureId": "cl6xnzjpq0dmw07yofocp6uf6", + "schemaId": "cl6xnuwt95lqq07330tbb3mfd", + "color": "#1CE6FF", + "title": "boxy", + "value": "boxy", + "bbox": { + "top": 82.13, + "left": 217.28, + "height": 279.76, + "width": 82.43 + }, + "page": 8, + "unit": "POINTS", + "instanceURI": "https://api.labelbox.com/masks/feature/cl6xnzjpq0dmw07yofocp6uf6?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJja2NjOWZtbXc0aGNkMDczOHFpeWM2YW54Iiwib3JnYW5pemF0aW9uSWQiOiJja2N6NmJ1YnVkeWZpMDg1NW8xZHQxZzlzIiwiaWF0IjoxNjYwNzYxNDUwLCJleHAiOjE2NjMzNTM0NTB9.X4-j6zee8o685PUrL9C6oC2m6TayKuJQHhN8iLgG8kI" + }, { + "featureId": "cl6xnzjpq0dmx07yo0qh40z0n", + "schemaId": "cl6xnuwt95lqq07330tbb3mfd", + "color": "#1CE6FF", + "title": "boxy", + "value": "boxy", + "bbox": { + "top": 298.12, + "left": 83.34, + "height": 203.83, + "width": 0.38 + }, + "page": 3, + "unit": "POINTS", + "instanceURI": "https://api.labelbox.com/masks/feature/cl6xnzjpq0dmx07yo0qh40z0n?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJja2NjOWZtbXc0aGNkMDczOHFpeWM2YW54Iiwib3JnYW5pemF0aW9uSWQiOiJja2N6NmJ1YnVkeWZpMDg1NW8xZHQxZzlzIiwiaWF0IjoxNjYwNzYxNDUwLCJleHAiOjE2NjMzNTM0NTB9.X4-j6zee8o685PUrL9C6oC2m6TayKuJQHhN8iLgG8kI" + }], + "classifications": [], + "relationships": [] + }, + "Created By": "jtso@labelbox.com", + "Project Name": "PDF MAL Test", + "Created At": "2022-08-17T18:37:18.000Z", + "Updated At": "2022-08-17T18:37:20.073Z", + "Seconds to Label": 15.003, + "External ID": "Airbnb_ Labelbox - Focus on Workforce - Labelbox Labeling Operations (1).pdf", + "Global Key": null, + "Agreement": -1, + "Benchmark Agreement": -1, + "Benchmark ID": null, + "Dataset Name": "PDF ", + "Reviews": [], + "View Label": "https://editor.labelbox.com?project=cl6xntneb7t28072bggdydv7a&label=cl6xnzi4a7ldn0729381g7104", + "Has Open Issues": 0, + "Skipped": false +}] \ No newline at end of file diff --git a/tests/data/serialization/labelbox_v1/test_document.py b/tests/data/serialization/labelbox_v1/test_document.py new file mode 100644 index 000000000..d1d0222d7 --- /dev/null +++ b/tests/data/serialization/labelbox_v1/test_document.py @@ -0,0 +1,49 @@ +import json +from typing import Dict, Any + +from labelbox.data.serialization.labelbox_v1.converter import LBV1Converter + +IGNORE_KEYS = [ + "Data Split", "media_type", "DataRow Metadata", "Media Attributes" +] + + +def round_dict(data: Dict[str, Any]) -> Dict[str, Any]: + print("hi", data) + for key in data: + print("me key", key) + if isinstance(data[key], float): + print("i am float", key) + data[key] = int(data[key]) + elif isinstance(data[key], dict): + print("i am dict", key) + data[key] = round_dict(data[key]) + return data + + +def test_pdf(): + """ + Tests an export from a pdf document with only bounding boxes + """ + payload = json.load( + open('tests/data/assets/labelbox_v1/pdf_export.json', 'r')) + collection = LBV1Converter.deserialize(payload) + serialized = next(LBV1Converter.serialize(collection)) + + payload = payload[0] # only one document in the export + + serialized = {k: v for k, v in serialized.items() if k not in IGNORE_KEYS} + + assert serialized.keys() == payload.keys() + for key in payload.keys(): + if key == 'Label': + serialized_no_classes = [{ + k: v for k, v in dic.items() if k != 'classifications' + } for dic in serialized[key]['objects']] + serialized_round = [ + round_dict(dic) for dic in serialized_no_classes + ] + payload_round = [round_dict(dic) for dic in payload[key]['objects']] + assert payload_round == serialized_round + else: + assert serialized[key] == payload[key] From 5588dbb92164fa6565602138c8cc0625ac27345a Mon Sep 17 00:00:00 2001 From: jtsodapop <67922677+jtsodapop@users.noreply.github.com> Date: Thu, 18 Aug 2022 11:31:30 -0400 Subject: [PATCH 17/26] Delete test.py --- test.py | 117 -------------------------------------------------------- 1 file changed, 117 deletions(-) delete mode 100644 test.py diff --git a/test.py b/test.py deleted file mode 100644 index 7a0ed70a3..000000000 --- a/test.py +++ /dev/null @@ -1,117 +0,0 @@ -import base64 -import time -import os -from datetime import datetime, timezone -import uuid -import json -import logging -# import cuid -import random -from pprint import pprint - -import requests - -from labelbox import Client, Project, DataRow, Label -from labelbox.data.serialization.labelbox_v1 import LBV1Converter -from labelbox.schema.annotation_import import LabelImport, MALPredictionImport -from labelbox.schema.labeling_frontend import LabelingFrontend -from labelbox.schema.ontology import Classification, OntologyBuilder, Ontology, Tool, Option - -# logging.basicConfig(level=logging.DEBUG) - -# ____________________________________________________________________________________ -"""HELPER FUNCTIONS""" - - -def cleanup_my_org(): - from datetime import datetime, timezone - date = datetime.strptime("2022-07-15", - "%Y-%m-%d").replace(tzinfo=timezone.utc) - - for project in client.get_projects(): - if project.created_at > date: - print(project.name) - project.delete() - for dataset in client.get_datasets(): - if dataset.created_at > date: - print(dataset.name) - dataset.delete() - # for model in client.get_models(): - # model.delete() - - -def get_lb_client(environment: str = "prod"): - if environment == "prod": - API_KEY = os.environ.get('apikey') - client = Client(API_KEY) - elif environment == "staging": - API_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbDN1OXJ3NzIwMDlvMHl4a2ViOHhkdml0Iiwib3JnYW5pemF0aW9uSWQiOiJjbDN1OXJ3Nm4wMDluMHl4azYzczVnNjZwIiwiYXBpS2V5SWQiOiJjbDZldGg2Zm0wZnBtMHkxZWJpazY2bTVlIiwic2VjcmV0IjoiYjhmNjcwZTFkYjdkODNhYzdkYzYzYjQzMjE5MTBkODQiLCJpYXQiOjE2NTk2MDQxNzYsImV4cCI6MjI5MDc1NjE3Nn0.zCYnfXQEQl8PwsJbsBvP3s_cDA-hQbiFcNgIy82uOrQ" - client = Client(API_KEY, endpoint="https://api.lb-stage.xyz/graphql") - elif environment == "local": - API_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJjbDI5M2N2OWUwMDBicnRvdTBneWgyZ3RvIiwib3JnYW5pemF0aW9uSWQiOiJjbDI5M2N2NjAwMDBhcnRvdTl0M3JhMGpzIiwiYXBpS2V5SWQiOiJjbDN2c21icWgwMDBhbjdvdWFldWMwMmcyIiwic2VjcmV0IjoiYTQ4MmZjODU4OGU5YmI0NmJhZDU2YjljZDBhZDcyZTUiLCJpYXQiOjE2NTQxMDAzMTUsImV4cCI6MjI4NTI1MjMxNX0.UJC2uF8Cu6WwBSIZGUZUY8UznP7RCRsG4ns616OFXjI" - client = Client(API_KEY, endpoint='http://localhost:8080/graphql') - else: - print("Invalid environment") - exit(1) - return client - - -# ____________________________________________________________________________________ - -os.system('clear') - -client = get_lb_client("prod") - -# ____________________________________________________________________________________ - -project = client.get_project("cl6xntneb7t28072bggdydv7a") -# organization_id = client.get_organization().uid -# dataset = client.create_dataset(name="hello world") -# dataset = client.get_dataset("cl6xy8bcw0g7v07068u9t5hiv") -# print(dataset.uid) -# file_path = "/Users/jonathantso/Downloads/sample_batch.txt" -# dataset.create_data_rows(file_path) -# ____________________________________________________________________________________ -# annotations = [] - -# rows = list(project.batches())[0].export_data_rows() - -# for row in project.export_queued_data_rows(include_metadata=True): -# print(f"row: {row['id']}, {row['externalId']}") -# for i in range(4): -# annotations.append({ -# "uuid": str(uuid.uuid4()), -# "name": "boxy", -# "dataRow": {"id": row['id']}, -# "bbox": {"top": round(random.uniform(0,300),2), "left": round(random.uniform(0,300),2), "height": round(random.uniform(200,500),2), "width": round(random.uniform(0,200),2)}, -# "unit": "POINTS", -# "page": random.randint(0,9) -# }) - -# import_annotations = MALPredictionImport.create_from_objects(client=client, project_id = project.uid, name=f"import {str(uuid.uuid4())}", predictions=annotations) -# import_annotations.wait_until_done() - -# print(f"\nerrors: {import_annotations.errors}") -#assert should be that import_annotations.errors == [] -#____________________________________________________________________________________ -# labels = project.label_generator() -with open("/Users/jonathantso/Downloads/export-2022-08-17T18_37_30.233Z.json", - "r") as f: - labels = json.load(f) - print("\nnow deserializing..\n") - labels = LBV1Converter.deserialize(labels) - # for label in labels: - # if label.annotations: - # for obj in label.annotations: - # print(f"\n\t{obj}") - - print("\nnow serializing..\n") - labels = LBV1Converter.serialize(labels) - # for label in labels: - # print("serialized") - # print(label, "\n") - labels = LBV1Converter.deserialize(labels) - for label in labels: - if label.annotations: - for obj in label.annotations: - print(f"\n\t{obj}") From 64a3bc95484d117e4116c4583afde9a2cfb31716 Mon Sep 17 00:00:00 2001 From: jtsodapop <67922677+jtsodapop@users.noreply.github.com> Date: Thu, 18 Aug 2022 11:33:16 -0400 Subject: [PATCH 18/26] remove unnecessary comments --- tests/data/serialization/labelbox_v1/test_document.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/data/serialization/labelbox_v1/test_document.py b/tests/data/serialization/labelbox_v1/test_document.py index d1d0222d7..a5a0f611e 100644 --- a/tests/data/serialization/labelbox_v1/test_document.py +++ b/tests/data/serialization/labelbox_v1/test_document.py @@ -9,14 +9,10 @@ def round_dict(data: Dict[str, Any]) -> Dict[str, Any]: - print("hi", data) for key in data: - print("me key", key) if isinstance(data[key], float): - print("i am float", key) data[key] = int(data[key]) elif isinstance(data[key], dict): - print("i am dict", key) data[key] = round_dict(data[key]) return data From b11bc139ea3c798952ebc12abe31823dd8b82ee8 Mon Sep 17 00:00:00 2001 From: jtsodapop <67922677+jtsodapop@users.noreply.github.com> Date: Thu, 18 Aug 2022 15:58:14 -0400 Subject: [PATCH 19/26] update to test image to account for new extra fields --- tests/data/serialization/labelbox_v1/test_image.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/data/serialization/labelbox_v1/test_image.py b/tests/data/serialization/labelbox_v1/test_image.py index f6da8b729..546c97f64 100644 --- a/tests/data/serialization/labelbox_v1/test_image.py +++ b/tests/data/serialization/labelbox_v1/test_image.py @@ -22,6 +22,7 @@ def test_image(file_path): # We are storing the media types now. payload['media_type'] = 'image' + payload['Global Key'] = None assert serialized.keys() == payload.keys() @@ -31,6 +32,8 @@ def test_image(file_path): elif key == 'Label': for annotation_a, annotation_b in zip(serialized[key]['objects'], payload[key]['objects']): + annotation_b['page'] = None + annotation_b['unit'] = None if not len(annotation_a['classifications']): # We don't add a classification key to the payload if there is no classifications. annotation_a.pop('classifications') From 896f96b289be0d38145b345e8f84c654908d1f6b Mon Sep 17 00:00:00 2001 From: jtsodapop <67922677+jtsodapop@users.noreply.github.com> Date: Thu, 18 Aug 2022 16:33:25 -0400 Subject: [PATCH 20/26] fix to test cases --- tests/data/serialization/labelbox_v1/test_text.py | 3 +++ tests/data/serialization/labelbox_v1/test_unknown_media.py | 3 +++ tests/data/serialization/labelbox_v1/test_video.py | 3 +++ 3 files changed, 9 insertions(+) diff --git a/tests/data/serialization/labelbox_v1/test_text.py b/tests/data/serialization/labelbox_v1/test_text.py index 1c6dfa479..bd28a6c04 100644 --- a/tests/data/serialization/labelbox_v1/test_text.py +++ b/tests/data/serialization/labelbox_v1/test_text.py @@ -10,6 +10,7 @@ def test_text(): serialized = next(LBV1Converter.serialize(collection)) payload['media_type'] = 'text' + payload['Global Key'] = None assert serialized.keys() == payload.keys() for key in serialized: @@ -18,6 +19,8 @@ def test_text(): elif key == 'Label': for annotation_a, annotation_b in zip(serialized[key]['objects'], payload[key]['objects']): + annotation_b['page'] = None + annotation_b['unit'] = None if not len(annotation_a['classifications']): # We don't add a classification key to the payload if there is no classifications. annotation_a.pop('classifications') diff --git a/tests/data/serialization/labelbox_v1/test_unknown_media.py b/tests/data/serialization/labelbox_v1/test_unknown_media.py index bd5cddd64..4607d7be3 100644 --- a/tests/data/serialization/labelbox_v1/test_unknown_media.py +++ b/tests/data/serialization/labelbox_v1/test_unknown_media.py @@ -16,6 +16,7 @@ def test_image(): for row in payload: row['media_type'] = 'image' + row['Global Key'] = None collection = LBV1Converter.deserialize(payload) for idx, serialized in enumerate(LBV1Converter.serialize(collection)): @@ -30,6 +31,8 @@ def test_image(): if not len(annotation_a['classifications']): # We don't add a classification key to the payload if there is no classifications. annotation_a.pop('classifications') + annotation_b['page'] = None + annotation_b['unit'] = None if isinstance(annotation_b.get('classifications'), list) and len( diff --git a/tests/data/serialization/labelbox_v1/test_video.py b/tests/data/serialization/labelbox_v1/test_video.py index 880e0be5f..169bef918 100644 --- a/tests/data/serialization/labelbox_v1/test_video.py +++ b/tests/data/serialization/labelbox_v1/test_video.py @@ -18,6 +18,7 @@ def test_video(): collection = LBV1Converter.deserialize([payload]) serialized = next(LBV1Converter.serialize(collection)) payload['media_type'] = 'video' + payload['Global Key'] = None assert serialized.keys() == payload.keys() for key in serialized: if key != 'Label': @@ -32,6 +33,8 @@ def test_video(): for obj_a, obj_b in zip(annotation_a['objects'], annotation_b['objects']): + obj_b['page'] = None + obj_b['unit'] = None obj_a = round_dict(obj_a) obj_b = round_dict(obj_b) assert obj_a == obj_b From 90716f48325e5ae640722feee43474ea8fc778a5 Mon Sep 17 00:00:00 2001 From: Richard Sun Date: Mon, 22 Aug 2022 15:13:18 -0700 Subject: [PATCH 21/26] [QQC-362] Document new project update restrictions regarding quality mode --- labelbox/schema/project.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/labelbox/schema/project.py b/labelbox/schema/project.py index 7ab503ec9..ffc255f59 100644 --- a/labelbox/schema/project.py +++ b/labelbox/schema/project.py @@ -93,6 +93,22 @@ class QueueMode(Enum): Dataset = "Dataset" def update(self, **kwargs): + """ Updates this project with the specified attributes + + Args: + kwargs: a dictionary containing attributes to be upserted + * Note that the quality setting cannot be changed after a project has been created. The quality mode + for a project is inferred through the following attributes: + Benchmark: + auto_audit_number_of_labels = 1 + auto_audit_percentage = 1.0 + Consensus: + auto_audit_number_of_labels > 1 + auto_audit_percentage <= 1.0 + Attempting to switch between benchmark and consensus modes is an invalid operation and will result + in an error. + + """ mode: Optional[Project.QueueMode] = kwargs.pop("queue_mode", None) if mode: self._update_queue_mode(mode) From 2d1065feb27439857331b7be76d407a40bda9695 Mon Sep 17 00:00:00 2001 From: Mateo Marin Date: Tue, 23 Aug 2022 16:19:49 +0800 Subject: [PATCH 22/26] Modify return get_config return value [AL-3352] --- labelbox/schema/model_run.py | 2 +- tests/integration/annotation_import/test_model_run.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/labelbox/schema/model_run.py b/labelbox/schema/model_run.py index d9f2fcf33..62b29c796 100644 --- a/labelbox/schema/model_run.py +++ b/labelbox/schema/model_run.py @@ -310,7 +310,7 @@ def get_config(self) -> Dict[str, Any]: } """, {'modelRunId': self.uid}, experimental=True) - return res["modelRun"] + return res["modelRun"]["trainingMetadata"] @experimental def export_labels( diff --git a/tests/integration/annotation_import/test_model_run.py b/tests/integration/annotation_import/test_model_run.py index 167d294a3..5b170f4aa 100644 --- a/tests/integration/annotation_import/test_model_run.py +++ b/tests/integration/annotation_import/test_model_run.py @@ -71,7 +71,7 @@ def test_model_run_get_config(model_run_with_training_metadata): new_config = {"batch_size": 2000} model_run_with_training_metadata.update_config(new_config) res = model_run_with_training_metadata.get_config() - assert res["trainingMetadata"]["batch_size"] == new_config["batch_size"] + assert res["batch_size"] == new_config["batch_size"] def test_model_run_data_rows_delete(client, model_run_with_model_run_data_rows): From 667e400acd470a3f086411868da6126d2384ed20 Mon Sep 17 00:00:00 2001 From: Mateo Marin Date: Tue, 23 Aug 2022 16:31:21 +0800 Subject: [PATCH 23/26] changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ec1e4505..bc0f05d41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ # Changelog # Version 0.0.0 (YYYY-MM-DD) +### Changed +* `ModelRun.get_config()` + * Modifies get_config to return un-nested Model Run config ### Added * `ModelRun.update_config()` * Updates model run training metadata From 24352e49079c792a2ca861a9bd8ba41cbbcb8f70 Mon Sep 17 00:00:00 2001 From: Mateo Marin Date: Tue, 23 Aug 2022 16:55:50 +0800 Subject: [PATCH 24/26] empty commit From d62f3b2451456c2fd6d688f82f049d04e3241ae2 Mon Sep 17 00:00:00 2001 From: Richard Sun Date: Tue, 23 Aug 2022 11:56:28 -0700 Subject: [PATCH 25/26] [QQC-362] Review feedback --- labelbox/schema/project.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/labelbox/schema/project.py b/labelbox/schema/project.py index ffc255f59..27e43f537 100644 --- a/labelbox/schema/project.py +++ b/labelbox/schema/project.py @@ -97,17 +97,17 @@ def update(self, **kwargs): Args: kwargs: a dictionary containing attributes to be upserted - * Note that the quality setting cannot be changed after a project has been created. The quality mode - for a project is inferred through the following attributes: - Benchmark: - auto_audit_number_of_labels = 1 - auto_audit_percentage = 1.0 - Consensus: - auto_audit_number_of_labels > 1 - auto_audit_percentage <= 1.0 - Attempting to switch between benchmark and consensus modes is an invalid operation and will result - in an error. + Note that the quality setting cannot be changed after a project has been created. The quality mode + for a project is inferred through the following attributes: + Benchmark: + auto_audit_number_of_labels = 1 + auto_audit_percentage = 1.0 + Consensus: + auto_audit_number_of_labels > 1 + auto_audit_percentage <= 1.0 + Attempting to switch between benchmark and consensus modes is an invalid operation and will result + in an error. """ mode: Optional[Project.QueueMode] = kwargs.pop("queue_mode", None) if mode: From e83f63ed3438d2fad1407aff92704271463159dd Mon Sep 17 00:00:00 2001 From: Kevin Kim Date: Tue, 23 Aug 2022 15:45:00 -0700 Subject: [PATCH 26/26] Prep 3.26.1 --- CHANGELOG.md | 2 +- docs/source/conf.py | 2 +- labelbox/__init__.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc0f05d41..4445a1d8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -# Version 0.0.0 (YYYY-MM-DD) +# Version 3.26.1 (2022-08-23) ### Changed * `ModelRun.get_config()` * Modifies get_config to return un-nested Model Run config diff --git a/docs/source/conf.py b/docs/source/conf.py index 694677233..f339e6045 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -21,7 +21,7 @@ copyright = '2021, Labelbox' author = 'Labelbox' -release = '3.25.1' +release = '3.26.1' # -- General configuration --------------------------------------------------- diff --git a/labelbox/__init__.py b/labelbox/__init__.py index 0efb083de..56b992c11 100644 --- a/labelbox/__init__.py +++ b/labelbox/__init__.py @@ -1,5 +1,5 @@ name = "labelbox" -__version__ = "3.26.0" +__version__ = "3.26.1" from labelbox.client import Client from labelbox.schema.project import Project