This repository has been archived by the owner on Jun 13, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 641
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #17 from khyew/voice-verification-api
Added Python libs for majority of voice Verification API calls
- Loading branch information
Showing
9 changed files
with
411 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import VerificationServiceHttpClientHelper | ||
import sys | ||
|
||
def create_profile(subscription_key, locale): | ||
"""Creates a profile on the server. | ||
Arguments: | ||
subscription_key -- the subscription key string | ||
locale -- the locale string | ||
""" | ||
helper = VerificationServiceHttpClientHelper.VerificationServiceHttpClientHelper( | ||
subscription_key) | ||
|
||
creation_response = helper.create_profile(locale) | ||
|
||
print('Profile ID = {0}'.format(creation_response.get_profile_id())) | ||
|
||
if __name__ == "__main__": | ||
if len(sys.argv) < 2: | ||
print('Usage: python CreateProfile.py <subscription_key>') | ||
print('\t<subscription_key> is the subscription key for the service') | ||
sys.exit('Error: Incorrect Usage.') | ||
|
||
create_profile(sys.argv[1], 'en-us') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import VerificationServiceHttpClientHelper | ||
import sys | ||
|
||
def enroll_profile(subscription_key, profile_id, file_path): | ||
"""Enrolls a profile on the server. | ||
Arguments: | ||
subscription_key -- the subscription key string | ||
profile_id -- the profile ID of the profile to enroll | ||
file_path -- the path of the file to use for enrollment | ||
""" | ||
helper = VerificationServiceHttpClientHelper.VerificationServiceHttpClientHelper( | ||
subscription_key) | ||
|
||
enrollment_response = helper.enroll_profile(profile_id, file_path) | ||
|
||
print('Enrollments Completed = {0}'.format(enrollment_response.get_enrollments_count())) | ||
print('Remaining Enrollments = {0}'.format(enrollment_response.get_remaining_enrollments())) | ||
print('Enrollment Status = {0}'.format(enrollment_response.get_enrollment_status())) | ||
print('Enrollment Phrase = {0}'.format(enrollment_response.get_enrollment_phrase())) | ||
|
||
if __name__ == "__main__": | ||
if len(sys.argv) < 4: | ||
print('Usage: python EnrollProfile.py <subscription_key> <profile_id> ' | ||
'<enrollment_file_path>') | ||
print('\t<subscription_key> is the subscription key for the service') | ||
print('\t<profile_id> is the profile ID of the profile to enroll') | ||
print('\t<enrollment_file_path> is the enrollment audio file path') | ||
|
||
sys.exit('Error: Incorrect Usage.') | ||
|
||
enroll_profile(sys.argv[1], sys.argv[2], sys.argv[3]) |
34 changes: 34 additions & 0 deletions
34
SpeakerRecognition/Python/Verification/EnrollmentResponse.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
class EnrollmentResponse: | ||
"""This class encapsulates the enrollment response.""" | ||
|
||
_ENROLLMENT_STATUS = 'enrollmentStatus' | ||
_ENROLLMENTS_COUNT = 'enrollmentsCount' | ||
_REMAINING_ENROLLMENTS = 'remainingEnrollments' | ||
_ENROLLMENT_PHRASE = 'phrase' | ||
|
||
def __init__(self, response): | ||
"""Constructor of the EnrollmentResponse class. | ||
Arguments: | ||
response -- the dictionary of the deserialized python response | ||
""" | ||
self._enrollment_status = response.get(self._ENROLLMENT_STATUS, None) | ||
self._enrollments_count = response.get(self._ENROLLMENTS_COUNT, None) | ||
self._remaining_enrollments = response.get(self._REMAINING_ENROLLMENTS, None) | ||
self._enrollment_phrase = response.get(self._ENROLLMENT_PHRASE, None) | ||
|
||
def get_enrollment_status(self): | ||
"""Returns the enrollment status""" | ||
return self._enrollment_status | ||
|
||
def get_enrollments_count(self): | ||
"""Returns the number of enrollments already performed""" | ||
return self._enrollments_count | ||
|
||
def get_enrollment_phrase(self): | ||
"""Returns the enrollment phrase extracted from this request""" | ||
return self._enrollment_phrase | ||
|
||
def get_remaining_enrollments(self): | ||
"""Returns the number of remaining enrollments before the profile is ready for verification""" | ||
return self._remaining_enrollments |
33 changes: 33 additions & 0 deletions
33
SpeakerRecognition/Python/Verification/PrintAllProfiles.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import VerificationServiceHttpClientHelper | ||
import sys | ||
|
||
def print_all_profiles(subscription_key): | ||
"""Print all the profiles for the given subscription key. | ||
Arguments: | ||
subscription_key -- the subscription key string | ||
""" | ||
helper = VerificationServiceHttpClientHelper.VerificationServiceHttpClientHelper( | ||
subscription_key) | ||
|
||
profiles = helper.get_all_profiles() | ||
|
||
print('Profile ID, Locale, Enrollments Count, Remaining Enrollments Count,' | ||
' Created Date Time, Last Action Date Time, Enrollment Status') | ||
for profile in profiles: | ||
print('{0}, {1}, {2}, {3}, {4}, {5}, {6}'.format( | ||
profile.get_profile_id(), | ||
profile.get_locale(), | ||
profile.get_enrollments_count(), | ||
profile.get_remaining_enrollments_count(), | ||
profile.get_created_date_time(), | ||
profile.get_last_action_date_time(), | ||
profile.get_enrollment_status())) | ||
|
||
if __name__ == "__main__": | ||
if len(sys.argv) < 2: | ||
print('Usage: python PrintAllProfiles.py <subscription_key>') | ||
print('\t<subscription_key> is the subscription key for the service') | ||
sys.exit('Error: Incorrect Usage.') | ||
|
||
print_all_profiles(sys.argv[1]) |
16 changes: 16 additions & 0 deletions
16
SpeakerRecognition/Python/Verification/ProfileCreationResponse.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
class ProfileCreationResponse: | ||
"""This class encapsulates the response of the creation of a user profile.""" | ||
|
||
_PROFILE_ID = 'verificationProfileId' | ||
|
||
def __init__(self, response): | ||
"""Constructor of the ProfileCreationResponse class. | ||
Arguments: | ||
response -- the dictionary of the deserialized python response | ||
""" | ||
self._profile_id = response.get(self._PROFILE_ID, None) | ||
|
||
def get_profile_id(self): | ||
"""Returns the profile ID of the user""" | ||
return self._profile_id |
52 changes: 52 additions & 0 deletions
52
SpeakerRecognition/Python/Verification/VerificationProfile.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
class VerificationProfile: | ||
"""This class encapsulates a user profile.""" | ||
|
||
_PROFILE_ID = 'verificationProfileId' | ||
_LOCALE = 'locale' | ||
_ENROLLMENTS_COUNT = 'enrollmentsCount' | ||
_REMAINING_ENROLLMENTS_COUNT = 'remainingEnrollmentsCount' | ||
_CREATED_DATE_TIME = 'createdDateTime' | ||
_LAST_ACTION_DATE_TIME = 'lastActionDateTime' | ||
_ENROLLMENT_STATUS = 'enrollmentStatus' | ||
|
||
def __init__(self, response): | ||
"""Constructor of the VerificationProfile class. | ||
Arguments: | ||
response -- the dictionary of the deserialized python response | ||
""" | ||
self._profile_id = response.get(self._PROFILE_ID, None) | ||
self._locale = response.get(self._LOCALE, None) | ||
self._enrollments_count = response.get(self._ENROLLMENTS_COUNT, None) | ||
self._remaining_enrollments_count = response.get(self._REMAINING_ENROLLMENTS_COUNT, None) | ||
self._created_date_time = response.get(self._CREATED_DATE_TIME, None) | ||
self._last_action_date_time = response.get(self._LAST_ACTION_DATE_TIME, None) | ||
self._enrollment_status = response.get(self._ENROLLMENT_STATUS, None) | ||
|
||
def get_profile_id(self): | ||
"""Returns the profile ID of the user""" | ||
return self._profile_id | ||
|
||
def get_locale(self): | ||
"""Returns the locale of the user""" | ||
return self._locale | ||
|
||
def get_enrollments_count(self): | ||
"""Returns the total number of speech samples submitted for enrollment for this user""" | ||
return self._enrollments_count | ||
|
||
def get_remaining_enrollments_count(self): | ||
"""Returns the number of speech samples required remaining to complete enrollment""" | ||
return self._remaining_enrollments_count | ||
|
||
def get_created_date_time(self): | ||
"""Returns the creation date time of the user""" | ||
return self._created_date_time | ||
|
||
def get_last_action_date_time(self): | ||
"""Returns the last action date time of the user""" | ||
return self._last_action_date_time | ||
|
||
def get_enrollment_status(self): | ||
"""Returns the enrollment status of the user""" | ||
return self._enrollment_status |
22 changes: 22 additions & 0 deletions
22
SpeakerRecognition/Python/Verification/VerificationResponse.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
class VerificationResponse: | ||
"""This class encapsulates the verification response.""" | ||
|
||
_RESULT = 'result' | ||
_CONFIDENCE = 'confidence' | ||
|
||
def __init__(self, response): | ||
"""Constructor of the VerificationResponse class. | ||
Arguments: | ||
response -- the dictionary of the deserialized python response | ||
""" | ||
self._result = response.get(self._RESULT, None) | ||
self._confidence = response.get(self._CONFIDENCE, None) | ||
|
||
def get_result(self): | ||
"""Returns whether the voice clip belongs to the profile (Accept / Reject)""" | ||
return self._result | ||
|
||
def get_confidence(self): | ||
"""Returns the verification confidence""" | ||
return self._confidence |
171 changes: 171 additions & 0 deletions
171
SpeakerRecognition/Python/Verification/VerificationServiceHttpClientHelper.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
import http.client | ||
import urllib.parse | ||
import json | ||
import time | ||
from contextlib import closing | ||
import ProfileCreationResponse | ||
import EnrollmentResponse | ||
import VerificationResponse | ||
import VerificationProfile | ||
import logging | ||
|
||
class VerificationServiceHttpClientHelper: | ||
"""Abstracts the interaction with the Verification service.""" | ||
|
||
_STATUS_OK = 200 | ||
_BASE_URI = 'api.projectoxford.ai' | ||
_VERIFICATION_PROFILES_URI = '/spid/v1.0/verificationProfiles' | ||
_VERIFICATION_URI = '/spid/v1.0/verify' | ||
_SUBSCRIPTION_KEY_HEADER = 'Ocp-Apim-Subscription-Key' | ||
_CONTENT_TYPE_HEADER = 'Content-Type' | ||
_JSON_CONTENT_HEADER_VALUE = 'application/json' | ||
_STREAM_CONTENT_HEADER_VALUE = 'application/octet-stream' | ||
|
||
def __init__(self, subscription_key): | ||
"""Constructor of the VerificationServiceHttpClientHelper class. | ||
Arguments: | ||
subscription_key -- the subscription key string | ||
""" | ||
self._subscription_key = subscription_key | ||
|
||
def get_all_profiles(self): | ||
"""Return a list of all profiles on the server.""" | ||
try: | ||
# Send the request | ||
res, message = self._send_request( | ||
'GET', | ||
self._BASE_URI, | ||
self._VERIFICATION_PROFILES_URI, | ||
self._JSON_CONTENT_HEADER_VALUE) | ||
|
||
if res.status == self._STATUS_OK: | ||
# Parse the response body | ||
profiles_raw = json.loads(message) | ||
return [VerificationProfile.VerificationProfile(profiles_raw[i]) | ||
for i in range(0, len(profiles_raw))] | ||
else: | ||
raise Exception('Error getting all profiles: ' + res.reason) | ||
except: | ||
logging.error('Error getting all profiles.') | ||
raise | ||
|
||
def create_profile(self, locale): | ||
"""Creates a profile on the server and returns a dictionary of the creation response. | ||
Arguments: | ||
locale -- the locale string for the profile | ||
""" | ||
try: | ||
# Prepare the body of the message | ||
body = json.dumps({'locale': '{0}'.format(locale)}) | ||
|
||
# Send the request | ||
res, message = self._send_request( | ||
'POST', | ||
self._BASE_URI, | ||
self._VERIFICATION_PROFILES_URI, | ||
self._JSON_CONTENT_HEADER_VALUE, | ||
body) | ||
|
||
if res.status == self._STATUS_OK: | ||
# Parse the response body | ||
return ProfileCreationResponse.ProfileCreationResponse(json.loads(message)) | ||
else: | ||
message = res.read().decode('utf-8') | ||
raise Exception('Error creating profile: ' + res.reason) | ||
except: | ||
logging.error('Error creating profile.') | ||
raise | ||
|
||
def enroll_profile(self, profile_id, file_path): | ||
"""Enrolls a profile using an audio file and returns a | ||
dictionary of the enrollment response. | ||
Arguments: | ||
profile_id -- the profile ID string of the user to enroll | ||
file_path -- the file path string of the audio file to use | ||
""" | ||
try: | ||
# Prepare the request | ||
request_url = '{0}/{1}/enroll'.format( | ||
self._VERIFICATION_PROFILES_URI, | ||
urllib.parse.quote(profile_id)) | ||
|
||
|
||
# Prepare the body of the message | ||
with open(file_path, 'rb') as body: | ||
# Send the request | ||
res, message = self._send_request( | ||
'POST', | ||
self._BASE_URI, | ||
request_url, | ||
self._STREAM_CONTENT_HEADER_VALUE, | ||
body) | ||
if res.status == self._STATUS_OK: | ||
# Parse the response body | ||
return EnrollmentResponse.EnrollmentResponse(json.loads(message)) | ||
else: | ||
raise Exception('Error enrolling profile: ' + res.reason) | ||
except: | ||
logging.error('Error enrolling profile.') | ||
raise | ||
|
||
def verify_file(self, file_path, profile_id): | ||
"""Verifies a profile using an audio file and returns a | ||
Arguments: | ||
file_path -- the file path of the audio file to test | ||
profile_id -- a profile to test against | ||
""" | ||
try: | ||
# Prepare the request | ||
request_url = '{0}?verificationProfileId={1}'.format( | ||
self._VERIFICATION_URI, | ||
urllib.parse.quote(profile_id)) | ||
|
||
# Prepare the body of the message | ||
with open(file_path, 'rb') as body: | ||
# Send the request | ||
res, message = self._send_request( | ||
'POST', | ||
self._BASE_URI, | ||
request_url, | ||
self._STREAM_CONTENT_HEADER_VALUE, | ||
body) | ||
|
||
if res.status == self._STATUS_OK: | ||
# Parse the response body | ||
return VerificationResponse.VerificationResponse(json.loads(message)) | ||
else: | ||
raise Exception('Error verifying audio from file: ' + res.reason) | ||
except: | ||
logging.error('Error performing verification.') | ||
raise | ||
|
||
def _send_request(self, method, base_url, request_url, content_type_value, body=None): | ||
"""Sends the request to the server then returns the response and the response body string. | ||
Arguments: | ||
method -- specifies whether the request is a GET or POST request | ||
base_url -- the base url for the connection | ||
request_url -- the request url for the connection | ||
content_type_value -- the value of the content type field in the headers | ||
body -- the body of the request (needed only in POST methods) | ||
""" | ||
try: | ||
# Set the headers | ||
headers = {self._CONTENT_TYPE_HEADER: content_type_value, | ||
self._SUBSCRIPTION_KEY_HEADER: self._subscription_key} | ||
|
||
# Start the connection | ||
with closing(http.client.HTTPSConnection(base_url)) as conn: | ||
# Send the request | ||
conn.request(method, request_url, body, headers) | ||
res = conn.getresponse() | ||
message = res.read().decode('utf-8') | ||
|
||
return res, message | ||
except: | ||
logging.error('Error sending the request.') | ||
raise |
Oops, something went wrong.