From 1c1a6ba3f934f67735aa234f83358ed462689816 Mon Sep 17 00:00:00 2001 From: Levi Szamek Date: Wed, 4 Dec 2024 15:16:10 +0100 Subject: [PATCH 01/10] feat: add integration test for tutorials --- mapswipe_workers/tests/integration/set_up.py | 55 ++++++++++---- .../tests/integration/test_create_tutorial.py | 71 +++++++++++++++++++ 2 files changed, 112 insertions(+), 14 deletions(-) create mode 100644 mapswipe_workers/tests/integration/test_create_tutorial.py diff --git a/mapswipe_workers/tests/integration/set_up.py b/mapswipe_workers/tests/integration/set_up.py index 74adc6fda..1c3c0bdf4 100644 --- a/mapswipe_workers/tests/integration/set_up.py +++ b/mapswipe_workers/tests/integration/set_up.py @@ -16,20 +16,28 @@ def set_firebase_test_data( - project_type: str, data_type: str, fixture_name: str, identifier: str + project_type: str, + data_type: str, + fixture_name: str, + identifier: str, + tutorial_id: str = None, ): test_dir = os.path.dirname(__file__) fixture_name = fixture_name + ".json" file_path = os.path.join( test_dir, "fixtures", project_type, data_type, fixture_name ) - upload_file_to_firebase(file_path, data_type, identifier) + upload_file_to_firebase(file_path, data_type, identifier, tutorial_id=tutorial_id) -def upload_file_to_firebase(file_path: str, data_type: str, identifier: str): +def upload_file_to_firebase( + file_path: str, data_type: str, identifier: str, tutorial_id: str = None +): with open(file_path) as test_file: test_data = json.load(test_file) + if tutorial_id: + test_data["tutorialId"] = tutorial_id fb_db = auth.firebaseDB() ref = fb_db.reference(f"/v2/{data_type}/{identifier}") ref.set(test_data) @@ -85,15 +93,20 @@ def create_test_project( set_postgres_test_data(project_type, "users", "user") set_firebase_test_data(project_type, "user_groups", "user_group", "") set_firebase_test_data(project_type, "results", fixture_name, project_id) - set_postgres_test_data(project_type, "mapping_sessions", fixture_name, columns=[ - "project_id", - "group_id", - "user_id", - "mapping_session_id", - "start_time", - "end_time", - "items_count", - ]) + set_postgres_test_data( + project_type, + "mapping_sessions", + fixture_name, + columns=[ + "project_id", + "group_id", + "user_id", + "mapping_session_id", + "start_time", + "end_time", + "items_count", + ], + ) set_postgres_test_data(project_type, mapping_sessions_results, fixture_name) if create_user_group_session_data: set_postgres_test_data( @@ -108,7 +121,9 @@ def create_test_project( "created_at", ], ) - set_postgres_test_data(project_type, "mapping_sessions_user_groups", fixture_name) + set_postgres_test_data( + project_type, "mapping_sessions_user_groups", fixture_name + ) time.sleep(5) # Wait for Firebase Functions to complete return project_id @@ -131,12 +146,24 @@ def create_test_user(project_type: str, user_id: str = None) -> str: def create_test_project_draft( - project_type: str, fixture_name: str = "user", identifier: str = "" + project_type: str, + fixture_name: str = "user", + identifier: str = "", + tutorial_id: str = None, ) -> str: """ Create test project drafts in Firebase and return project ids. Project drafts in Firebase are created by project manager using the dashboard. """ + if tutorial_id: + set_firebase_test_data( + project_type, + "projectDrafts", + fixture_name, + identifier, + tutorial_id=tutorial_id, + ) + return identifier if not identifier: identifier = f"test_{fixture_name}" set_firebase_test_data(project_type, "projectDrafts", fixture_name, identifier) diff --git a/mapswipe_workers/tests/integration/test_create_tutorial.py b/mapswipe_workers/tests/integration/test_create_tutorial.py new file mode 100644 index 000000000..76327120b --- /dev/null +++ b/mapswipe_workers/tests/integration/test_create_tutorial.py @@ -0,0 +1,71 @@ +import unittest + +from click.testing import CliRunner + +from mapswipe_workers import auth, mapswipe_workers +from mapswipe_workers.utils.create_directories import create_directories +from tests.integration import set_up, tear_down + + +class TestCreateTileClassificationProject(unittest.TestCase): + def setUp(self): + self.tutorial_id = set_up.create_test_tutorial_draft("footprint", "footprint") + + self.project_id = set_up.create_test_project_draft( + "tile_classification", + "tile_classification", + "test_tile_classification_tutorial", + tutorial_id=self.tutorial_id, + ) + create_directories() + + def tearDown(self): + tear_down.delete_test_data(self.project_id) + + def test_create_tile_classification_project(self): + runner = CliRunner() + runner.invoke(mapswipe_workers.run_create_projects, catch_exceptions=False) + + pg_db = auth.postgresDB() + query = "SELECT project_id FROM projects WHERE project_id = %s" + result = pg_db.retr_query(query, [self.project_id])[0][0] + self.assertEqual(result, self.project_id) + + query = """ + SELECT project_id + FROM projects + WHERE project_id = %s + and project_type_specifics::jsonb ? 'customOptions' + """ + result = pg_db.retr_query(query, [self.project_id])[0][0] + self.assertEqual(result, self.project_id) + + query = "SELECT count(*) FROM groups WHERE project_id = %s" + result = pg_db.retr_query(query, [self.project_id])[0][0] + self.assertEqual(result, 20) + + query = "SELECT count(*) FROM tasks WHERE project_id = %s" + result = pg_db.retr_query(query, [self.project_id])[0][0] + self.assertEqual(result, 5040) + + fb_db = auth.firebaseDB() + ref = fb_db.reference(f"/v2/projects/{self.project_id}") + result = ref.get(shallow=True) + self.assertIsNotNone(result) + + ref = fb_db.reference(f"/v2/groups/{self.project_id}") + result = ref.get(shallow=True) + self.assertEqual(len(result), 20) + + # Tile classification projects do not have tasks in Firebase + ref = fb_db.reference(f"/v2/tasks/{self.project_id}") + result = ref.get(shallow=True) + self.assertIsNone(result) + + ref = fb_db.reference(f"/v2/projects/{self.project_id}/tutorialId") + result = ref.get(shallow=True) + self.assertEqual(self.tutorial_id, result) + + +if __name__ == "__main__": + unittest.main() From dadca061437099800b41f9f4af1e3d56ad5ba218 Mon Sep 17 00:00:00 2001 From: Levi Szamek Date: Wed, 4 Dec 2024 15:43:33 +0100 Subject: [PATCH 02/10] fix: delete tutorial draft at end of test --- .../tests/integration/test_create_tutorial.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mapswipe_workers/tests/integration/test_create_tutorial.py b/mapswipe_workers/tests/integration/test_create_tutorial.py index 76327120b..c0904f06b 100644 --- a/mapswipe_workers/tests/integration/test_create_tutorial.py +++ b/mapswipe_workers/tests/integration/test_create_tutorial.py @@ -9,7 +9,11 @@ class TestCreateTileClassificationProject(unittest.TestCase): def setUp(self): - self.tutorial_id = set_up.create_test_tutorial_draft("footprint", "footprint") + self.tutorial_id = set_up.create_test_tutorial_draft( + "tile_classification", + "tile_classification", + "test_tile_classification_tutorial", + ) self.project_id = set_up.create_test_project_draft( "tile_classification", @@ -66,6 +70,8 @@ def test_create_tile_classification_project(self): result = ref.get(shallow=True) self.assertEqual(self.tutorial_id, result) + breakpoint() + if __name__ == "__main__": unittest.main() From 8c9d77402d6f4543af54ecb744680bd65ce97202 Mon Sep 17 00:00:00 2001 From: Levi Szamek Date: Thu, 12 Dec 2024 14:49:57 +0100 Subject: [PATCH 03/10] WIP: add tutorial for street project --- .../mapswipe_workers/definitions.py | 2 + .../project_types/__init__.py | 2 + .../project_types/street/tutorial.py | 80 ++++++++- .../tests/fixtures/projectDrafts/street.json | 1 - .../tests/fixtures/tutorialDrafts/street.json | 170 ++++++++++++++++++ .../integration/test_create_street_project.py | 2 + .../tests/integration/test_create_tutorial.py | 4 +- .../tests/unittests/test_tutorial.py | 11 +- 8 files changed, 259 insertions(+), 13 deletions(-) create mode 100644 mapswipe_workers/tests/fixtures/tutorialDrafts/street.json diff --git a/mapswipe_workers/mapswipe_workers/definitions.py b/mapswipe_workers/mapswipe_workers/definitions.py index c9dec6d79..aa32d3aac 100644 --- a/mapswipe_workers/mapswipe_workers/definitions.py +++ b/mapswipe_workers/mapswipe_workers/definitions.py @@ -170,6 +170,7 @@ def tutorial(self): ClassificationTutorial, CompletenessTutorial, FootprintTutorial, + StreetTutorial, ) project_type_classes = { @@ -177,5 +178,6 @@ def tutorial(self): 2: FootprintTutorial, 3: ChangeDetectionTutorial, 4: CompletenessTutorial, + 7: StreetTutorial, } return project_type_classes[self.value] diff --git a/mapswipe_workers/mapswipe_workers/project_types/__init__.py b/mapswipe_workers/mapswipe_workers/project_types/__init__.py index 43013b0dc..9560c76ef 100644 --- a/mapswipe_workers/mapswipe_workers/project_types/__init__.py +++ b/mapswipe_workers/mapswipe_workers/project_types/__init__.py @@ -3,6 +3,7 @@ from .arbitrary_geometry.footprint.tutorial import FootprintTutorial from .media_classification.project import MediaClassificationProject from .street.project import StreetProject +from .street.tutorial import StreetTutorial from .tile_map_service.change_detection.project import ChangeDetectionProject from .tile_map_service.change_detection.tutorial import ChangeDetectionTutorial from .tile_map_service.classification.project import ClassificationProject @@ -22,4 +23,5 @@ "FootprintTutorial", "DigitizationProject", "StreetProject", + "StreetTutorial", ] diff --git a/mapswipe_workers/mapswipe_workers/project_types/street/tutorial.py b/mapswipe_workers/mapswipe_workers/project_types/street/tutorial.py index cfbfc0ead..ca2c56cbe 100644 --- a/mapswipe_workers/mapswipe_workers/project_types/street/tutorial.py +++ b/mapswipe_workers/mapswipe_workers/project_types/street/tutorial.py @@ -1,14 +1,84 @@ +from dataclasses import asdict, dataclass + +from mapswipe_workers.definitions import logger +from mapswipe_workers.firebase.firebase import Firebase +from mapswipe_workers.project_types.street.project import StreetGroup, StreetTask from mapswipe_workers.project_types.tutorial import BaseTutorial +@dataclass +class StreetTutorialTask(StreetTask): + projectId: int + taskId: str + groupId: int + referenceAnswer: int + screen: int + + class StreetTutorial(BaseTutorial): - """The subclass for an TMS Grid based Tutorial.""" + """The subclass for an arbitrary geometry based Tutorial.""" - def save_tutorial(self): - raise NotImplementedError("Currently Street has no Tutorial") + def __init__(self, tutorial_draft): + # this will create the basis attributes + super().__init__(tutorial_draft) + + # self.projectId = tutorial_draft["projectId"] + self.projectType = tutorial_draft["projectType"] + self.tutorial_tasks = tutorial_draft["tasks"] + self.groups = dict() + self.tasks = dict() def create_tutorial_groups(self): - raise NotImplementedError("Currently Street has no Tutorial") + """Create group for the tutorial based on provided examples in geojson file.""" + # load examples/tasks from file + + group = StreetGroup( + groupId=101, + projectId=self.projectId, + numberOfTasks=len(self.tutorial_tasks), + progress=0, + finishedCount=0, + requiredCount=0, + ) + self.groups[101] = group + + # Add number of tasks for the group here. This needs to be set according to + # the number of features/examples in the geojson file + + logger.info( + f"{self.projectId}" + f" - create_tutorial_groups - " + f"created groups dictionary" + ) def create_tutorial_tasks(self): - raise NotImplementedError("Currently Street has no Tutorial") + """Create the tasks dict based on provided examples in geojson file.""" + task_list = [] + for i, task in enumerate(self.tutorial_tasks): + task = StreetTutorialTask( + projectId=self.projectId, + groupId=101, + taskId=f"{task['taskImageId']}", + geometry="", + referenceAnswer=task["referenceAnswer"], + screen=i, + ) + task_list.append(asdict(task)) + if task_list: + self.tasks[101] = task_list + else: + logger.info(f"group in project {self.projectId} is not valid.") + + logger.info( + f"{self.projectId}" + f" - create_tutorial_tasks - " + f"created tasks dictionary" + ) + + def save_tutorial(self): + firebase = Firebase() + firebase.save_tutorial_to_firebase( + self, self.groups, self.tasks, useCompression=True + ) + logger.info(self.tutorialDraftId) + firebase.drop_tutorial_draft(self.tutorialDraftId) diff --git a/mapswipe_workers/tests/fixtures/projectDrafts/street.json b/mapswipe_workers/tests/fixtures/projectDrafts/street.json index 1dd5b452a..67d1d8b04 100644 --- a/mapswipe_workers/tests/fixtures/projectDrafts/street.json +++ b/mapswipe_workers/tests/fixtures/projectDrafts/street.json @@ -46,6 +46,5 @@ "requestingOrganisation": "test", "verificationNumber": 3, "groupSize": 25, - "startTimestamp": "2019-07-01T00:00:00.000Z", "samplingThreshold": 0.1 } diff --git a/mapswipe_workers/tests/fixtures/tutorialDrafts/street.json b/mapswipe_workers/tests/fixtures/tutorialDrafts/street.json new file mode 100644 index 000000000..22116363b --- /dev/null +++ b/mapswipe_workers/tests/fixtures/tutorialDrafts/street.json @@ -0,0 +1,170 @@ +{ + "createdBy": "LtCUyou6CnSSc1H0Q0nDrN97x892", + "tutorialDraftId": "waste_mapping_dar_es_salaam", + "taskImageIds": [ + 888464808378923, + 1552821322020271, + 2969692853315413, + 1036040467918366, + 837497816845037 + ], + "answers": [ + 1, + 2, + 1, + 2, + 3 + ], + "customOptions": [ + { + "description": "the shape does outline a building in the image", + "icon": "hand-right-outline", + "iconColor": "#00796B", + "subOptions": [ + { + "description": "doppelt", + "value": 2 + }, + { + "description": "dreifach", + "value": 3 + } + ], + "title": "Jetzt rede ich", + "value": 1 + }, + { + "description": "the shape doesn't match a building in the image", + "icon": "close-outline", + "iconColor": "#D32F2F", + "title": "No", + "value": 0 + } + ], + "informationPages": [ + { + "blocks": [ + { + "blockNumber": 1, + "blockType": "text", + "textDescription": "asdf" + }, + { + "blockNumber": 2, + "blockType": "image", + "image": "https://firebasestorage.googleapis.com/v0/b/dev-mapswipe.appspot.com/o/tutorialImages%2F1705402528654-block-image-2-base-query-form.png?alt=media&token=54325ab8-c5e7-45a3-be41-1926a5984a05" + } + ], + "pageNumber": 1, + "title": "asdf" + } + ], + "lookFor": "waste", + "name": "Waste Mapping Dar es Salaam", + "projectType": 7, + "screens": [ + null, + { + "hint": { + "description": "Swipe to learn some more.", + "icon": "swipe-left", + "title": "We've marked the correct square green." + }, + "instructions": { + "description": "Tap once to mark the squares with buildings green.", + "icon": "tap-1", + "title": "There are some buildings in this image" + }, + "success": { + "description": "Swipe to the next screen to look for more.", + "icon": "check", + "title": "You found your first areas with buildings!" + } + }, + { + "hint": { + "description": "Swipe to learn some more.", + "icon": "swipe-left", + "title": "We've marked the correct square green." + }, + "instructions": { + "description": "Tap once to mark the squares with buildings green.", + "icon": "tap-1", + "title": "There are some buildings in this image" + }, + "success": { + "description": "Swipe to the next screen to look for more.", + "icon": "check", + "title": "You found your first" + } + }, + { + "hint": { + "description": "Swipe to learn some more.", + "icon": "swipe-left", + "title": "We've marked the correct square green." + }, + "instructions": { + "description": "Tap once to mark the squares with buildings green.", + "icon": "tap-1", + "title": "There are some buildings in this image" + }, + "success": { + "description": "Swipe to the next screen to look for more.", + "icon": "check", + "title": "You found your first areas with buildings!" + } + }, + { + "hint": { + "description": "Swipe to learn some more.", + "icon": "swipe-left", + "title": "We've marked the correct square green." + }, + "instructions": { + "description": "Tap once to mark the squares with buildings green.", + "icon": "tap-1", + "title": "There are some buildings in this image" + }, + "success": { + "description": "Swipe to the next screen to look for more.", + "icon": "check", + "title": "You found your first areas with buildings!" + } + }, + { + "hint": { + "description": "Swipe to learn some more.", + "icon": "swipe-left", + "title": "We've marked the correct square green." + }, + "instructions": { + "description": "Tap once to mark the squares with buildings green.", + "icon": "tap-1", + "title": "There are some buildings in this image" + }, + "success": { + "description": "Swipe to the next screen to look for more.", + "icon": "check", + "title": "You found your first areas with buildings!" + } + }, + { + "hint": { + "description": "Swipe to learn some more.", + "icon": "swipe-left", + "title": "We've marked the correct square green." + }, + "instructions": { + "description": "Tap once to mark the squares with buildings green.", + "icon": "tap-1", + "title": "There are some buildings in this image" + }, + "success": { + "description": "Swipe to the next screen to look for more.", + "icon": "check", + "title": "You found your first areas with buildings!" + } + } + ] +} \ No newline at end of file diff --git a/mapswipe_workers/tests/integration/test_create_street_project.py b/mapswipe_workers/tests/integration/test_create_street_project.py index fd0608f98..10da2bd0e 100644 --- a/mapswipe_workers/tests/integration/test_create_street_project.py +++ b/mapswipe_workers/tests/integration/test_create_street_project.py @@ -56,6 +56,8 @@ def test_create_street_project(self): result = ref.get(shallow=True) self.assertIsNotNone(result) + breakpoint() + if __name__ == "__main__": unittest.main() diff --git a/mapswipe_workers/tests/integration/test_create_tutorial.py b/mapswipe_workers/tests/integration/test_create_tutorial.py index c0904f06b..5ae13c76d 100644 --- a/mapswipe_workers/tests/integration/test_create_tutorial.py +++ b/mapswipe_workers/tests/integration/test_create_tutorial.py @@ -10,8 +10,8 @@ class TestCreateTileClassificationProject(unittest.TestCase): def setUp(self): self.tutorial_id = set_up.create_test_tutorial_draft( - "tile_classification", - "tile_classification", + "street", + "street", "test_tile_classification_tutorial", ) diff --git a/mapswipe_workers/tests/unittests/test_tutorial.py b/mapswipe_workers/tests/unittests/test_tutorial.py index 5ba1c209a..51a4cbf84 100644 --- a/mapswipe_workers/tests/unittests/test_tutorial.py +++ b/mapswipe_workers/tests/unittests/test_tutorial.py @@ -1,26 +1,27 @@ import os import unittest -from mapswipe_workers.project_types import ClassificationTutorial +from mapswipe_workers.project_types import StreetTutorial from tests.fixtures import FIXTURE_DIR, get_fixture class TestTutorial(unittest.TestCase): def test_init_tile_classification_project(self): tutorial_draft = get_fixture( - os.path.join(FIXTURE_DIR, "tutorialDrafts", "tile_classification.json") + os.path.join(FIXTURE_DIR, "tutorialDrafts", "street.json") ) - self.assertIsNotNone(ClassificationTutorial(tutorial_draft=tutorial_draft)) + self.assertIsNotNone(StreetTutorial(tutorial_draft=tutorial_draft)) def test_create_tile_classification_tasks(self): tutorial_draft = get_fixture( - os.path.join(FIXTURE_DIR, "tutorialDrafts", "tile_classification.json") + os.path.join(FIXTURE_DIR, "tutorialDrafts", "street.json") ) - tutorial = ClassificationTutorial(tutorial_draft=tutorial_draft) + tutorial = StreetTutorial(tutorial_draft=tutorial_draft) tutorial.create_tutorial_groups() tutorial.create_tutorial_tasks() self.assertTrue(tutorial.groups) self.assertTrue(tutorial.tasks) + breakpoint() if __name__ == "__main__": From e2486d9b61d8679000b1fa462706a20e3b1cd5e9 Mon Sep 17 00:00:00 2001 From: Levi Szamek Date: Mon, 27 Jan 2025 11:39:19 +0100 Subject: [PATCH 04/10] fix: remove artifacts from debugging --- mapswipe_workers/tests/unittests/test_tutorial.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/mapswipe_workers/tests/unittests/test_tutorial.py b/mapswipe_workers/tests/unittests/test_tutorial.py index 51a4cbf84..5ba1c209a 100644 --- a/mapswipe_workers/tests/unittests/test_tutorial.py +++ b/mapswipe_workers/tests/unittests/test_tutorial.py @@ -1,27 +1,26 @@ import os import unittest -from mapswipe_workers.project_types import StreetTutorial +from mapswipe_workers.project_types import ClassificationTutorial from tests.fixtures import FIXTURE_DIR, get_fixture class TestTutorial(unittest.TestCase): def test_init_tile_classification_project(self): tutorial_draft = get_fixture( - os.path.join(FIXTURE_DIR, "tutorialDrafts", "street.json") + os.path.join(FIXTURE_DIR, "tutorialDrafts", "tile_classification.json") ) - self.assertIsNotNone(StreetTutorial(tutorial_draft=tutorial_draft)) + self.assertIsNotNone(ClassificationTutorial(tutorial_draft=tutorial_draft)) def test_create_tile_classification_tasks(self): tutorial_draft = get_fixture( - os.path.join(FIXTURE_DIR, "tutorialDrafts", "street.json") + os.path.join(FIXTURE_DIR, "tutorialDrafts", "tile_classification.json") ) - tutorial = StreetTutorial(tutorial_draft=tutorial_draft) + tutorial = ClassificationTutorial(tutorial_draft=tutorial_draft) tutorial.create_tutorial_groups() tutorial.create_tutorial_tasks() self.assertTrue(tutorial.groups) self.assertTrue(tutorial.tasks) - breakpoint() if __name__ == "__main__": From 6e785980d71656171d983249dd969a5cd7a43fef Mon Sep 17 00:00:00 2001 From: Levi Szamek Date: Mon, 27 Jan 2025 16:27:16 +0100 Subject: [PATCH 05/10] fix: remove articat from debugging --- mapswipe_workers/tests/integration/test_create_tutorial.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/mapswipe_workers/tests/integration/test_create_tutorial.py b/mapswipe_workers/tests/integration/test_create_tutorial.py index 5ae13c76d..b61eb182e 100644 --- a/mapswipe_workers/tests/integration/test_create_tutorial.py +++ b/mapswipe_workers/tests/integration/test_create_tutorial.py @@ -10,8 +10,8 @@ class TestCreateTileClassificationProject(unittest.TestCase): def setUp(self): self.tutorial_id = set_up.create_test_tutorial_draft( - "street", - "street", + "tile_classification", + "tile_classification", "test_tile_classification_tutorial", ) @@ -70,8 +70,6 @@ def test_create_tile_classification_project(self): result = ref.get(shallow=True) self.assertEqual(self.tutorial_id, result) - breakpoint() - if __name__ == "__main__": unittest.main() From 23b654e569e0a89ea552738cd6d29192841b2d4f Mon Sep 17 00:00:00 2001 From: Levi Szamek Date: Tue, 28 Jan 2025 17:22:42 +0100 Subject: [PATCH 06/10] fix: remove debugging artifact --- .../tests/integration/test_create_street_project.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/mapswipe_workers/tests/integration/test_create_street_project.py b/mapswipe_workers/tests/integration/test_create_street_project.py index 10da2bd0e..fd0608f98 100644 --- a/mapswipe_workers/tests/integration/test_create_street_project.py +++ b/mapswipe_workers/tests/integration/test_create_street_project.py @@ -56,8 +56,6 @@ def test_create_street_project(self): result = ref.get(shallow=True) self.assertIsNotNone(result) - breakpoint() - if __name__ == "__main__": unittest.main() From dfb374329cc71389ca49698804de96684db6270b Mon Sep 17 00:00:00 2001 From: Levi Szamek Date: Thu, 20 Feb 2025 12:18:38 +0100 Subject: [PATCH 07/10] feat: change structure of street tutorial to match those of other project types --- .../project_types/street/tutorial.py | 10 +- .../tests/fixtures/tutorialDrafts/street.json | 198 ++++++------------ 2 files changed, 74 insertions(+), 134 deletions(-) diff --git a/mapswipe_workers/mapswipe_workers/project_types/street/tutorial.py b/mapswipe_workers/mapswipe_workers/project_types/street/tutorial.py index ca2c56cbe..e59a97f09 100644 --- a/mapswipe_workers/mapswipe_workers/project_types/street/tutorial.py +++ b/mapswipe_workers/mapswipe_workers/project_types/street/tutorial.py @@ -24,7 +24,7 @@ def __init__(self, tutorial_draft): # self.projectId = tutorial_draft["projectId"] self.projectType = tutorial_draft["projectType"] - self.tutorial_tasks = tutorial_draft["tasks"] + self.tutorial_tasks = tutorial_draft["tutorialTasks"] self.groups = dict() self.tasks = dict() @@ -54,14 +54,14 @@ def create_tutorial_groups(self): def create_tutorial_tasks(self): """Create the tasks dict based on provided examples in geojson file.""" task_list = [] - for i, task in enumerate(self.tutorial_tasks): + for i, task in enumerate(self.tutorial_tasks["features"]): task = StreetTutorialTask( projectId=self.projectId, groupId=101, - taskId=f"{task['taskImageId']}", + taskId=f"{task['properties']['id']}", geometry="", - referenceAnswer=task["referenceAnswer"], - screen=i, + referenceAnswer=task["properties"]["reference"], + screen=task["properties"]["screen"], ) task_list.append(asdict(task)) if task_list: diff --git a/mapswipe_workers/tests/fixtures/tutorialDrafts/street.json b/mapswipe_workers/tests/fixtures/tutorialDrafts/street.json index 22116363b..1385ffb52 100644 --- a/mapswipe_workers/tests/fixtures/tutorialDrafts/street.json +++ b/mapswipe_workers/tests/fixtures/tutorialDrafts/street.json @@ -1,170 +1,110 @@ { - "createdBy": "LtCUyou6CnSSc1H0Q0nDrN97x892", - "tutorialDraftId": "waste_mapping_dar_es_salaam", - "taskImageIds": [ - 888464808378923, - 1552821322020271, - 2969692853315413, - 1036040467918366, - 837497816845037 - ], - "answers": [ - 1, - 2, - 1, - 2, - 3 - ], - "customOptions": [ - { - "description": "the shape does outline a building in the image", - "icon": "hand-right-outline", - "iconColor": "#00796B", - "subOptions": [ - { - "description": "doppelt", - "value": 2 - }, - { - "description": "dreifach", - "value": 3 - } - ], - "title": "Jetzt rede ich", - "value": 1 - }, - { - "description": "the shape doesn't match a building in the image", - "icon": "close-outline", - "iconColor": "#D32F2F", - "title": "No", - "value": 0 - } - ], + "createdBy": "atCSosZACaN0qhcVjtMO1tq9d1G3", + "tutorialDraftId": "test_tile_classification", "informationPages": [ { "blocks": [ { "blockNumber": 1, "blockType": "text", - "textDescription": "asdf" + "textDescription": "This is the first information page" }, { "blockNumber": 2, "blockType": "image", - "image": "https://firebasestorage.googleapis.com/v0/b/dev-mapswipe.appspot.com/o/tutorialImages%2F1705402528654-block-image-2-base-query-form.png?alt=media&token=54325ab8-c5e7-45a3-be41-1926a5984a05" + "image": "https://firebasestorage.googleapis.com/v0/b/dev-mapswipe.appspot.com/o/tutorialImages%2F1739963139725-block-image-2-1x1.png?alt=media&token=ae584dcd-d351-4bfe-be5f-1e0d38547f72" } ], "pageNumber": 1, - "title": "asdf" + "title": "Information page 1" } ], - "lookFor": "waste", - "name": "Waste Mapping Dar es Salaam", + "lookFor": "cobblestone", + "name": "cobblestone-tutorial", "projectType": 7, "screens": [ null, { "hint": { - "description": "Swipe to learn some more.", - "icon": "swipe-left", - "title": "We've marked the correct square green." + "description": "This seems to be a tarmac surface.", + "icon": "check", + "title": "Tarmac" }, "instructions": { - "description": "Tap once to mark the squares with buildings green.", - "icon": "tap-1", - "title": "There are some buildings in this image" + "description": "Check out if the road surface material is cobblestone here", + "icon": "check", + "title": "Is this cobblestone?" }, "success": { - "description": "Swipe to the next screen to look for more.", + "description": "Correct, this is not cobblestone", "icon": "check", - "title": "You found your first areas with buildings!" + "title": "Nice!" } }, { "hint": { - "description": "Swipe to learn some more.", - "icon": "swipe-left", - "title": "We've marked the correct square green." + "description": "That surface does look like cobblestone!", + "icon": "heart-outline", + "title": "Cobblestone" }, "instructions": { - "description": "Tap once to mark the squares with buildings green.", - "icon": "tap-1", - "title": "There are some buildings in this image" + "description": "Does this look like cobblestone?", + "icon": "egg-outline", + "title": "How about this one?" }, "success": { - "description": "Swipe to the next screen to look for more.", - "icon": "check", - "title": "You found your first" - } + "description": "Correct", + "icon": "search-outline", + "title": "Correct" + } + } + ], + "tileServer": { + "credits": "© 2019 Microsoft Corporation, Earthstar Geographics SIO", + "name": "bing" + }, + "tutorialTasks": { + "crs": { + "properties": { + "name": "urn:ogc:def:crs:OGC:1.3:CRS84" + }, + "type": "name" }, - { - "hint": { - "description": "Swipe to learn some more.", - "icon": "swipe-left", - "title": "We've marked the correct square green." - }, - "instructions": { - "description": "Tap once to mark the squares with buildings green.", - "icon": "tap-1", - "title": "There are some buildings in this image" - }, - "success": { - "description": "Swipe to the next screen to look for more.", - "icon": "check", - "title": "You found your first areas with buildings!" - } - }, - { - "hint": { - "description": "Swipe to learn some more.", - "icon": "swipe-left", - "title": "We've marked the correct square green." + "features": [ + { + "geometry": { + "coordinates": [ + 13.4514123, + 52.5103378, + 0 + ], + "type": "Point" }, - "instructions": { - "description": "Tap once to mark the squares with buildings green.", - "icon": "tap-1", - "title": "There are some buildings in this image" + "properties": { + "id": "1171343450849316", + "reference": 1, + "screen": 1 }, - "success": { - "description": "Swipe to the next screen to look for more.", - "icon": "check", - "title": "You found your first areas with buildings!" - } - }, - { - "hint": { - "description": "Swipe to learn some more.", - "icon": "swipe-left", - "title": "We've marked the correct square green." + "type": "Feature" + }, + { + "geometry": { + "coordinates": [ + 13.45285, + 52.508467, + 0 + ], + "type": "Point" }, - "instructions": { - "description": "Tap once to mark the squares with buildings green.", - "icon": "tap-1", - "title": "There are some buildings in this image" + "properties": { + "id": "378811598610667", + "reference": 0, + "screen": 2 }, - "success": { - "description": "Swipe to the next screen to look for more.", - "icon": "check", - "title": "You found your first areas with buildings!" - } - }, - { - "hint": { - "description": "Swipe to learn some more.", - "icon": "swipe-left", - "title": "We've marked the correct square green." - }, - "instructions": { - "description": "Tap once to mark the squares with buildings green.", - "icon": "tap-1", - "title": "There are some buildings in this image" - }, - "success": { - "description": "Swipe to the next screen to look for more.", - "icon": "check", - "title": "You found your first areas with buildings!" + "type": "Feature" } - } - ] + ], + "name": "cobblestone-scenario", + "type": "FeatureCollection" + } } \ No newline at end of file From 550df7a237036e9217e58d745f19293375da6453 Mon Sep 17 00:00:00 2001 From: Levi Szamek Date: Thu, 20 Feb 2025 12:36:50 +0100 Subject: [PATCH 08/10] fix: working integration test for tutorial creation --- .../tests/integration/tear_down.py | 8 ++- .../tests/integration/test_create_tutorial.py | 50 ++++--------------- .../tests/unittests/test_tutorial.py | 10 ++-- 3 files changed, 22 insertions(+), 46 deletions(-) diff --git a/mapswipe_workers/tests/integration/tear_down.py b/mapswipe_workers/tests/integration/tear_down.py index 61760781c..33af5d52d 100644 --- a/mapswipe_workers/tests/integration/tear_down.py +++ b/mapswipe_workers/tests/integration/tear_down.py @@ -8,7 +8,7 @@ from mapswipe_workers import auth -def delete_test_data(project_id: str) -> None: +def delete_test_data(project_id: str, tutorial_id: str = None) -> None: """ Delete test project indluding groups, tasks and results from Firebase and Postgres @@ -38,6 +38,12 @@ def delete_test_data(project_id: str) -> None: ref = fb_db.reference(f"v2/users/{project_id}") ref.delete() + if tutorial_id is not None: + ref = fb_db.reference(f"v2/projects/{tutorial_id}") + ref.delete() + ref = fb_db.reference(f"v2/tutorialDrafts/{tutorial_id}") + ref.delete() + # Clear out the user-group used in test. # XXX: Use a firebase simulator for running test. # For CI/CD, use a real firebase with scope using commit hash, diff --git a/mapswipe_workers/tests/integration/test_create_tutorial.py b/mapswipe_workers/tests/integration/test_create_tutorial.py index b61eb182e..31e45b3fa 100644 --- a/mapswipe_workers/tests/integration/test_create_tutorial.py +++ b/mapswipe_workers/tests/integration/test_create_tutorial.py @@ -10,65 +10,35 @@ class TestCreateTileClassificationProject(unittest.TestCase): def setUp(self): self.tutorial_id = set_up.create_test_tutorial_draft( - "tile_classification", - "tile_classification", + "street", + "street", "test_tile_classification_tutorial", ) self.project_id = set_up.create_test_project_draft( - "tile_classification", - "tile_classification", + "street", + "street", "test_tile_classification_tutorial", tutorial_id=self.tutorial_id, ) create_directories() def tearDown(self): - tear_down.delete_test_data(self.project_id) + tear_down.delete_test_data(self.project_id, self.tutorial_id) def test_create_tile_classification_project(self): runner = CliRunner() + runner.invoke(mapswipe_workers.run_create_tutorials, catch_exceptions=False) runner.invoke(mapswipe_workers.run_create_projects, catch_exceptions=False) - pg_db = auth.postgresDB() - query = "SELECT project_id FROM projects WHERE project_id = %s" - result = pg_db.retr_query(query, [self.project_id])[0][0] - self.assertEqual(result, self.project_id) - - query = """ - SELECT project_id - FROM projects - WHERE project_id = %s - and project_type_specifics::jsonb ? 'customOptions' - """ - result = pg_db.retr_query(query, [self.project_id])[0][0] - self.assertEqual(result, self.project_id) - - query = "SELECT count(*) FROM groups WHERE project_id = %s" - result = pg_db.retr_query(query, [self.project_id])[0][0] - self.assertEqual(result, 20) - - query = "SELECT count(*) FROM tasks WHERE project_id = %s" - result = pg_db.retr_query(query, [self.project_id])[0][0] - self.assertEqual(result, 5040) - fb_db = auth.firebaseDB() ref = fb_db.reference(f"/v2/projects/{self.project_id}") - result = ref.get(shallow=True) - self.assertIsNotNone(result) + result = ref.get() + self.assertEqual(result["tutorialId"], self.tutorial_id) - ref = fb_db.reference(f"/v2/groups/{self.project_id}") + ref = fb_db.reference(f"/v2/projects/{self.tutorial_id}") result = ref.get(shallow=True) - self.assertEqual(len(result), 20) - - # Tile classification projects do not have tasks in Firebase - ref = fb_db.reference(f"/v2/tasks/{self.project_id}") - result = ref.get(shallow=True) - self.assertIsNone(result) - - ref = fb_db.reference(f"/v2/projects/{self.project_id}/tutorialId") - result = ref.get(shallow=True) - self.assertEqual(self.tutorial_id, result) + self.assertIsNotNone(result) if __name__ == "__main__": diff --git a/mapswipe_workers/tests/unittests/test_tutorial.py b/mapswipe_workers/tests/unittests/test_tutorial.py index 5ba1c209a..16e9e6aa4 100644 --- a/mapswipe_workers/tests/unittests/test_tutorial.py +++ b/mapswipe_workers/tests/unittests/test_tutorial.py @@ -1,22 +1,22 @@ import os import unittest -from mapswipe_workers.project_types import ClassificationTutorial +from mapswipe_workers.project_types import StreetTutorial from tests.fixtures import FIXTURE_DIR, get_fixture class TestTutorial(unittest.TestCase): def test_init_tile_classification_project(self): tutorial_draft = get_fixture( - os.path.join(FIXTURE_DIR, "tutorialDrafts", "tile_classification.json") + os.path.join(FIXTURE_DIR, "tutorialDrafts", "street.json") ) - self.assertIsNotNone(ClassificationTutorial(tutorial_draft=tutorial_draft)) + self.assertIsNotNone(StreetTutorial(tutorial_draft=tutorial_draft)) def test_create_tile_classification_tasks(self): tutorial_draft = get_fixture( - os.path.join(FIXTURE_DIR, "tutorialDrafts", "tile_classification.json") + os.path.join(FIXTURE_DIR, "tutorialDrafts", "street.json") ) - tutorial = ClassificationTutorial(tutorial_draft=tutorial_draft) + tutorial = StreetTutorial(tutorial_draft=tutorial_draft) tutorial.create_tutorial_groups() tutorial.create_tutorial_tasks() self.assertTrue(tutorial.groups) From 5237ce07d4ac4e4981b8fe5a75d838e1603603f8 Mon Sep 17 00:00:00 2001 From: Levi Szamek Date: Thu, 20 Feb 2025 12:38:00 +0100 Subject: [PATCH 09/10] fix: naming in integration test --- .../tests/integration/test_create_tutorial.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mapswipe_workers/tests/integration/test_create_tutorial.py b/mapswipe_workers/tests/integration/test_create_tutorial.py index 31e45b3fa..e6db39579 100644 --- a/mapswipe_workers/tests/integration/test_create_tutorial.py +++ b/mapswipe_workers/tests/integration/test_create_tutorial.py @@ -10,14 +10,14 @@ class TestCreateTileClassificationProject(unittest.TestCase): def setUp(self): self.tutorial_id = set_up.create_test_tutorial_draft( - "street", - "street", + "tile_classification", + "tile_classification", "test_tile_classification_tutorial", ) self.project_id = set_up.create_test_project_draft( - "street", - "street", + "tile_classification", + "tile_classification", "test_tile_classification_tutorial", tutorial_id=self.tutorial_id, ) @@ -26,7 +26,7 @@ def setUp(self): def tearDown(self): tear_down.delete_test_data(self.project_id, self.tutorial_id) - def test_create_tile_classification_project(self): + def test_create_tile_classification_project_and_tutorial(self): runner = CliRunner() runner.invoke(mapswipe_workers.run_create_tutorials, catch_exceptions=False) runner.invoke(mapswipe_workers.run_create_projects, catch_exceptions=False) From e2efb18a07c60e3aa28936863e30cc768d67fee8 Mon Sep 17 00:00:00 2001 From: Levi Szamek Date: Thu, 20 Feb 2025 12:40:32 +0100 Subject: [PATCH 10/10] refactor: change name of test_tutorial to test_tutorial_street --- .../unittests/{test_tutorial.py => test_tutorial_street.py} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename mapswipe_workers/tests/unittests/{test_tutorial.py => test_tutorial_street.py} (87%) diff --git a/mapswipe_workers/tests/unittests/test_tutorial.py b/mapswipe_workers/tests/unittests/test_tutorial_street.py similarity index 87% rename from mapswipe_workers/tests/unittests/test_tutorial.py rename to mapswipe_workers/tests/unittests/test_tutorial_street.py index 16e9e6aa4..6dd9b0127 100644 --- a/mapswipe_workers/tests/unittests/test_tutorial.py +++ b/mapswipe_workers/tests/unittests/test_tutorial_street.py @@ -6,13 +6,13 @@ class TestTutorial(unittest.TestCase): - def test_init_tile_classification_project(self): + def test_init_street_tutorial(self): tutorial_draft = get_fixture( os.path.join(FIXTURE_DIR, "tutorialDrafts", "street.json") ) self.assertIsNotNone(StreetTutorial(tutorial_draft=tutorial_draft)) - def test_create_tile_classification_tasks(self): + def test_create_street_tasks(self): tutorial_draft = get_fixture( os.path.join(FIXTURE_DIR, "tutorialDrafts", "street.json") )