Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,136 changes: 595 additions & 541 deletions examples/doc_scan/templates/success.html

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions yoti_python_sdk/doc_scan/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
ID_DOCUMENT_FACE_MATCH = "ID_DOCUMENT_FACE_MATCH"
LIVENESS = "LIVENESS"
ZOOM = "ZOOM"
STATIC = "STATIC"
SUPPLEMENTARY_DOCUMENT_TEXT_DATA_CHECK = "SUPPLEMENTARY_DOCUMENT_TEXT_DATA_CHECK"
SUPPLEMENTARY_DOCUMENT_TEXT_DATA_EXTRACTION = (
"SUPPLEMENTARY_DOCUMENT_TEXT_DATA_EXTRACTION"
Expand Down
45 changes: 42 additions & 3 deletions yoti_python_sdk/doc_scan/session/create/check/liveness.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,18 @@ class RequestedLivenessCheckConfig(YotiSerializable):
The configuration applied when creating a Liveness Check
"""

def __init__(self, liveness_type, max_retries):
def __init__(self, liveness_type, max_retries, manual_check=None):
"""
:param liveness_type: the liveness type
:type liveness_type: str
:param max_retries: the maximum number of retries
:type max_retries: int
:param manual_check: the manual check value
:type manual_check: str or None
"""
self.__liveness_type = liveness_type
self.__max_retries = max_retries
self.__manual_check = manual_check

@property
def liveness_type(self):
Expand All @@ -39,9 +42,23 @@ def max_retries(self):
"""
return self.__max_retries

@property
def manual_check(self):
"""
The manual check value for the liveness check

:return: the manual check value
:rtype: str or None
"""
return self.__manual_check

def to_json(self):
return remove_null_values(
{"liveness_type": self.liveness_type, "max_retries": self.max_retries}
{
"liveness_type": self.liveness_type,
"max_retries": self.max_retries,
"manual_check": self.manual_check,
}
)


Expand Down Expand Up @@ -74,6 +91,7 @@ class RequestedLivenessCheckBuilder(object):
def __init__(self):
self.__liveness_type = None
self.__max_retries = None
self.__manual_check = None

def for_zoom_liveness(self):
"""
Expand All @@ -84,6 +102,15 @@ def for_zoom_liveness(self):
"""
return self.with_liveness_type(constants.ZOOM)

def for_static_liveness(self):
"""
Sets the liveness type to "STATIC"

:return: the builder
:rtype: RequestedLivenessCheckBuilder
"""
return self.with_liveness_type(constants.STATIC)

def with_liveness_type(self, liveness_type):
"""
Sets the liveness type on the builder
Expand All @@ -109,6 +136,18 @@ def with_max_retries(self, max_retries):
self.__max_retries = max_retries
return self

def with_manual_check_never(self):
"""
Sets the manual check value to "NEVER"

:return: the builder
:rtype: RequestedLivenessCheckBuilder
"""
self.__manual_check = constants.NEVER
return self

def build(self):
config = RequestedLivenessCheckConfig(self.__liveness_type, self.__max_retries)
config = RequestedLivenessCheckConfig(
self.__liveness_type, self.__max_retries, self.__manual_check
)
return RequestedLivenessCheck(config)
32 changes: 32 additions & 0 deletions yoti_python_sdk/doc_scan/session/retrieve/image_response.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from .media_response import MediaResponse


class ImageResponse(object):
"""
Represents an image resource within a static liveness check
"""

def __init__(self, data=None):
"""
:param data: the data to parse
:type data: dict or None
"""
if data is None:
data = dict()

Check warning on line 18 in yoti_python_sdk/doc_scan/session/retrieve/image_response.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace this constructor call with a literal.

See more on https://sonarcloud.io/project/issues?id=getyoti%3Apython&issues=AZrBJbNRGoChscpF8vbY&open=AZrBJbNRGoChscpF8vbY&pullRequest=425

self.__media = (
MediaResponse(data["media"]) if "media" in data.keys() else None
)

@property
def media(self):
"""
Returns the media information for the image

:return: the media
:rtype: MediaResponse or None
"""
return self.__media
19 changes: 18 additions & 1 deletion yoti_python_sdk/doc_scan/session/retrieve/resource_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
LivenessResourceResponse,
ZoomLivenessResourceResponse,
)
from yoti_python_sdk.doc_scan.session.retrieve.static_liveness_resource_response import (
StaticLivenessResourceResponse,
)


class ResourceContainer(object):
Expand Down Expand Up @@ -54,7 +57,7 @@ def __parse_liveness_capture(liveness_capture):
:return: the parsed liveness capture
:rtype: LivenessResourceResponse
"""
types = {"ZOOM": ZoomLivenessResourceResponse}
types = {"ZOOM": ZoomLivenessResourceResponse, "STATIC": StaticLivenessResourceResponse}

clazz = types.get(
liveness_capture.get("liveness_type", None),
Expand Down Expand Up @@ -105,3 +108,17 @@ def zoom_liveness_resources(self):
for liveness in self.__liveness_capture
if isinstance(liveness, ZoomLivenessResourceResponse)
]

@property
def static_liveness_resources(self):
"""
Returns a filtered list of static liveness capture resources

:return: list of static liveness captures
:rtype: list[StaticLivenessResourceResponse]
"""
return [
liveness
for liveness in self.__liveness_capture
if isinstance(liveness, StaticLivenessResourceResponse)
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from .resource_response import ResourceResponse
from .liveness_resource_response import LivenessResourceResponse
from .image_response import ImageResponse


class StaticLivenessResourceResponse(LivenessResourceResponse):
"""
Represents a Static Liveness resource for a given session
"""

def __init__(self, data=None):
"""
:param data: the data to parse
:type data: dict or None
"""
if data is None:
data = dict()

Check warning on line 20 in yoti_python_sdk/doc_scan/session/retrieve/static_liveness_resource_response.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace this constructor call with a literal.

See more on https://sonarcloud.io/project/issues?id=getyoti%3Apython&issues=AZrBJbMaGoChscpF8vbX&open=AZrBJbMaGoChscpF8vbX&pullRequest=425

LivenessResourceResponse.__init__(self, data)

self.__image = (
ImageResponse(data["image"]) if "image" in data.keys() else None
)

@property
def image(self):
"""
Returns the associated image for the static liveness resource
:return: the image
:rtype: ImageResponse or None
"""
return self.__image
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,49 @@ def test_should_serialize_to_json_without_error(self):
s = json.dumps(result, cls=YotiEncoder)
assert s is not None and s != ""

def test_should_build_with_static_liveness_type(self):
result = (
RequestedLivenessCheckBuilder()
.for_static_liveness()
.with_max_retries(3)
.build()
)

assert result.type == "LIVENESS"
assert result.config.liveness_type == "STATIC"
assert result.config.max_retries == 3

def test_should_build_with_manual_check_never(self):
result = (
RequestedLivenessCheckBuilder()
.for_static_liveness()
.with_max_retries(3)
.with_manual_check_never()
.build()
)

assert result.config.liveness_type == "STATIC"
assert result.config.manual_check == "NEVER"

def test_should_serialize_static_liveness_to_json(self):
result = (
RequestedLivenessCheckBuilder()
.for_static_liveness()
.with_max_retries(3)
.with_manual_check_never()
.build()
)

json_str = json.dumps(result, cls=YotiEncoder)
assert json_str is not None

# Verify the JSON contains the expected fields
json_data = json.loads(json_str)
assert json_data["type"] == "LIVENESS"
assert json_data["config"]["liveness_type"] == "STATIC"
assert json_data["config"]["manual_check"] == "NEVER"
assert json_data["config"]["max_retries"] == 3


if __name__ == "__main__":
unittest.main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import unittest
import json

from yoti_python_sdk.doc_scan.session.retrieve.static_liveness_resource_response import (
StaticLivenessResourceResponse,
)
from yoti_python_sdk.doc_scan.session.retrieve.image_response import ImageResponse
from yoti_python_sdk.doc_scan.session.retrieve.media_response import MediaResponse


class StaticLivenessResourceResponseTest(unittest.TestCase):
def test_should_parse_static_liveness_resource(self):
data = {
"id": "bbbbbbb-5717-4562-b3fc-2c963f66afa6",
"source": {"type": "END_USER"},
"liveness_type": "STATIC",
"image": {
"media": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"type": "IMAGE",
"created": "2021-06-11T11:39:24Z",
"last_updated": "2021-06-11T11:39:24Z",
}
},
"tasks": [],
}

result = StaticLivenessResourceResponse(data)

assert result.id == "bbbbbbb-5717-4562-b3fc-2c963f66afa6"
assert result.liveness_type == "STATIC"
assert isinstance(result.image, ImageResponse)
assert isinstance(result.image.media, MediaResponse)
assert result.image.media.id == "3fa85f64-5717-4562-b3fc-2c963f66afa6"
assert result.image.media.type == "IMAGE"

def test_should_handle_missing_image(self):
data = {
"id": "test-id",
"liveness_type": "STATIC",
"tasks": [],
}

result = StaticLivenessResourceResponse(data)

assert result.id == "test-id"
assert result.liveness_type == "STATIC"
assert result.image is None

def test_should_parse_media_id_for_retrieval(self):
data = {
"id": "resource-id",
"liveness_type": "STATIC",
"image": {
"media": {
"id": "media-id-123",
"type": "IMAGE",
"created": "2021-06-11T11:39:24Z",
"last_updated": "2021-06-11T11:39:24Z",
}
},
"tasks": [],
}

result = StaticLivenessResourceResponse(data)

# Verify we can access the media ID for content retrieval
assert result.image is not None
assert result.image.media is not None
assert result.image.media.id == "media-id-123"


if __name__ == "__main__":
unittest.main()
Loading