Skip to content

Commit 8d91cf2

Browse files
committed
WIP: Introduce marker_content_factory.py
- MarkerContentFactory returns the appropriate MarkerContent implementation (i.e. either Pin or Image) - Add unit tests
1 parent fe89c79 commit 8d91cf2

File tree

10 files changed

+172
-48
lines changed

10 files changed

+172
-48
lines changed

examples/simple.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
from flask import Flask, render_template
22

33
from flask_googlemaps import GoogleMaps, Map
4-
from flask_googlemaps.image import Image
5-
from flask_googlemaps.pin import Pin
64

75
app = Flask(__name__)
86
GoogleMaps(app)
97

8+
pin_content = {
9+
"border_color": "",
10+
"glyph_colors": "",
11+
"background": "",
12+
"glyph": "",
13+
"scale": 2.0,
14+
}
15+
image_content = {"icon_urls": "https://img.shields.io/badge/PayPal-Donante-red.svg"}
16+
1017

1118
@app.route("/")
1219
def map_created_in_view():
@@ -20,13 +27,12 @@ def map_created_in_view():
2027
"latitude": 37.4419,
2128
"longitude": -122.1419,
2229
"label": "1",
23-
# 'content': Pin(border_color="blue", background="blue")
24-
"content": Pin(border_color="blue", background="blue"),
30+
"content": pin_content,
2531
},
2632
{
2733
"latitude": 37.4519,
2834
"longitude": -122.1519,
29-
"content": Pin(border_color="blue", background="blue"),
35+
"content": image_content,
3036
},
3137
],
3238
style="height:400px;width:600px;margin:0;",

flask_googlemaps/image.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55

66
@dataclass
77
class Image(MarkerContent):
8-
url: str
8+
icon_url: str
99

1010
def content(self) -> str:
1111
return self.name
1212

1313
def dom_element(self) -> str:
1414
return (
1515
f"const {self.name} = document.createElement('img');\n"
16-
f"{self.name}.src = '{self.url}';"
16+
f"{self.name}.src = '{self.icon_url}';"
1717
)

flask_googlemaps/marker.py

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,27 @@
11
from typing import Optional, List, Any, Dict
22

3-
from flask_googlemaps.marker_content import MarkerContent
3+
from flask_googlemaps.marker_content_factory import MarkerContentFactory
44
from flask_googlemaps.pin import Pin
55

66
DEFAULT_PIN = Pin()
77

88

9-
# from json import JSONEncoder
10-
#
11-
#
12-
# def wrapped_default(self, obj):
13-
# return getattr(obj.__class__, "__json__", wrapped_default.default)(obj)
14-
#
15-
#
16-
# wrapped_default.default = JSONEncoder().default
17-
#
18-
# # apply the patch
19-
# JSONEncoder.original_default = JSONEncoder.default
20-
# JSONEncoder.default = wrapped_default
21-
22-
239
class Marker(dict):
2410

2511
def __init__(
2612
self,
2713
latitude: float,
2814
longitude: float,
2915
infobox: Optional[str] = None,
30-
content: Optional[MarkerContent] = None,
16+
content: Optional[dict] = None,
3117
label: Optional[str] = None,
3218
):
3319
self.latitude = Marker.verify_latitude(latitude)
3420
self.longitude = Marker.verify_longitude(longitude)
3521
self.infobox = infobox
36-
self.marker_icon = content
37-
self.content = content.content()
38-
self.dom_element = content.dom_element()
22+
self.marker_content = MarkerContentFactory(**content).marker_content
23+
self.content = self.marker_content.content()
24+
self.dom_element = self.marker_content.dom_element()
3925
self.label = label
4026

4127
dict.__init__(
@@ -45,7 +31,7 @@ def __init__(
4531
"longitude": self.longitude,
4632
"infobox": self.infobox,
4733
"content": self.content,
48-
"id": self.marker_icon.name,
34+
"id": self.marker_content.name,
4935
},
5036
)
5137

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
from enum import Enum
2+
from typing import Optional
3+
4+
from flask_googlemaps.image import Image
5+
from flask_googlemaps.marker_content import MarkerContent
6+
from flask_googlemaps.pin import Pin
7+
8+
9+
class MarkerContentType(Enum):
10+
PIN = 1
11+
IMAGE = 2
12+
13+
14+
class MarkerContentFactory:
15+
16+
def __init__(self, **kwargs):
17+
self.kwargs = kwargs
18+
if "icon_url" in kwargs:
19+
self.__content_type = MarkerContentType.IMAGE
20+
elif (
21+
"border_color" in kwargs
22+
or "glyph_color" in kwargs
23+
or "background" in kwargs
24+
or "glyph" in kwargs
25+
or "scale" in kwargs
26+
):
27+
self.__content_type = MarkerContentType.PIN
28+
else:
29+
self.__content_type = None
30+
31+
@property
32+
def marker_content(self) -> Optional[MarkerContent]:
33+
if self.__content_type is MarkerContentType.PIN:
34+
try:
35+
return Pin(**self.kwargs)
36+
except TypeError as e:
37+
print(e)
38+
return Pin()
39+
elif self.__content_type is MarkerContentType.IMAGE:
40+
try:
41+
return Image(**self.kwargs)
42+
except TypeError as e:
43+
print(e)
44+
return Pin()
45+
else:
46+
return Pin()

flask_googlemaps/pin.py

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,39 @@
1-
from dataclasses import dataclass
1+
from dataclasses import dataclass, fields
2+
from enum import Enum
23
from typing import Optional
34

45
from flask_googlemaps.marker_content import MarkerContent
56

7+
pin_property_mapping = {
8+
"glyph_color": "glyphColor",
9+
"background": "background",
10+
"border_color": "borderColor",
11+
"glyph": "glyph",
12+
"scale": "scale",
13+
}
14+
15+
16+
class PinPropertyMapping(Enum):
17+
glyph_color = "glyphColor"
18+
background = "background"
19+
border_color = "borderColor"
20+
glyph = "glyph"
21+
scale = "scale"
22+
623

724
@dataclass
825
class Pin(MarkerContent):
926

1027
def __post_init__(self):
1128
MarkerContent.__post_init__(self)
1229
self.dom = []
13-
if self.glyph_color:
14-
self.dom.append(f"\tglyphColor: '{self.glyph_color}',")
15-
if self.background:
16-
self.dom.append(f"\tbackground: '{self.background}',")
17-
if self.border_color:
18-
self.dom.append(f"\tborderColor: '{self.border_color}',")
19-
if self.glyph:
20-
self.dom.append(f"\tglyph: '{self.glyph}',")
30+
for field in fields(Pin):
31+
if self.__getattribute__(field.name):
32+
self.dom.append(
33+
f"\t{PinPropertyMapping.__getitem__(field.name).value}: "
34+
f"'{self.__getattribute__(field.name)}',"
35+
)
36+
2137
if self.dom:
2238
self.dom.insert(0, f"const {self.name} = new PinElement({{")
2339
self.dom.append("\t\t});\n")
@@ -32,7 +48,7 @@ def dom_element(self) -> Optional[str]:
3248
glyph_color: str = ""
3349
background: str = ""
3450
glyph: Optional[str] = ""
35-
scale: float = 1.0
51+
scale: Optional[float] = None
3652

3753
def content(self) -> str:
3854
if self.dom:

tests/test_image.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
@pytest.fixture
77
def image():
88
return Image(
9-
url="https://developers.google.com/maps/documentation/"
9+
icon_url="https://developers.google.com/maps/documentation/"
1010
"javascript/examples/full/images/beachflag.png"
1111
)
1212

@@ -20,4 +20,4 @@ def test_content(image):
2020
def test_dom_image_element(image):
2121
variable, url_set = image.dom_element().split("\n")
2222
assert variable == f"const {image.name} = document.createElement('img');"
23-
assert url_set == f"{image.name}.src = '{image.url}';"
23+
assert url_set == f"{image.name}.src = '{image.icon_url}';"

tests/test_marker.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ def marker_pin_object() -> Marker:
1111
return Marker(
1212
latitude=37.4419,
1313
longitude=-122.1419,
14-
pin=Pin(border_color="", glyph_color="", background=""),
14+
content=dict(border_color="", glyph_color="", background=""),
1515
infobox="<b>Hello World</b>",
1616
)
1717

@@ -21,9 +21,11 @@ def marker_pin_url() -> Marker:
2121
return Marker(
2222
latitude=37.4419,
2323
longitude=-122.1419,
24-
pin="https://developers.google.com/maps/"
25-
"documentation/javascript/examples/"
26-
"full/images/beachflag.png",
24+
content={
25+
"icon_url": "https://developers.google.com/maps/"
26+
"documentation/javascript/examples/"
27+
"full/images/beachflag.png"
28+
},
2729
infobox="<b>Hello World</b>",
2830
)
2931

@@ -68,4 +70,4 @@ def test_marker_with_wrong_longitude():
6870

6971

7072
def test_marker_with_pin_object(marker_pin_object):
71-
assert isinstance(marker_pin_object.pin, Pin)
73+
assert isinstance(marker_pin_object.marker_content, Pin)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from flask_googlemaps.marker_content_factory import MarkerContentFactory
2+
from flask_googlemaps.pin import Pin
3+
4+
5+
def test_marker_content_unknown_keys():
6+
marker_content_factory = MarkerContentFactory(**{"bla": "bla"})
7+
marker_content = marker_content_factory.marker_content
8+
assert isinstance(marker_content, Pin)
9+
assert marker_content.dom_element() is None
10+
11+
12+
def test_marker_content_unknown():
13+
marker_content_factory = MarkerContentFactory(
14+
**{"background": "", "glyph_colors": ""}
15+
)
16+
marker_content = marker_content_factory.marker_content
17+
assert isinstance(marker_content, Pin)
18+
assert marker_content.dom_element() is None

tests/test_pin.py

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,63 @@
1+
import json
2+
13
import pytest
24

3-
from flask_googlemaps.pin import Pin
5+
from flask_googlemaps.pin import Pin, PinPropertyMapping
46

57

68
@pytest.fixture
7-
def pin():
9+
def empty_pin():
810
return Pin()
911

1012

13+
@pytest.fixture
14+
def pin():
15+
return Pin(border_color="red", scale=2.0)
16+
17+
18+
def test_content_empty(empty_pin):
19+
content = empty_pin.content()
20+
assert content == ""
21+
22+
1123
def test_content(pin):
1224
content = pin.content()
13-
assert False
25+
assert content.startswith("var_")
26+
assert len(content) == 20
27+
28+
29+
@pytest.mark.parametrize(
30+
"p",
31+
[
32+
Pin(border_color="blue", scale=3.0),
33+
Pin(background="red", glyph_color="black", scale=2.0),
34+
Pin(glyph="Test", border_color="red"),
35+
],
36+
)
37+
def test_dom_element(p):
38+
dom_element = p.dom_element()
39+
_, js_element = dom_element.split(" = ")
40+
41+
js_element = js_element_to_dict(js_element)
42+
reversed_dict = {item.value: item.name for item in list(PinPropertyMapping)}
43+
44+
for property_name, value in js_element.items():
45+
if property_name == "scale":
46+
value = float(value)
47+
assert value == p.__getattribute__(reversed_dict[property_name])
48+
49+
50+
def js_element_to_dict(js_element: str) -> dict:
51+
js_element = (
52+
js_element.strip("new PinElement(")
53+
.strip(");\n")
54+
.replace("\n", "")
55+
.replace("\t", "")
56+
.replace("'", '"')
57+
.replace("{", '{"')
58+
.replace(",}", "}")
59+
.replace(",", ',"')
60+
.replace(":", '":')
61+
)
62+
63+
return json.loads(js_element)

tests/test_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22

33

44
def test_version():
5-
assert __version__ == "0.4.1"
5+
assert __version__ == "0.5.0"

0 commit comments

Comments
 (0)