Skip to content

Commit ad075d3

Browse files
committed
SDK-765: Add DocumentImages attribute
1 parent b8068e9 commit ad075d3

18 files changed

+275
-84
lines changed

yoti_python_sdk/activity_details.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,14 @@ def __init__(self, receipt, decrypted_profile=None):
2929
self.base64_selfie_uri = value.base64_content()
3030
value = field.value # set value to be byte content, for backwards compatibility
3131

32-
self.user_profile[field.name] = value
32+
if field.name == config.ATTRIBUTE_STRUCTURED_POSTAL_ADDRESS:
33+
value = self.try_convert_structured_postal_address_to_dict(field)
3334

3435
if field.name.startswith(config.ATTRIBUTE_AGE_OVER) or field.name.startswith(
3536
config.ATTRIBUTE_AGE_UNDER):
3637
self.try_parse_age_verified_field(field)
3738

38-
if field.name == config.ATTRIBUTE_STRUCTURED_POSTAL_ADDRESS:
39-
self.try_convert_structured_postal_address_to_dict(field)
39+
self.user_profile[field.name] = value
4040

4141
except ValueError as ve:
4242
if logging.getLogger().propagate:
@@ -76,13 +76,14 @@ def try_parse_age_verified_field(self, field):
7676
print(
7777
"age_verified_field value: '{0}' was unable to be parsed into a boolean value".format(age_verified))
7878

79-
def try_convert_structured_postal_address_to_dict(self, field):
79+
@staticmethod
80+
def try_convert_structured_postal_address_to_dict(field):
8081
decoder = json.JSONDecoder(object_pairs_hook=collections.OrderedDict, strict=False)
8182
value_to_decode = field.value
8283
if not isinstance(value_to_decode, str):
8384
value_to_decode = value_to_decode.decode()
8485

85-
self.user_profile[config.ATTRIBUTE_STRUCTURED_POSTAL_ADDRESS] = decoder.decode(value_to_decode)
86+
return decoder.decode(value_to_decode)
8687

8788
def ensure_postal_address(self):
8889
# setting in 'user_profile' - will be removed once user_profile is removed

yoti_python_sdk/attribute_parser.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import json
66
import logging
77

8+
from yoti_python_sdk import multivalue
89
from yoti_python_sdk.protobuf.protobuf import Protobuf
910

1011

@@ -24,6 +25,8 @@ def value_based_on_content_type(value, content_type=None):
2425
string_value = value.decode('utf-8')
2526
int_value = int(string_value)
2627
return int_value
28+
elif content_type == Protobuf.CT_MULTI_VALUE:
29+
return multivalue.parse(value)
2730

2831
if logging.getLogger().propagate:
2932
logging.warning("Unknown type '{0}', attempting to parse it as a String".format(content_type))

yoti_python_sdk/config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
ATTRIBUTE_POSTAL_ADDRESS = "postal_address"
1515
ATTRIBUTE_SELFIE = "selfie"
1616
ATTRIBUTE_STRUCTURED_POSTAL_ADDRESS = "structured_postal_address"
17+
ATTRIBUTE_DOCUMENT_IMAGES = "document_images"
1718
ANCHOR_SOURCE = "SOURCE"
1819
ANCHOR_VERIFIER = "VERIFIER"
1920
KEY_AGE_VERIFIED = "is_age_verified"

yoti_python_sdk/image.py

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,21 @@
33

44
from yoti_python_sdk.protobuf.protobuf import Protobuf
55

6+
CONTENT_TYPE_JPEG = "jpeg"
7+
CONTENT_TYPE_PNG = "png"
8+
69

710
class Image:
811
def __init__(self, image_bytes, image_content_type):
9-
if image_content_type in Image.allowed_types():
10-
self.__data = image_bytes
11-
self.__content_type = image_content_type
12+
if image_content_type == Protobuf.CT_JPEG:
13+
self.__content_type = CONTENT_TYPE_JPEG
14+
elif image_content_type == Protobuf.CT_PNG:
15+
self.__content_type = CONTENT_TYPE_PNG
1216
else:
1317
raise TypeError("Content type '{0}' is not a supported image type".format(image_content_type))
1418

19+
self.__data = image_bytes
20+
1521
@staticmethod
1622
def allowed_types():
1723
return [Protobuf.CT_PNG, Protobuf.CT_JPEG]
@@ -25,19 +31,8 @@ def content_type(self):
2531
return self.__content_type
2632

2733
def mime_type(self):
28-
if self.__content_type == Protobuf.CT_JPEG:
29-
return "image/jpeg"
30-
elif self.__content_type == Protobuf.CT_PNG:
31-
return "image/png"
32-
else:
33-
return ""
34+
return "image/{0}".format(self.__content_type)
3435

3536
def base64_content(self):
36-
if self.__content_type == Protobuf.CT_JPEG:
37-
data = base64.b64encode(self.__data).decode('utf-8')
38-
return 'data:image/jpeg;base64,{0}'.format(data)
39-
elif self.__content_type == Protobuf.CT_PNG:
40-
data = base64.b64encode(self.__data).decode('utf-8')
41-
return 'data:image/png;base64,{0}'.format(data)
42-
43-
raise TypeError("Content type '{0}' is not a supported image type".format(self.__content_type))
37+
data = base64.b64encode(self.__data).decode('utf-8')
38+
return 'data:{0};base64,{1}'.format(self.mime_type(), data)

yoti_python_sdk/multivalue.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# -*- coding: utf-8 -*-
2+
from yoti_python_sdk import attribute_parser
3+
from yoti_python_sdk.protobuf import protobuf
4+
5+
6+
def parse(multi_value_bytes):
7+
proto = protobuf.Protobuf()
8+
multi_value_list = []
9+
parsed_multi_value = proto.multi_value(multi_value_bytes)
10+
11+
for multi_value_item in parsed_multi_value.values:
12+
value = attribute_parser.value_based_on_content_type(multi_value_item.data, multi_value_item.content_type)
13+
14+
if value.content_type == proto.CT_MULTI_VALUE:
15+
multi_value_list.append(parse(value))
16+
else:
17+
multi_value_list.append(value)
18+
19+
return multi_value_list
20+
21+
22+
def filter_values(values, type_to_filter):
23+
return list(filter(lambda v: isinstance(v, type_to_filter), values))

yoti_python_sdk/profile.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# -*- coding: utf-8 -*-
22
import logging
33

4-
from yoti_python_sdk import attribute_parser, config
4+
from yoti_python_sdk import attribute_parser, config, multivalue
55
from yoti_python_sdk.anchor import Anchor
66
from yoti_python_sdk.attribute import Attribute
77
from yoti_python_sdk.image import Image
@@ -24,6 +24,9 @@ def __init__(self, profile_attributes):
2424
if field.name == config.ATTRIBUTE_SELFIE:
2525
value = field.value
2626

27+
if field.name == config.ATTRIBUTE_DOCUMENT_IMAGES:
28+
value = multivalue.filter_values(value, Image)
29+
2730
anchors = Anchor().parse_anchors(field.anchors)
2831

2932
self.attributes[field.name] = Attribute(field.name, value, anchors)
@@ -82,6 +85,10 @@ def selfie(self):
8285
def structured_postal_address(self):
8386
return self.get_attribute(config.ATTRIBUTE_STRUCTURED_POSTAL_ADDRESS)
8487

88+
@property
89+
def document_images(self):
90+
return self.get_attribute(config.ATTRIBUTE_DOCUMENT_IMAGES)
91+
8592
def get_attribute(self, attribute_name):
8693
if attribute_name in self.attributes:
8794
return self.attributes.get(attribute_name)

yoti_python_sdk/protobuf/protobuf.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ class Protobuf(object):
1313
CT_DATE = 3 # string in RFC3339 format (YYYY-MM-DD)
1414
CT_PNG = 4 # standard .png image
1515
CT_JSON = 5 # value encoded using JSON
16-
CT_INT = 6 # int value
17-
CT_MULTI_VALUE = 7 # allows a list of values
16+
CT_MULTI_VALUE = 6 # allows a list of values
17+
CT_INT = 7 # int value
1818

1919
@staticmethod
2020
def current_user(receipt):
@@ -39,3 +39,15 @@ def anchor(data):
3939
anchor = Attribute_pb2.Anchor()
4040
anchor.MergeFromString(data)
4141
return anchor
42+
43+
@staticmethod
44+
def attribute(data):
45+
attribute = Attribute_pb2.Attribute()
46+
attribute.MergeFromString(data)
47+
return attribute
48+
49+
@staticmethod
50+
def multi_value(data):
51+
multi_value = Attribute_pb2.MultiValue()
52+
multi_value.MergeFromString(data)
53+
return multi_value
Lines changed: 33 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,33 @@
1-
import binascii
2-
import io
3-
from os.path import abspath, dirname, join
4-
5-
from yoti_python_sdk import anchor
6-
from yoti_python_sdk.protobuf import protobuf
7-
8-
FIXTURES_DIR = join(dirname(abspath(__file__)), 'fixtures')
9-
ANCHOR_DRIVING_LICENSE = join(FIXTURES_DIR, 'anchor_driving_license.txt')
10-
ANCHOR_PASSPORT = join(FIXTURES_DIR, 'anchor_passport.txt')
11-
ANCHOR_YOTI_ADMIN = join(FIXTURES_DIR, 'anchor_yoti_admin.txt')
12-
13-
14-
def get_anchor_from_base64_text(file_path):
15-
base64_driving_license_anchor = read_file(file_path)
16-
driving_license_anchor_bytes = binascii.a2b_base64(base64_driving_license_anchor)
17-
18-
protobuf_anchor = protobuf.Protobuf().anchor(driving_license_anchor_bytes)
19-
anchors = list()
20-
anchors.append(protobuf_anchor)
21-
22-
return anchors
23-
24-
25-
def get_parsed_driving_license_anchor():
26-
return anchor.Anchor().parse_anchors(get_anchor_from_base64_text(ANCHOR_DRIVING_LICENSE))[0]
27-
28-
29-
def get_parsed_passport_anchor():
30-
return anchor.Anchor().parse_anchors(get_anchor_from_base64_text(ANCHOR_PASSPORT))[0]
31-
32-
33-
def get_parsed_yoti_admin_anchor():
34-
return anchor.Anchor().parse_anchors(get_anchor_from_base64_text(ANCHOR_YOTI_ADMIN))[0]
35-
36-
37-
def read_file(file_path):
38-
with io.open(file_path, mode='r', encoding='utf-8') as file:
39-
return file.read()
1+
# -*- coding: utf-8 -*-
2+
from os.path import abspath, dirname, join
3+
4+
from yoti_python_sdk import anchor
5+
from yoti_python_sdk.protobuf import protobuf
6+
from yoti_python_sdk.tests import file_helper
7+
8+
FIXTURES_DIR = join(dirname(abspath(__file__)), 'fixtures')
9+
ANCHOR_DRIVING_LICENSE = join(FIXTURES_DIR, 'anchor_driving_license.txt')
10+
ANCHOR_PASSPORT = join(FIXTURES_DIR, 'anchor_passport.txt')
11+
ANCHOR_YOTI_ADMIN = join(FIXTURES_DIR, 'anchor_yoti_admin.txt')
12+
13+
14+
def get_anchor_from_base64_text(file_path):
15+
anchor_bytes = file_helper.get_file_bytes(file_path)
16+
17+
protobuf_anchor = protobuf.Protobuf().anchor(anchor_bytes)
18+
anchors = list()
19+
anchors.append(protobuf_anchor)
20+
21+
return anchors
22+
23+
24+
def get_parsed_driving_license_anchor():
25+
return anchor.Anchor().parse_anchors(get_anchor_from_base64_text(ANCHOR_DRIVING_LICENSE))[0]
26+
27+
28+
def get_parsed_passport_anchor():
29+
return anchor.Anchor().parse_anchors(get_anchor_from_base64_text(ANCHOR_PASSPORT))[0]
30+
31+
32+
def get_parsed_yoti_admin_anchor():
33+
return anchor.Anchor().parse_anchors(get_anchor_from_base64_text(ANCHOR_YOTI_ADMIN))[0]
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# -*- coding: utf-8 -*-
2+
from os.path import abspath, dirname, join
3+
4+
from yoti_python_sdk import attribute_parser
5+
from yoti_python_sdk.protobuf import protobuf
6+
from yoti_python_sdk.tests import file_helper
7+
8+
FIXTURES_DIR = join(dirname(abspath(__file__)), 'fixtures')
9+
ATTRIBUTE_DOCUMENT_IMAGES = join(FIXTURES_DIR, 'attribute_document_images.txt')
10+
11+
12+
def get_attribute_from_base64_text(file_path):
13+
attribute_bytes = file_helper.get_file_bytes(file_path)
14+
15+
return protobuf.Protobuf().attribute(attribute_bytes)
16+
17+
18+
def parse_multi_value():
19+
multi_value_proto_attribute = get_attribute_from_base64_text(
20+
ATTRIBUTE_DOCUMENT_IMAGES)
21+
22+
return attribute_parser.value_based_on_content_type(
23+
multi_value_proto_attribute.value,
24+
multi_value_proto_attribute.content_type)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import binascii
2+
import io
3+
from os.path import abspath, dirname, join
4+
5+
FIXTURES_DIR = join(dirname(abspath(__file__)), 'fixtures')
6+
7+
8+
def get_file_bytes(file_path):
9+
base64_data = read_file(file_path)
10+
file_bytes = binascii.a2b_base64(base64_data)
11+
12+
return file_bytes
13+
14+
15+
def read_file(file_path):
16+
with io.open(file_path, mode='r', encoding='utf-8') as file:
17+
return file.read()

0 commit comments

Comments
 (0)