Skip to content
This repository was archived by the owner on Apr 3, 2026. It is now read-only.

Commit 65555a8

Browse files
authored
Merge pull request #983 from mapswipe/street-tutorial
feat: add tutorial for street project type
2 parents f227899 + e2efb18 commit 65555a8

10 files changed

Lines changed: 309 additions & 48 deletions

File tree

mapswipe_workers/mapswipe_workers/definitions.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,12 +170,14 @@ def tutorial(self):
170170
ClassificationTutorial,
171171
CompletenessTutorial,
172172
FootprintTutorial,
173+
StreetTutorial,
173174
)
174175

175176
project_type_classes = {
176177
1: ClassificationTutorial,
177178
2: FootprintTutorial,
178179
3: ChangeDetectionTutorial,
179180
4: CompletenessTutorial,
181+
7: StreetTutorial,
180182
}
181183
return project_type_classes[self.value]

mapswipe_workers/mapswipe_workers/project_types/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from .arbitrary_geometry.footprint.tutorial import FootprintTutorial
44
from .media_classification.project import MediaClassificationProject
55
from .street.project import StreetProject
6+
from .street.tutorial import StreetTutorial
67
from .tile_map_service.change_detection.project import ChangeDetectionProject
78
from .tile_map_service.change_detection.tutorial import ChangeDetectionTutorial
89
from .tile_map_service.classification.project import ClassificationProject
@@ -22,4 +23,5 @@
2223
"FootprintTutorial",
2324
"DigitizationProject",
2425
"StreetProject",
26+
"StreetTutorial",
2527
]
Lines changed: 75 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,84 @@
1+
from dataclasses import asdict, dataclass
2+
3+
from mapswipe_workers.definitions import logger
4+
from mapswipe_workers.firebase.firebase import Firebase
5+
from mapswipe_workers.project_types.street.project import StreetGroup, StreetTask
16
from mapswipe_workers.project_types.tutorial import BaseTutorial
27

38

9+
@dataclass
10+
class StreetTutorialTask(StreetTask):
11+
projectId: int
12+
taskId: str
13+
groupId: int
14+
referenceAnswer: int
15+
screen: int
16+
17+
418
class StreetTutorial(BaseTutorial):
5-
"""The subclass for an TMS Grid based Tutorial."""
19+
"""The subclass for an arbitrary geometry based Tutorial."""
620

7-
def save_tutorial(self):
8-
raise NotImplementedError("Currently Street has no Tutorial")
21+
def __init__(self, tutorial_draft):
22+
# this will create the basis attributes
23+
super().__init__(tutorial_draft)
24+
25+
# self.projectId = tutorial_draft["projectId"]
26+
self.projectType = tutorial_draft["projectType"]
27+
self.tutorial_tasks = tutorial_draft["tutorialTasks"]
28+
self.groups = dict()
29+
self.tasks = dict()
930

1031
def create_tutorial_groups(self):
11-
raise NotImplementedError("Currently Street has no Tutorial")
32+
"""Create group for the tutorial based on provided examples in geojson file."""
33+
# load examples/tasks from file
34+
35+
group = StreetGroup(
36+
groupId=101,
37+
projectId=self.projectId,
38+
numberOfTasks=len(self.tutorial_tasks),
39+
progress=0,
40+
finishedCount=0,
41+
requiredCount=0,
42+
)
43+
self.groups[101] = group
44+
45+
# Add number of tasks for the group here. This needs to be set according to
46+
# the number of features/examples in the geojson file
47+
48+
logger.info(
49+
f"{self.projectId}"
50+
f" - create_tutorial_groups - "
51+
f"created groups dictionary"
52+
)
1253

1354
def create_tutorial_tasks(self):
14-
raise NotImplementedError("Currently Street has no Tutorial")
55+
"""Create the tasks dict based on provided examples in geojson file."""
56+
task_list = []
57+
for i, task in enumerate(self.tutorial_tasks["features"]):
58+
task = StreetTutorialTask(
59+
projectId=self.projectId,
60+
groupId=101,
61+
taskId=f"{task['properties']['id']}",
62+
geometry="",
63+
referenceAnswer=task["properties"]["reference"],
64+
screen=task["properties"]["screen"],
65+
)
66+
task_list.append(asdict(task))
67+
if task_list:
68+
self.tasks[101] = task_list
69+
else:
70+
logger.info(f"group in project {self.projectId} is not valid.")
71+
72+
logger.info(
73+
f"{self.projectId}"
74+
f" - create_tutorial_tasks - "
75+
f"created tasks dictionary"
76+
)
77+
78+
def save_tutorial(self):
79+
firebase = Firebase()
80+
firebase.save_tutorial_to_firebase(
81+
self, self.groups, self.tasks, useCompression=True
82+
)
83+
logger.info(self.tutorialDraftId)
84+
firebase.drop_tutorial_draft(self.tutorialDraftId)

mapswipe_workers/tests/fixtures/projectDrafts/street.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,5 @@
4646
"requestingOrganisation": "test",
4747
"verificationNumber": 3,
4848
"groupSize": 25,
49-
"startTimestamp": "2019-07-01T00:00:00.000Z",
5049
"samplingThreshold": 0.1
5150
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
{
2+
"createdBy": "atCSosZACaN0qhcVjtMO1tq9d1G3",
3+
"tutorialDraftId": "test_tile_classification",
4+
"informationPages": [
5+
{
6+
"blocks": [
7+
{
8+
"blockNumber": 1,
9+
"blockType": "text",
10+
"textDescription": "This is the first information page"
11+
},
12+
{
13+
"blockNumber": 2,
14+
"blockType": "image",
15+
"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"
16+
}
17+
],
18+
"pageNumber": 1,
19+
"title": "Information page 1"
20+
}
21+
],
22+
"lookFor": "cobblestone",
23+
"name": "cobblestone-tutorial",
24+
"projectType": 7,
25+
"screens": [
26+
null,
27+
{
28+
"hint": {
29+
"description": "This seems to be a tarmac surface.",
30+
"icon": "check",
31+
"title": "Tarmac"
32+
},
33+
"instructions": {
34+
"description": "Check out if the road surface material is cobblestone here",
35+
"icon": "check",
36+
"title": "Is this cobblestone?"
37+
},
38+
"success": {
39+
"description": "Correct, this is not cobblestone",
40+
"icon": "check",
41+
"title": "Nice!"
42+
}
43+
},
44+
{
45+
"hint": {
46+
"description": "That surface does look like cobblestone!",
47+
"icon": "heart-outline",
48+
"title": "Cobblestone"
49+
},
50+
"instructions": {
51+
"description": "Does this look like cobblestone?",
52+
"icon": "egg-outline",
53+
"title": "How about this one?"
54+
},
55+
"success": {
56+
"description": "Correct",
57+
"icon": "search-outline",
58+
"title": "Correct"
59+
}
60+
}
61+
],
62+
"tileServer": {
63+
"credits": "© 2019 Microsoft Corporation, Earthstar Geographics SIO",
64+
"name": "bing"
65+
},
66+
"tutorialTasks": {
67+
"crs": {
68+
"properties": {
69+
"name": "urn:ogc:def:crs:OGC:1.3:CRS84"
70+
},
71+
"type": "name"
72+
},
73+
"features": [
74+
{
75+
"geometry": {
76+
"coordinates": [
77+
13.4514123,
78+
52.5103378,
79+
0
80+
],
81+
"type": "Point"
82+
},
83+
"properties": {
84+
"id": "1171343450849316",
85+
"reference": 1,
86+
"screen": 1
87+
},
88+
"type": "Feature"
89+
},
90+
{
91+
"geometry": {
92+
"coordinates": [
93+
13.45285,
94+
52.508467,
95+
0
96+
],
97+
"type": "Point"
98+
},
99+
"properties": {
100+
"id": "378811598610667",
101+
"reference": 0,
102+
"screen": 2
103+
},
104+
"type": "Feature"
105+
}
106+
],
107+
"name": "cobblestone-scenario",
108+
"type": "FeatureCollection"
109+
}
110+
}

mapswipe_workers/tests/integration/set_up.py

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,28 @@
1616

1717

1818
def set_firebase_test_data(
19-
project_type: str, data_type: str, fixture_name: str, identifier: str
19+
project_type: str,
20+
data_type: str,
21+
fixture_name: str,
22+
identifier: str,
23+
tutorial_id: str = None,
2024
):
2125
test_dir = os.path.dirname(__file__)
2226
fixture_name = fixture_name + ".json"
2327
file_path = os.path.join(
2428
test_dir, "fixtures", project_type, data_type, fixture_name
2529
)
26-
upload_file_to_firebase(file_path, data_type, identifier)
30+
upload_file_to_firebase(file_path, data_type, identifier, tutorial_id=tutorial_id)
2731

2832

29-
def upload_file_to_firebase(file_path: str, data_type: str, identifier: str):
33+
def upload_file_to_firebase(
34+
file_path: str, data_type: str, identifier: str, tutorial_id: str = None
35+
):
3036
with open(file_path) as test_file:
3137
test_data = json.load(test_file)
3238

39+
if tutorial_id:
40+
test_data["tutorialId"] = tutorial_id
3341
fb_db = auth.firebaseDB()
3442
ref = fb_db.reference(f"/v2/{data_type}/{identifier}")
3543
ref.set(test_data)
@@ -85,15 +93,20 @@ def create_test_project(
8593
set_postgres_test_data(project_type, "users", "user")
8694
set_firebase_test_data(project_type, "user_groups", "user_group", "")
8795
set_firebase_test_data(project_type, "results", fixture_name, project_id)
88-
set_postgres_test_data(project_type, "mapping_sessions", fixture_name, columns=[
89-
"project_id",
90-
"group_id",
91-
"user_id",
92-
"mapping_session_id",
93-
"start_time",
94-
"end_time",
95-
"items_count",
96-
])
96+
set_postgres_test_data(
97+
project_type,
98+
"mapping_sessions",
99+
fixture_name,
100+
columns=[
101+
"project_id",
102+
"group_id",
103+
"user_id",
104+
"mapping_session_id",
105+
"start_time",
106+
"end_time",
107+
"items_count",
108+
],
109+
)
97110
set_postgres_test_data(project_type, mapping_sessions_results, fixture_name)
98111
if create_user_group_session_data:
99112
set_postgres_test_data(
@@ -108,7 +121,9 @@ def create_test_project(
108121
"created_at",
109122
],
110123
)
111-
set_postgres_test_data(project_type, "mapping_sessions_user_groups", fixture_name)
124+
set_postgres_test_data(
125+
project_type, "mapping_sessions_user_groups", fixture_name
126+
)
112127

113128
time.sleep(5) # Wait for Firebase Functions to complete
114129
return project_id
@@ -131,12 +146,24 @@ def create_test_user(project_type: str, user_id: str = None) -> str:
131146

132147

133148
def create_test_project_draft(
134-
project_type: str, fixture_name: str = "user", identifier: str = ""
149+
project_type: str,
150+
fixture_name: str = "user",
151+
identifier: str = "",
152+
tutorial_id: str = None,
135153
) -> str:
136154
"""
137155
Create test project drafts in Firebase and return project ids.
138156
Project drafts in Firebase are created by project manager using the dashboard.
139157
"""
158+
if tutorial_id:
159+
set_firebase_test_data(
160+
project_type,
161+
"projectDrafts",
162+
fixture_name,
163+
identifier,
164+
tutorial_id=tutorial_id,
165+
)
166+
return identifier
140167
if not identifier:
141168
identifier = f"test_{fixture_name}"
142169
set_firebase_test_data(project_type, "projectDrafts", fixture_name, identifier)

mapswipe_workers/tests/integration/tear_down.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from mapswipe_workers import auth
99

1010

11-
def delete_test_data(project_id: str) -> None:
11+
def delete_test_data(project_id: str, tutorial_id: str = None) -> None:
1212
"""
1313
Delete test project indluding groups, tasks and results
1414
from Firebase and Postgres
@@ -38,6 +38,12 @@ def delete_test_data(project_id: str) -> None:
3838
ref = fb_db.reference(f"v2/users/{project_id}")
3939
ref.delete()
4040

41+
if tutorial_id is not None:
42+
ref = fb_db.reference(f"v2/projects/{tutorial_id}")
43+
ref.delete()
44+
ref = fb_db.reference(f"v2/tutorialDrafts/{tutorial_id}")
45+
ref.delete()
46+
4147
# Clear out the user-group used in test.
4248
# XXX: Use a firebase simulator for running test.
4349
# For CI/CD, use a real firebase with scope using commit hash,

0 commit comments

Comments
 (0)