From 78792cb4fa8b57ae7719e2eb109b2a0b85b79b9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Werbrouck?= <122839953+Francois-Werbrouck@users.noreply.github.com> Date: Thu, 27 Feb 2025 16:02:40 -0500 Subject: [PATCH] 251 fertiscan as a dev i want upload date and updated at to be included in inspection for consistency (#259) * Issue #252: updated_at added to the Inspection model * Issue #252: fix lint --- .gitignore | 3 +- fertiscan/__init__.py | 15 +- fertiscan/db/metadata/inspection/__init__.py | 10 +- fertiscan/db/queries/inspection/__init__.py | 19 +- fertiscan/db/queries/organization/__init__.py | 2 +- tests/fertiscan/db/test_inspection.py | 271 +++++------------- tests/fertiscan/test_datastore.py | 35 ++- 7 files changed, 129 insertions(+), 226 deletions(-) diff --git a/.gitignore b/.gitignore index d693a8be..5e83d15b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ **/__pycache__/** -.vscode/settings.json +.vscode/** test/** .env 0.1.3.json @@ -7,4 +7,3 @@ ml_structure.json .DS_Store .venv venv -.vscode/extensions.json diff --git a/fertiscan/__init__.py b/fertiscan/__init__.py index 7b867064..a8054f3f 100644 --- a/fertiscan/__init__.py +++ b/fertiscan/__init__.py @@ -130,12 +130,14 @@ def update_inspection( "The inspection you are trying to update is already verified" ) else: - inspection.update_inspection( + updated_at = inspection.update_inspection( cursor=cursor, inspection_id=updated_data.inspection_id, verified=updated_data.verified, inspection_comment=updated_data.inspection_comment, ) + updated_data.updated_at = updated_at + self.model.updated_at = updated_at # just in case # -------- label_to_update = updated_data.product label_id = updated_data.product.label_id @@ -252,7 +254,7 @@ def update_inspection( "'Main contact organization information is required and was not found'" ) else: - organization.upsert_organization( + organization_id = organization.upsert_organization( cursor=cursor, name=main_org.name, website=main_org.website, @@ -264,12 +266,13 @@ def update_inspection( cursor=cursor, name=fertilizer_name, reg_number=registration_number_value, - org_owner_id=main_org.id, + org_owner_id=organization_id, latest_inspection_id=updated_data.inspection_id, ) else: updated_data.verified = False # ------------ + updated_data.updated_at = updated_at return data_inspection.Inspection.model_validate(updated_data) async def delete_inspection( @@ -528,10 +531,11 @@ def new_inspection( ) if flag == True: # We do this since we have no way of knowing who is the main contact + record.is_main_contact = True # We assume the first one is the main contact flag = False # Inspection - formatted_analysis.inspection_id = inspection.new_inspection( + inspection_id, upload_date = inspection.new_inspection( cursor=cursor, user_id=user_id, picture_set_id=folder_id, @@ -539,6 +543,9 @@ def new_inspection( label_id=label_info_id, container_id=container_id, ) + formatted_analysis.inspection_id = inspection_id + formatted_analysis.upload_date = upload_date + formatted_analysis.updated_at = upload_date analysis_db = data_inspection.Inspection.model_validate(formatted_analysis) inspection.save_inspection_original_dataset( cursor=cursor, diff --git a/fertiscan/db/metadata/inspection/__init__.py b/fertiscan/db/metadata/inspection/__init__.py index c4c8b70e..5b9a0cad 100644 --- a/fertiscan/db/metadata/inspection/__init__.py +++ b/fertiscan/db/metadata/inspection/__init__.py @@ -148,6 +148,8 @@ class Inspection(ValidatedModel): ingredients: ValuesObjects folder_id: UUID4 container_id: UUID4 + upload_date: Optional[datetime] = None + updated_at: Optional[datetime] = None @@ -355,7 +357,9 @@ def build_inspection_import(analysis_form: dict, user_id:UUID,folder_id:UUID,con ingredients=ingredients, folder_id=folder_id, container_id=container_id, - inspection_comment= None + inspection_comment= None, + upload_date=None, + updated_at=None, ) return inspection_formatted except MetadataError: @@ -441,7 +445,9 @@ def build_inspection_export(cursor, inspection_id) -> Inspection: verified=db_inspection.verified, ingredients=ingredients, folder_id=folder_id, - container_id=container_id + container_id=container_id, + upload_date=db_inspection.upload_date, + updated_at=db_inspection.updated_at, ) return inspection_formatted except QueryError as e: diff --git a/fertiscan/db/queries/inspection/__init__.py b/fertiscan/db/queries/inspection/__init__.py index 5cd793fc..6f400b61 100644 --- a/fertiscan/db/queries/inspection/__init__.py +++ b/fertiscan/db/queries/inspection/__init__.py @@ -5,7 +5,7 @@ import json from uuid import UUID -from datetime import datetime as Date +from datetime import datetime from psycopg import Cursor from psycopg.rows import dict_row @@ -25,7 +25,7 @@ @handle_query_errors(InspectionCreationError) def new_inspection( cursor: Cursor, user_id, picture_set_id, label_id, container_id, verified=False -): +) -> tuple[UUID, datetime]: """ This function uploads a new inspection to the database. @@ -50,11 +50,11 @@ def new_inspection( VALUES (%s, %s, %s,%s,%s) RETURNING - id + id, upload_date; """ cursor.execute(query, (user_id, picture_set_id, verified, label_id, container_id)) if result := cursor.fetchone(): - return result[0] + return result raise InspectionCreationError("Failed to create inspection. No data returned.") @@ -384,7 +384,7 @@ def get_all_organization_inspection(cursor: Cursor, org_id): def update_inspection( cursor: Cursor, inspection_id: str | UUID, verified: bool, inspection_comment: str -): +) -> datetime: if verified: query = """ UPDATE @@ -395,7 +395,9 @@ def update_inspection( inspection_comment = %s, verified_date = CURRENT_TIMESTAMP WHERE - id = %s; + id = %s + RETURNING + updated_at; """ else: query = """ @@ -406,9 +408,12 @@ def update_inspection( updated_at = CURRENT_TIMESTAMP, inspection_comment = %s WHERE - id = %s; + id = %s + RETURNING + updated_at; """ cursor.execute(query, (verified, inspection_comment, inspection_id)) + return cursor.fetchone()[0] @handle_query_errors(InspectionUpdateError) diff --git a/fertiscan/db/queries/organization/__init__.py b/fertiscan/db/queries/organization/__init__.py index a61bb630..af72501d 100644 --- a/fertiscan/db/queries/organization/__init__.py +++ b/fertiscan/db/queries/organization/__init__.py @@ -87,7 +87,7 @@ def upsert_organization(cursor: Cursor, name:str, website:str, phone_number: str """ cursor.execute(query, (name,)) res = cursor.fetchone() - if res[0] is None: + if res is None or res[0] is None: id =new_organization( cursor=cursor, name=name, diff --git a/tests/fertiscan/db/test_inspection.py b/tests/fertiscan/db/test_inspection.py index 21f00c14..8bbe712e 100644 --- a/tests/fertiscan/db/test_inspection.py +++ b/tests/fertiscan/db/test_inspection.py @@ -7,11 +7,12 @@ import unittest from datetime import datetime +from time import sleep import datastore.db as db from datastore import Role from datastore.db.metadata import picture_set, validator -from datastore.db.queries import picture, user,container +from datastore.db.queries import picture, user, container from fertiscan.db.queries import inspection, label, organization DB_CONNECTION_STRING = os.environ.get("FERTISCAN_DB_URL") @@ -30,12 +31,21 @@ def setUp(self): db.create_search_path(self.con, self.cursor, DB_SCHEMA) self.user_email = "testessr@email" - self.user_id = user.register_user(self.cursor, self.user_email,Role.INSPECTOR.value) + self.user_id = user.register_user( + self.cursor, self.user_email, Role.INSPECTOR.value + ) self.folder_name = "test-folder" self.picture_set = picture_set.build_picture_set_metadata(self.user_id, 1) - self.container_id = container.create_container(self.cursor,'test-container',self.user_id,False,"test-fertiscan-user") + self.container_id = container.create_container( + self.cursor, "test-container", self.user_id, False, "test-fertiscan-user" + ) self.picture_set_id = picture.new_picture_set( - self.cursor, self.picture_set, self.user_id, self.folder_name,container_id=self.container_id,parent_id=None + self.cursor, + self.picture_set, + self.user_id, + self.folder_name, + container_id=self.container_id, + parent_id=None, ) def tearDown(self): @@ -43,45 +53,46 @@ def tearDown(self): db.end_query(self.con, self.cursor) def test_new_inspection(self): - inspection_id = inspection.new_inspection( - cursor=self.cursor, - user_id=self.user_id, + inspection_id, upload_date = inspection.new_inspection( + cursor=self.cursor, + user_id=self.user_id, picture_set_id=self.picture_set_id, label_id=None, container_id=self.container_id, - verified=False + verified=False, ) self.assertTrue(validator.is_valid_uuid(inspection_id)) + self.assertIsInstance(upload_date, datetime) def test_is_inspection_verified(self): inspection_id = inspection.new_inspection( - cursor=self.cursor, - user_id=self.user_id, + cursor=self.cursor, + user_id=self.user_id, picture_set_id=self.picture_set_id, label_id=None, container_id=self.container_id, - verified=False - ) + verified=False, + )[0] inspection_id2 = inspection.new_inspection( - cursor=self.cursor, - user_id=self.user_id, + cursor=self.cursor, + user_id=self.user_id, picture_set_id=self.picture_set_id, label_id=None, container_id=self.container_id, - verified=True - ) + verified=True, + )[0] self.assertFalse(inspection.is_inspection_verified(self.cursor, inspection_id)) self.assertTrue(inspection.is_inspection_verified(self.cursor, inspection_id2)) def test_get_inspection(self): inspection_id = inspection.new_inspection( - cursor=self.cursor, - user_id=self.user_id, + cursor=self.cursor, + user_id=self.user_id, picture_set_id=self.picture_set_id, label_id=None, container_id=self.container_id, - verified=False - ) + verified=False, + )[0] inspection_data = inspection.get_inspection(self.cursor, inspection_id) self.assertEqual(inspection_data[0], False) self.assertEqual(inspection_data[3], self.user_id) @@ -89,21 +100,21 @@ def test_get_inspection(self): def test_get_all_user_inspection(self): inspection_id = inspection.new_inspection( - cursor=self.cursor, - user_id=self.user_id, + cursor=self.cursor, + user_id=self.user_id, picture_set_id=self.picture_set_id, label_id=None, container_id=self.container_id, - verified=False - ) + verified=False, + )[0] inspection_id2 = inspection.new_inspection( - cursor=self.cursor, - user_id=self.user_id, + cursor=self.cursor, + user_id=self.user_id, picture_set_id=self.picture_set_id, label_id=None, container_id=self.container_id, - verified=False - ) + verified=False, + )[0] inspection_data = inspection.get_all_user_inspection(self.cursor, self.user_id) self.assertEqual(len(inspection_data), 2) self.assertEqual(inspection_data[0][0], inspection_id) @@ -111,21 +122,21 @@ def test_get_all_user_inspection(self): def test_get_all_user_inspection_filter_verified(self): inspection_id = inspection.new_inspection( - cursor=self.cursor, - user_id=self.user_id, + cursor=self.cursor, + user_id=self.user_id, picture_set_id=self.picture_set_id, label_id=None, container_id=self.container_id, - verified=False - ) + verified=False, + )[0] inspection_id2 = inspection.new_inspection( - cursor=self.cursor, - user_id=self.user_id, + cursor=self.cursor, + user_id=self.user_id, picture_set_id=self.picture_set_id, label_id=None, container_id=self.container_id, - verified=True - ) + verified=True, + )[0] inspection_data = inspection.get_all_user_inspection_filter_verified( self.cursor, self.user_id, True ) @@ -157,172 +168,30 @@ def test_get_all_user_inspection_filter_verified(self): # self.assertEqual(inspection_data[0][0], inspection_id) # self.assertEqual(inspection_data[1][0], inspection_id2) - def test_search_inspection(self): - - product_name = "searched_product_name_test" - lot_number = "lot_number" - npk = "npk" - n = 10.0 - p = 20.0 - k = 30.0 - guaranteed_analysis_title_en = "guaranteed_analysis" - guaranteed_analysis_title_fr = "analyse_garantie" - guaranteed_is_minimal = False - record_keeping = False - label_information_id = label.new_label_information( - self.cursor, - product_name, - lot_number, - npk, - n, - p, - k, - guaranteed_analysis_title_en, - guaranteed_analysis_title_fr, - guaranteed_is_minimal, - record_keeping, - ) - inspection_id = inspection.new_inspection( - cursor=self.cursor, - user_id=self.user_id, - picture_set_id=self.picture_set_id, - label_id=label_information_id, - container_id=self.container_id, - verified=False - ) - - other_product_name = product_name # this is to test the search function - other_lot_number = "other_lot_number" - other_npk = "other_npk" - other_n = 100.0 - other_p = 200.0 - other_k = 300.0 - other_guaranteed_analysis_title_en = "other_guaranteed_analysis" - other_guaranteed_analysis_title_fr = "other_analyse_garantie" - other_guaranteed_is_minimal = True - other_record_keeping = True - other_label_information_id = label.new_label_information( - self.cursor, - other_product_name, - other_lot_number, - other_npk, - other_n, - other_p, - other_k, - other_guaranteed_analysis_title_en, - other_guaranteed_analysis_title_fr, - other_guaranteed_is_minimal, - other_record_keeping, - ) - other_inspection_id = inspection.new_inspection( - cursor=self.cursor, - user_id=self.user_id, + def test_update_inspection(self): + inspection_id, upload_date = inspection.new_inspection( + cursor=self.cursor, + user_id=self.user_id, picture_set_id=self.picture_set_id, - label_id=other_label_information_id, + label_id=None, container_id=self.container_id, - verified=False - ) - # Testing search by name - inspection_data = inspection.search_inspection( - self.cursor, - fertilizer_name=product_name, - lower_bound_date=None, - upper_bound_date=None, - lot_number=None, - label_ids=None, - ) - self.assertEqual(len(inspection_data), 2) - - # Testing search by lot number - inspection_data = inspection.search_inspection( - self.cursor, - fertilizer_name=None, - lower_bound_date=None, - upper_bound_date=None, - lot_number=lot_number, - label_ids=None, - ) - self.assertEqual(len(inspection_data), 1) - self.assertEqual(inspection_data[0][0], inspection_id) - - # Testing search by dates - # Testing search by lower bound date - today = datetime.today() - yesterday = today.replace(day=today.day-1) - tomorrow = today.replace(day=today.day+1) - inspection_data = inspection.search_inspection( - self.cursor, - fertilizer_name=None, - lower_bound_date=yesterday, - upper_bound_date=None, - lot_number=None, - label_ids=None, - ) - self.assertEqual(len(inspection_data), 2) - inspection_data = inspection.search_inspection( - self.cursor, - fertilizer_name=None, - lower_bound_date=today, - upper_bound_date=None, - lot_number=None, - label_ids=None, - ) - self.assertEqual(len(inspection_data), 2) - inspection_data = inspection.search_inspection( - self.cursor, - fertilizer_name=None, - lower_bound_date=tomorrow, - upper_bound_date=None, - lot_number=None, - label_ids=None, - ) - self.assertEqual(len(inspection_data),0) - # Testing search by upper bound date - inspection_data = inspection.search_inspection( - self.cursor, - fertilizer_name=None, - lower_bound_date=None, - upper_bound_date=today, - lot_number=None, - label_ids=None, - ) - self.assertEqual(len(inspection_data), 2) - inspection_data = inspection.search_inspection( - self.cursor, - fertilizer_name=None, - lower_bound_date=None, - upper_bound_date=yesterday, - lot_number=None, - label_ids=None, - ) - self.assertEqual(len(inspection_data), 0) - # Testing searching with both dates - inspection_data = inspection.search_inspection( - self.cursor, - fertilizer_name=None, - lower_bound_date=yesterday, - upper_bound_date=today, - lot_number=None, - label_ids=None, + verified=False, ) - self.assertEqual(len(inspection_data), 2) - inspection_data = inspection.search_inspection( - self.cursor, - fertilizer_name=None, - lower_bound_date=today, - upper_bound_date=today, - lot_number=None, - label_ids=None, - ) - self.assertEqual(len(inspection_data), 2) - # Testing search by label ids - label_ids= [label_information_id, other_label_information_id] - inspection_data = inspection.search_inspection( - self.cursor, - fertilizer_name=None, - lower_bound_date=None, - upper_bound_date=None, - lot_number=None, - label_ids=label_ids, + self.con.commit() # need to commit to get the time change + sleep(2) + updated_at = inspection.update_inspection( + cursor=self.cursor, + inspection_id=inspection_id, + verified=True, + inspection_comment="Test comment", ) - self.assertEqual(len(inspection_data), 2) + inspection_data = inspection.get_inspection(self.cursor, inspection_id) + inspection.delete_inspection(self.cursor, inspection_id, self.user_id) + picture.delete_picture_set(self.cursor, self.picture_set_id) + container.delete_container(self.cursor, self.container_id) + user.delete_user(self.cursor, self.user_id) + self.con.commit() + + self.assertTrue(inspection_data[0]) + self.assertNotEqual(updated_at, upload_date) + self.assertEqual(updated_at, inspection_data[2]) diff --git a/tests/fertiscan/test_datastore.py b/tests/fertiscan/test_datastore.py index 127ff85e..db79e8c1 100644 --- a/tests/fertiscan/test_datastore.py +++ b/tests/fertiscan/test_datastore.py @@ -11,6 +11,7 @@ from uuid import UUID from PIL import Image +from datetime import datetime import datastore import datastore.db as db @@ -27,6 +28,7 @@ nutrients, organization, sub_label, + errors, ) BLOB_CONNECTION_STRING = os.environ["FERTISCAN_STORAGE_URL"] @@ -62,6 +64,8 @@ def loop_into_empty_dict(dict_data): continue else: passing = False + elif isinstance(value, datetime): + continue elif isinstance(value, bool): if not value: continue @@ -358,16 +362,14 @@ def test_register_analysy_missing_key(self): ) def test_get_inspection(self): - formatted_analysis = metadata.build_inspection_import( - self.analysis_json, self.user.id, self.folder_id, self.container_model.id - ) - formatted_analysis.inspection_comment - inspection_dict = inspection.new_inspection_with_label_info( + inspection_controller = fertiscan.new_inspection( self.cursor, self.user.id, - self.folder_id, - formatted_analysis.model_dump_json(), + self.analysis_json, + self.container_controller.id, + folder_id=self.folder_id, ) + inspection_dict = inspection_controller.model inspection_model = fertiscan.data_inspection.Inspection.model_validate( inspection_dict ) @@ -450,6 +452,15 @@ def test_update_inspection(self): inspection_id = inspection_controller.id label_id = inspection_controller.model.product.label_id self.assertTrue(validator.is_valid_uuid(inspection_id)) + + og_inspection_data = inspection.get_inspection(self.cursor, inspection_id) + # Check the updated_at is the same as the the upload date + self.assertEqual(og_inspection_data[1], og_inspection_data[2]) + self.assertEqual( + inspection_controller.model.upload_date, + inspection_controller.model.updated_at, + ) + self.assertEqual(og_inspection_data[1], inspection_controller.model.updated_at) # new values new_product_name = "New Product Name" untouched_weight = inspection_controller.model.product.metrics.weight[1].value @@ -548,6 +559,8 @@ def test_update_inspection(self): inspection_to_update, ) updated_inspection = metadata.Inspection.model_validate(updated_inspection) + # check if the updated_at is updated in the model + # self.assertNotEqual(updated_inspection.updated_at,updated_inspection.upload_date) # This is not passing since you need to commit the cursor to get a different timestamp # check if specifications are updated # specifications = specification.get_all_specifications(self.cursor, label_id) @@ -592,6 +605,11 @@ def test_update_inspection(self): inspection_data = inspection.get_inspection(self.cursor, inspection_id) self.assertEqual(inspection_data[8], user_feedback) + # Verify updated_at is updated in the db + # This is not passing since you need to commit the cursor to get a different timestamp + # self.assertNotEqual(inspection_data[1], og_inspection_data[1]) + # self.assertNotEqual(inspection_data[1], inspection_data[2]) + # Verify organizations are saved orgs = organization.get_organizations_info_label(self.cursor, label_id) self.assertEqual(len(orgs), 1) # There is only 1 new @@ -700,7 +718,6 @@ def test_update_inspection_already_verified(self): inspection_to_update.verified = True self.assertTrue(inspection_to_update.verified) inspection_to_update.product.name = "verified_product" - print(inspection_to_update.organizations) updated_inspection = inspection_controller.update_inspection( self.cursor, self.user.id, @@ -708,7 +725,7 @@ def test_update_inspection_already_verified(self): ) self.assertTrue(updated_inspection.verified) updated_inspection.product.name = "blocked to be updated" - self.assertRaises( + with self.assertRaises(inspection.InspectionUpdateError): inspection_controller.update_inspection( self.cursor, self.user.id,