Skip to content

Commit bbc57a6

Browse files
Merge pull request #357 from mapswipe/project_types
Define project type and its class constructor in definition.py
2 parents 5626995 + 2b6b238 commit bbc57a6

File tree

20 files changed

+216
-284
lines changed

20 files changed

+216
-284
lines changed

mapswipe_workers/mapswipe_workers/definitions.py

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,11 @@
11
import logging.config
22
import os
3-
import sentry_sdk
4-
from xdg import XDG_DATA_HOME
5-
from mapswipe_workers.config import SENTRY_DSN
63
from enum import Enum
74

5+
import sentry_sdk
6+
from xdg import XDG_DATA_HOME
87

9-
class CustomError(Exception):
10-
pass
11-
12-
13-
class MessageType(Enum):
14-
SUCCESS = 1
15-
FAIL = 2
16-
NOTIFICATION_90 = 3
17-
NOTIFICATION_100 = 4
18-
8+
from mapswipe_workers.config import SENTRY_DSN
199

2010
DATA_PATH = os.path.join(XDG_DATA_HOME, "mapswipe_workers")
2111
if not os.path.exists(DATA_PATH):
@@ -27,7 +17,7 @@ class MessageType(Enum):
2717
"disable_existing_loggers": True,
2818
"formatters": {
2919
"standard": {
30-
"format": "%(asctime)s - %(levelname)s - %(filename)s - %(funcName)s - %(message)s"
20+
"format": "%(asctime)s - %(levelname)s - %(filename)s - %(funcName)s - %(message)s" # noqa: E501
3121
},
3222
},
3323
"handlers": {
@@ -104,3 +94,60 @@ class MessageType(Enum):
10494
+ "tilematrix={z}&tilecol={x}&tilerow={y}&layer={layer}"
10595
),
10696
}
97+
98+
99+
class CustomError(Exception):
100+
pass
101+
102+
103+
class MessageType(Enum):
104+
SUCCESS = 1
105+
FAIL = 2
106+
NOTIFICATION_90 = 3
107+
NOTIFICATION_100 = 4
108+
109+
110+
class ProjectType(Enum):
111+
"""
112+
Definition of Project Type names, identifiers and constructors.
113+
114+
There are different project types with the same constructor.
115+
Get the class constructor of a project type with:
116+
ProjectType(1).constructor
117+
"""
118+
119+
BUILD_AREA = 1
120+
FOOTPRINT = 2
121+
CHANGE_DETECTION = 3
122+
123+
@property
124+
def constructor(self):
125+
# Imports are first made once this method get called to avoid circular imports.
126+
from mapswipe_workers.project_types.tile_map_service_grid.project import (
127+
Project as tmsg_project,
128+
)
129+
from mapswipe_workers.project_types.arbitrary_geometry.project import (
130+
Project as ag_project,
131+
)
132+
133+
project_type_classes = {
134+
1: tmsg_project,
135+
2: ag_project,
136+
3: tmsg_project,
137+
}
138+
return project_type_classes[self.value]
139+
140+
@property
141+
def tutorial(self):
142+
from mapswipe_workers.project_types.tile_map_service_grid import (
143+
build_area_tutorial,
144+
)
145+
from mapswipe_workers.project_types.tile_map_service_grid import (
146+
change_detection_tutorial,
147+
)
148+
149+
project_type_tutorial = {
150+
1: build_area_tutorial,
151+
3: change_detection_tutorial,
152+
}
153+
return project_type_tutorial[self.value]

mapswipe_workers/mapswipe_workers/mapswipe_workers.py

Lines changed: 13 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,29 @@
44
import json
55
import time
66

7+
import click
78
import schedule as sched
89

9-
import click
1010
from mapswipe_workers import auth
11-
from mapswipe_workers.definitions import CustomError, logger, sentry, MessageType
11+
from mapswipe_workers.definitions import (
12+
CustomError,
13+
MessageType,
14+
ProjectType,
15+
logger,
16+
sentry,
17+
)
1218
from mapswipe_workers.firebase_to_postgres import (
1319
archive_project,
1420
delete_project,
1521
transfer_results,
1622
update_data,
1723
)
1824
from mapswipe_workers.generate_stats import generate_stats
19-
from mapswipe_workers.project_types.build_area import build_area_tutorial
20-
from mapswipe_workers.project_types.build_area.build_area_project import (
21-
BuildAreaProject,
22-
)
23-
from mapswipe_workers.project_types.change_detection import change_detection_tutorial
24-
from mapswipe_workers.project_types.change_detection.change_detection_project import (
25-
ChangeDetectionProject,
26-
)
27-
from mapswipe_workers.project_types.footprint.footprint_project import FootprintProject
2825
from mapswipe_workers.utils import user_management
2926
from mapswipe_workers.utils.create_directories import create_directories
3027
from mapswipe_workers.utils.slack_helper import (
31-
send_slack_message,
3228
send_progress_notification,
29+
send_slack_message,
3330
)
3431

3532

@@ -63,12 +60,6 @@ def run_create_projects():
6360
Save created projects, groups and tasks to Firebase and Postgres.
6461
"""
6562

66-
project_type_classes = {
67-
1: BuildAreaProject,
68-
2: FootprintProject,
69-
3: ChangeDetectionProject,
70-
}
71-
7263
fb_db = auth.firebaseDB()
7364
ref = fb_db.reference("v2/projectDrafts/")
7465
project_drafts = ref.get()
@@ -83,7 +74,7 @@ def run_create_projects():
8374
project_name = project_draft["name"]
8475
try:
8576
# Create a project object using appropriate class (project type).
86-
project = project_type_classes[project_type](project_draft)
77+
project = ProjectType(project_type).constructor(project_draft)
8778
project.geometry = project.validate_geometries()
8879
project.create_groups()
8980
project.calc_required_results()
@@ -176,16 +167,9 @@ def run_create_tutorial(input_file) -> None:
176167
try:
177168
logger.info(f"will generate tutorial based on {input_file}")
178169
with open(input_file) as json_file:
179-
tutorial = json.load(json_file)
180-
181-
project_type = tutorial["projectType"]
182-
183-
project_types_tutorial = {
184-
# Make sure to import all project types here
185-
1: build_area_tutorial.create_tutorial,
186-
3: change_detection_tutorial.create_tutorial,
187-
}
188-
project_types_tutorial[project_type](tutorial)
170+
tutorial_data = json.load(json_file)
171+
project_type = tutorial_data["projectType"]
172+
ProjectType(project_type).tutorial(tutorial_data)
189173
except Exception:
190174
logger.exception()
191175
sentry.capture_exception()

mapswipe_workers/mapswipe_workers/base/__init__.py renamed to mapswipe_workers/mapswipe_workers/project_types/arbitrary_geometry/__init__.py

File renamed without changes.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from typing import List
2+
3+
from mapswipe_workers.project_types.base.group import BaseGroup
4+
from mapswipe_workers.project_types.arbitrary_geometry.task import Task
5+
6+
7+
class Group(BaseGroup):
8+
def __init__(self, project: object, groupId: int) -> None:
9+
super().__init__(project, groupId)
10+
11+
def create_tasks(self, feature_ids: List, feature_geometries: List) -> None:
12+
"""Create tasks for a group
13+
14+
feature_geometries is a list of geometries or feature in geojson format.
15+
These consist two keys: Coordinates and type.
16+
Coordinates of four two pair coordinates.
17+
Every coordinate pair is a vertex.
18+
"""
19+
for i in range(0, len(feature_ids)):
20+
task = Task(self, feature_ids[i], feature_geometries[i])
21+
self.tasks.append(task)
22+
self.numberOfTasks = len(self.tasks)

mapswipe_workers/mapswipe_workers/project_types/footprint/grouping_functions.py renamed to mapswipe_workers/mapswipe_workers/project_types/arbitrary_geometry/grouping_functions.py

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,26 @@
44

55

66
########################################################################################################################
7-
parser = argparse.ArgumentParser(description='Process some integers.')
8-
parser.add_argument('-i', '--input_file', required=False, default=None, type=str,
9-
help='the input file containning the geometries as geojson')
10-
parser.add_argument('-g', '--group_size', required=False, default=50, type=int,
11-
help='the size of each group')
7+
parser = argparse.ArgumentParser(description="Process some integers.")
8+
parser.add_argument(
9+
"-i",
10+
"--input_file",
11+
required=False,
12+
default=None,
13+
type=str,
14+
help="the input file containning the geometries as geojson",
15+
)
16+
parser.add_argument(
17+
"-g",
18+
"--group_size",
19+
required=False,
20+
default=50,
21+
type=int,
22+
help="the size of each group",
23+
)
1224
########################################################################################################################
1325

26+
1427
def group_input_geometries(input_geometries_file, group_size):
1528
"""
1629
The function to create groups of input geometries using the given size (number of features) per group
@@ -28,43 +41,42 @@ def group_input_geometries(input_geometries_file, group_size):
2841
the dictionary containing a list of "feature_ids" and a list of "feature_geometries" per group with given group id key
2942
"""
3043

31-
driver = ogr.GetDriverByName('GeoJSON')
44+
driver = ogr.GetDriverByName("GeoJSON")
3245
datasource = driver.Open(input_geometries_file, 0)
3346
layer = datasource.GetLayer()
3447

3548
groups = {}
3649

3750
# we will simply, we will start with min group id = 100
3851
group_id = 100
39-
group_id_string = f'g{group_id}'
52+
group_id_string = f"g{group_id}"
4053
feature_count = 0
4154
for feature in layer:
4255
feature_count += 1
43-
if feature_count % (group_size+1) == 0:
56+
if feature_count % (group_size + 1) == 0:
4457
group_id += 1
45-
group_id_string = f'g{group_id}'
58+
group_id_string = f"g{group_id}"
4659

4760
try:
4861
groups[group_id_string]
4962
except:
50-
groups[group_id_string] = {
51-
"feature_ids": [],
52-
"feature_geometries": []
53-
}
63+
groups[group_id_string] = {"feature_ids": [], "feature_geometries": []}
5464

55-
groups[group_id_string]['feature_ids'].append(feature.GetFID())
56-
groups[group_id_string]['feature_geometries'].append(json.loads(feature.GetGeometryRef().ExportToJson()))
65+
groups[group_id_string]["feature_ids"].append(feature.GetFID())
66+
groups[group_id_string]["feature_geometries"].append(
67+
json.loads(feature.GetGeometryRef().ExportToJson())
68+
)
5769

5870
return groups
5971

6072

6173
########################################################################################################################
62-
if __name__ == '__main__':
74+
if __name__ == "__main__":
6375

6476
try:
6577
args = parser.parse_args()
6678
except:
67-
print('have a look at the input arguments, something went wrong there.')
79+
print("have a look at the input arguments, something went wrong there.")
6880

6981
groups = group_input_geometries(args.input_file, args.group_size)
7082

mapswipe_workers/mapswipe_workers/project_types/footprint/footprint_project.py renamed to mapswipe_workers/mapswipe_workers/project_types/arbitrary_geometry/project.py

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,15 @@
11
import os
22
import urllib.request
3-
from osgeo import ogr
4-
5-
from mapswipe_workers.definitions import DATA_PATH
6-
from mapswipe_workers.definitions import CustomError
7-
from mapswipe_workers.definitions import logger
8-
from mapswipe_workers.base.base_project import BaseProject
9-
from mapswipe_workers.project_types.footprint import grouping_functions as g
10-
from mapswipe_workers.project_types.footprint.footprint_group import FootprintGroup
11-
123

13-
class FootprintProject(BaseProject):
14-
"""
15-
The subclass for an import of the type Footprint
16-
"""
4+
from mapswipe_workers.project_types.base.project import BaseProject
5+
from mapswipe_workers.definitions import DATA_PATH, CustomError, logger
6+
from mapswipe_workers.project_types.arbitrary_geometry import grouping_functions as g
7+
from mapswipe_workers.project_types.arbitrary_geometry.group import Group
8+
from osgeo import ogr
179

18-
project_type = 2
19-
project_type_name = "Footprint"
2010

21-
def __init__(self, project_draft):
22-
# this will create the basis attributes
11+
class Project(BaseProject):
12+
def __init__(self, project_draft: dict) -> None:
2313
super().__init__(project_draft)
2414

2515
# set group size
@@ -80,7 +70,7 @@ def validate_geometries(self):
8070
raise Exception(err)
8171

8272
# get geometry as wkt
83-
# for footprint type project we get the bounding box / extent of the layer
73+
# get the bounding box/ extent of the layer
8474
extent = layer.GetExtent()
8575
# Create a Polygon from the extent tuple
8676
ring = ogr.Geometry(ogr.wkbLinearRing)
@@ -148,14 +138,10 @@ def validate_geometries(self):
148138
return wkt_geometry
149139

150140
def create_groups(self):
151-
"""
152-
The function to create groups of footprint geometries
153-
"""
154-
155141
raw_groups = g.group_input_geometries(self.validInputGeometries, self.groupSize)
156142

157143
for group_id, item in raw_groups.items():
158-
group = FootprintGroup(self, group_id)
144+
group = Group(self, group_id)
159145
group.create_tasks(item["feature_ids"], item["feature_geometries"])
160146
self.groups.append(group)
161147

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from mapswipe_workers.project_types.base.task import BaseTask
2+
from osgeo import ogr
3+
4+
5+
class Task(BaseTask):
6+
def __init__(self, group: object, featureId: int, featureGeometry: dict):
7+
"""
8+
Parameters
9+
----------
10+
feature_geometry: dict
11+
The geometries or feature in geojson format.
12+
It consist of two keys: Coordinates and type.
13+
Coordinates of four two pair coordinates.
14+
Every coordinate pair is a vertex.
15+
"""
16+
task_id = f"t{featureId}"
17+
super().__init__(group, taskId=task_id)
18+
self.geojson = featureGeometry
19+
20+
# create wkt geometry from geojson
21+
poly = ogr.CreateGeometryFromJson(str(featureGeometry))
22+
wkt_geometry = poly.ExportToWkt()
23+
self.geometry = wkt_geometry

mapswipe_workers/mapswipe_workers/project_types/build_area/__init__.py renamed to mapswipe_workers/mapswipe_workers/project_types/base/__init__.py

File renamed without changes.

mapswipe_workers/mapswipe_workers/base/base_group.py renamed to mapswipe_workers/mapswipe_workers/project_types/base/group.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ class BaseGroup(metaclass=ABCMeta):
1414
completedCount: int
1515
Number of users who finished the group
1616
neededCount: int
17-
Required number of users left to finish the group. Decreases with the increase of completedCount
17+
Required number of users left to finish the group.
18+
Decreases with the increase of completedCount.
1819
count: int
1920
Number of tasks associated with the group
2021
"""
@@ -45,11 +46,11 @@ def __init__(self, project, groupId):
4546

4647
@abstractmethod
4748
def create_tasks():
48-
'''
49+
"""
4950
Create tasks as task object for one group
5051
and appends those to a tasks list of the group object.
5152
5253
The number of tasks has to be calculated
5354
and saved to the numberOfTasks attribute of the group object.
54-
'''
55+
"""
5556
pass

mapswipe_workers/mapswipe_workers/base/base_project.py renamed to mapswipe_workers/mapswipe_workers/project_types/base/project.py

File renamed without changes.

0 commit comments

Comments
 (0)