Skip to content

Commit eab6d64

Browse files
authored
Release/2.14.5 (#436)
* Python library updates * added python static liveness check (#425) * update version files
1 parent 3b2454e commit eab6d64

File tree

12 files changed

+872
-548
lines changed

12 files changed

+872
-548
lines changed

examples/doc_scan/app.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ def create_session():
4747
.with_preset_issuing_country("GBR")
4848
.with_success_url("{url}/success".format(url=YOTI_APP_BASE_URL))
4949
.with_error_url("{url}/error".format(url=YOTI_APP_BASE_URL))
50+
.with_privacy_policy_url("{url}/privacy-policy".format(url=YOTI_APP_BASE_URL))
5051
.build()
5152
)
5253

examples/doc_scan/templates/success.html

Lines changed: 595 additions & 541 deletions
Large diffs are not rendered by default.

sonar-project.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ sonar.host.url = https://sonarcloud.io
22
sonar.organization = getyoti
33
sonar.projectKey = getyoti:python
44
sonar.projectName = Python SDK
5-
sonar.projectVersion = 2.14.4
5+
sonar.projectVersion = 2.14.5
66
sonar.exclusions = yoti_python_sdk/tests/**,examples/**,yoti_python_sdk/protobuf/**/*
77

88
sonar.python.pylint.reportPath = coverage.out

yoti_python_sdk/doc_scan/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
ID_DOCUMENT_FACE_MATCH = "ID_DOCUMENT_FACE_MATCH"
99
LIVENESS = "LIVENESS"
1010
ZOOM = "ZOOM"
11+
STATIC = "STATIC"
1112
SUPPLEMENTARY_DOCUMENT_TEXT_DATA_CHECK = "SUPPLEMENTARY_DOCUMENT_TEXT_DATA_CHECK"
1213
SUPPLEMENTARY_DOCUMENT_TEXT_DATA_EXTRACTION = (
1314
"SUPPLEMENTARY_DOCUMENT_TEXT_DATA_EXTRACTION"

yoti_python_sdk/doc_scan/session/create/check/liveness.py

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,18 @@ class RequestedLivenessCheckConfig(YotiSerializable):
1111
The configuration applied when creating a Liveness Check
1212
"""
1313

14-
def __init__(self, liveness_type, max_retries):
14+
def __init__(self, liveness_type, max_retries, manual_check=None):
1515
"""
1616
:param liveness_type: the liveness type
1717
:type liveness_type: str
1818
:param max_retries: the maximum number of retries
1919
:type max_retries: int
20+
:param manual_check: the manual check value
21+
:type manual_check: str or None
2022
"""
2123
self.__liveness_type = liveness_type
2224
self.__max_retries = max_retries
25+
self.__manual_check = manual_check
2326

2427
@property
2528
def liveness_type(self):
@@ -39,9 +42,23 @@ def max_retries(self):
3942
"""
4043
return self.__max_retries
4144

45+
@property
46+
def manual_check(self):
47+
"""
48+
The manual check value for the liveness check
49+
50+
:return: the manual check value
51+
:rtype: str or None
52+
"""
53+
return self.__manual_check
54+
4255
def to_json(self):
4356
return remove_null_values(
44-
{"liveness_type": self.liveness_type, "max_retries": self.max_retries}
57+
{
58+
"liveness_type": self.liveness_type,
59+
"max_retries": self.max_retries,
60+
"manual_check": self.manual_check,
61+
}
4562
)
4663

4764

@@ -74,6 +91,7 @@ class RequestedLivenessCheckBuilder(object):
7491
def __init__(self):
7592
self.__liveness_type = None
7693
self.__max_retries = None
94+
self.__manual_check = None
7795

7896
def for_zoom_liveness(self):
7997
"""
@@ -84,6 +102,15 @@ def for_zoom_liveness(self):
84102
"""
85103
return self.with_liveness_type(constants.ZOOM)
86104

105+
def for_static_liveness(self):
106+
"""
107+
Sets the liveness type to "STATIC"
108+
109+
:return: the builder
110+
:rtype: RequestedLivenessCheckBuilder
111+
"""
112+
return self.with_liveness_type(constants.STATIC)
113+
87114
def with_liveness_type(self, liveness_type):
88115
"""
89116
Sets the liveness type on the builder
@@ -109,6 +136,18 @@ def with_max_retries(self, max_retries):
109136
self.__max_retries = max_retries
110137
return self
111138

139+
def with_manual_check_never(self):
140+
"""
141+
Sets the manual check value to "NEVER"
142+
143+
:return: the builder
144+
:rtype: RequestedLivenessCheckBuilder
145+
"""
146+
self.__manual_check = constants.NEVER
147+
return self
148+
112149
def build(self):
113-
config = RequestedLivenessCheckConfig(self.__liveness_type, self.__max_retries)
150+
config = RequestedLivenessCheckConfig(
151+
self.__liveness_type, self.__max_retries, self.__manual_check
152+
)
114153
return RequestedLivenessCheck(config)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# -*- coding: utf-8 -*-
2+
from __future__ import unicode_literals
3+
4+
from .media_response import MediaResponse
5+
6+
7+
class ImageResponse(object):
8+
"""
9+
Represents an image resource within a static liveness check
10+
"""
11+
12+
def __init__(self, data=None):
13+
"""
14+
:param data: the data to parse
15+
:type data: dict or None
16+
"""
17+
if data is None:
18+
data = dict()
19+
20+
self.__media = (
21+
MediaResponse(data["media"]) if "media" in data.keys() else None
22+
)
23+
24+
@property
25+
def media(self):
26+
"""
27+
Returns the media information for the image
28+
29+
:return: the media
30+
:rtype: MediaResponse or None
31+
"""
32+
return self.__media

yoti_python_sdk/doc_scan/session/retrieve/resource_container.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
LivenessResourceResponse,
1212
ZoomLivenessResourceResponse,
1313
)
14+
from yoti_python_sdk.doc_scan.session.retrieve.static_liveness_resource_response import (
15+
StaticLivenessResourceResponse,
16+
)
1417

1518

1619
class ResourceContainer(object):
@@ -54,7 +57,7 @@ def __parse_liveness_capture(liveness_capture):
5457
:return: the parsed liveness capture
5558
:rtype: LivenessResourceResponse
5659
"""
57-
types = {"ZOOM": ZoomLivenessResourceResponse}
60+
types = {"ZOOM": ZoomLivenessResourceResponse, "STATIC": StaticLivenessResourceResponse}
5861

5962
clazz = types.get(
6063
liveness_capture.get("liveness_type", None),
@@ -105,3 +108,17 @@ def zoom_liveness_resources(self):
105108
for liveness in self.__liveness_capture
106109
if isinstance(liveness, ZoomLivenessResourceResponse)
107110
]
111+
112+
@property
113+
def static_liveness_resources(self):
114+
"""
115+
Returns a filtered list of static liveness capture resources
116+
117+
:return: list of static liveness captures
118+
:rtype: list[StaticLivenessResourceResponse]
119+
"""
120+
return [
121+
liveness
122+
for liveness in self.__liveness_capture
123+
if isinstance(liveness, StaticLivenessResourceResponse)
124+
]
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# -*- coding: utf-8 -*-
2+
from __future__ import unicode_literals
3+
4+
from .liveness_resource_response import LivenessResourceResponse
5+
from .image_response import ImageResponse
6+
7+
8+
class StaticLivenessResourceResponse(LivenessResourceResponse):
9+
"""
10+
Represents a Static Liveness resource for a given session
11+
"""
12+
13+
def __init__(self, data=None):
14+
"""
15+
:param data: the data to parse
16+
:type data: dict or None
17+
"""
18+
if data is None:
19+
data = dict()
20+
21+
LivenessResourceResponse.__init__(self, data)
22+
23+
self.__image = (
24+
ImageResponse(data["image"]) if "image" in data.keys() else None
25+
)
26+
27+
@property
28+
def image(self):
29+
"""
30+
Returns the associated image for the static liveness resource
31+
32+
:return: the image
33+
:rtype: ImageResponse or None
34+
"""
35+
return self.__image

yoti_python_sdk/tests/doc_scan/session/create/check/test_liveness_check.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,67 @@ def test_should_serialize_to_json_without_error(self):
5252
s = json.dumps(result, cls=YotiEncoder)
5353
assert s is not None and s != ""
5454

55+
def test_should_build_with_static_liveness_type(self):
56+
result = (
57+
RequestedLivenessCheckBuilder()
58+
.for_static_liveness()
59+
.with_max_retries(3)
60+
.build()
61+
)
62+
63+
assert result.type == "LIVENESS"
64+
assert result.config.liveness_type == "STATIC"
65+
assert result.config.max_retries == 3
66+
67+
def test_should_build_with_manual_check_never(self):
68+
result = (
69+
RequestedLivenessCheckBuilder()
70+
.for_static_liveness()
71+
.with_max_retries(3)
72+
.with_manual_check_never()
73+
.build()
74+
)
75+
76+
assert result.config.liveness_type == "STATIC"
77+
assert result.config.manual_check == "NEVER"
78+
79+
def test_should_serialize_static_liveness_to_json(self):
80+
result = (
81+
RequestedLivenessCheckBuilder()
82+
.for_static_liveness()
83+
.with_max_retries(3)
84+
.with_manual_check_never()
85+
.build()
86+
)
87+
88+
json_str = json.dumps(result, cls=YotiEncoder)
89+
assert json_str is not None
90+
91+
# Verify the JSON contains the expected fields
92+
json_data = json.loads(json_str)
93+
assert json_data["type"] == "LIVENESS"
94+
assert json_data["config"]["liveness_type"] == "STATIC"
95+
assert json_data["config"]["manual_check"] == "NEVER"
96+
assert json_data["config"]["max_retries"] == 3
97+
98+
def test_should_omit_manual_check_when_not_set(self):
99+
result = (
100+
RequestedLivenessCheckBuilder()
101+
.for_static_liveness()
102+
.with_max_retries(3)
103+
.build()
104+
)
105+
106+
json_str = json.dumps(result, cls=YotiEncoder)
107+
assert json_str is not None
108+
109+
# Verify the JSON does not contain the manual_check field
110+
json_data = json.loads(json_str)
111+
assert json_data["type"] == "LIVENESS"
112+
assert json_data["config"]["liveness_type"] == "STATIC"
113+
assert "manual_check" not in json_data["config"]
114+
assert json_data["config"]["max_retries"] == 3
115+
55116

56117
if __name__ == "__main__":
57118
unittest.main()

yoti_python_sdk/tests/doc_scan/session/retrieve/test_resource_container.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,19 @@ def test_should_filter_zoom_liveness_resources(self):
4949
assert len(result.liveness_capture) == 2
5050
assert len(result.zoom_liveness_resources) == 1
5151

52+
def test_should_filter_static_liveness_resources(self):
53+
data = {
54+
"liveness_capture": [
55+
{"liveness_type": "STATIC"},
56+
{"liveness_type": "someUnknown"},
57+
]
58+
}
59+
60+
result = ResourceContainer(data)
61+
62+
assert len(result.liveness_capture) == 2
63+
assert len(result.static_liveness_resources) == 1
64+
5265

5366
if __name__ == "__main__":
5467
unittest.main()

0 commit comments

Comments
 (0)