From 0a6c5503ba9d59336857cb328e0d11d4d775cbf0 Mon Sep 17 00:00:00 2001 From: Waidhoferj Date: Thu, 14 May 2020 21:02:55 -0700 Subject: [PATCH 1/5] AudioMetadata lives again!!!! --- Entity/AudioSampleMetaData.py | 10 +++++++--- database_wrapper.py | 8 ++++++-- flask_api.py | 32 ++++++++++++++++++++++---------- modules/validators.py | 5 +++++ 4 files changed, 40 insertions(+), 15 deletions(-) diff --git a/Entity/AudioSampleMetaData.py b/Entity/AudioSampleMetaData.py index 780afa8..2cf281e 100644 --- a/Entity/AudioSampleMetaData.py +++ b/Entity/AudioSampleMetaData.py @@ -26,14 +26,15 @@ class AudioSampleMetaData(Base): tone = Column(String(255)) timestamp = Column(Integer) username = Column(String(255)) - # Text chosen because filename is standarized concatenation of above fields - filename = Column(Text) + emphasis = Column(String(255)) + script = Column(String(255)) + audio_file_id = Column(String(1024)) is_view = False def __repr__(self): string = " dict: "tone": "serious-but-not-really", "timestamp": 1577077883, "username": "guest", - "filename": "ww_q_serious-but-not-really_here_m_doe_jj_1577077883_guest.wav" # noqa because too hard. + "emphasis": "us", + "script": "Nimbus" + "audio_file_id": Id from Google Drive # noqa because too hard. } Raises: diff --git a/flask_api.py b/flask_api.py index e1bb967..c6e4c61 100755 --- a/flask_api.py +++ b/flask_api.py @@ -122,6 +122,8 @@ def handle_question(): def save_a_recording(): """Given the audio metadata & audio file, resamples it, saves to storage. """ + if("wav_file" not in request.files): + return "Please provide an audio file under the key 'wav_file' in your FormData", BAD_REQUEST validator = WakeWordValidator() formatter = WakeWordFormatter() data = request.form @@ -133,19 +135,17 @@ def save_a_recording(): return str(err), BAD_REQUEST formatted_data = formatter.format(data) filename = create_filename(formatted_data) + try: + file_id = save_audiofile(filename, request.files["wav_file"]) + except Exception as err: + return f"Failed to save audio file because... {err}", BAD_REQUEST - # Save the audiofile first because if error then we stop here - # We do not want to save any metadata to the NimbusDatabase - # if the audio fails to save. - save_audiofile(filename, request.files["wav_file"]) - - # Let's also save the filename to the database for quick reference - formatted_data["filename"] = filename + formatted_data["audio_file_id"] = file_id initializeDB() try: - db.save_audio_sample_meta_data(formatted_data) + db.insert_entity(AudioSampleMetaData, formatted_data) except BadDictionaryKeyError as e: return str(e), BAD_REQUEST except BadDictionaryValueError as e: @@ -158,7 +158,7 @@ def save_a_recording(): # HINT: security always wins raise e - return filename + return f"Successfully stored audiofile as '{filename}''", SUCCESS @app.route("/new_data/office_hours", methods=["POST"]) @@ -479,7 +479,18 @@ def resample_audio(): def save_audiofile(filename, content): - """Actually save the file into Google Drive.""" + """ + Saves audio to the club Google Drive folder. + + Parameters + ---------- + - `filename:str` the name of the file, formatted by `create_filename()` + - `content: file` audio file to store + + Returns + ------- + The Google Drive file id that can be used to retrieve the file + """ # Initialize our google drive authentication object using saved credentials, # or through the command line gauth = GoogleAuth() @@ -499,6 +510,7 @@ def save_audiofile(filename, content): # Set the content of the file to the POST request's wav_file parameter. file.content = content file.Upload() # Upload file. + return file["id"] def get_folder_id(): diff --git a/modules/validators.py b/modules/validators.py index 896a5d0..6462725 100644 --- a/modules/validators.py +++ b/modules/validators.py @@ -84,6 +84,8 @@ def __init__(self, validators=None): "firstName": lambda firstName: type(firstName) == str, "timestamp": lambda timestamp: str.isdigit(timestamp), "username": lambda username: type(username) == str, + "emphasis": lambda emphasis: type(emphasis) == str, + "script": lambda script: type(script) == str, } def validate(self, data): @@ -118,6 +120,9 @@ def fix(self, data, issues): elif key == "timestamp": form[key] = int(time.time()) print("fixed timestamp", form[key]) + elif key == "script" and form["isWakeWord"] == "ww": + form[key] = "nimbus" + print("Added 'script' value of 'nimbus'") else: raise WakeWordValidatorError( f"Required audio metadata '{key}' was not provided" From 3f17ea72a667510f0a10e58a17ef5f5583e36551 Mon Sep 17 00:00:00 2001 From: Waidhoferj Date: Sat, 16 May 2020 09:49:50 -0700 Subject: [PATCH 2/5] added fields to tests --- tests/test_database_wrapper.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_database_wrapper.py b/tests/test_database_wrapper.py index 4d8e6f2..fbf7e69 100644 --- a/tests/test_database_wrapper.py +++ b/tests/test_database_wrapper.py @@ -43,7 +43,9 @@ "tone": "serious-but-not-really", "timestamp": 1577077883, "username": "guest", - "filename": "ww_q_serious-fake_m_doe_jj_1577077883_guest.wav", + "audio_file_id": "OZ234FSDWER5GDF234F4G5", + "script": "Nimbus", + "emphasis": "us" } TEST_CONFIG_FILENAME = "testConfig.json" From 30ae7499266622bbf6524a63d4978029ba281917 Mon Sep 17 00:00:00 2001 From: Waidhoferj Date: Sat, 16 May 2020 10:30:34 -0700 Subject: [PATCH 3/5] fixed misplaced quote --- flask_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask_api.py b/flask_api.py index c6e4c61..48914c8 100755 --- a/flask_api.py +++ b/flask_api.py @@ -158,7 +158,7 @@ def save_a_recording(): # HINT: security always wins raise e - return f"Successfully stored audiofile as '{filename}''", SUCCESS + return f"Successfully stored audiofile as '{filename}'", SUCCESS @app.route("/new_data/office_hours", methods=["POST"]) From 6a1a5200e893ed0d4775e43a169de4b799026a9e Mon Sep 17 00:00:00 2001 From: Waidhoferj Date: Sat, 16 May 2020 10:31:08 -0700 Subject: [PATCH 4/5] updated first two tests to reflect the new wake word upload --- tests/test_flask_api.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_flask_api.py b/tests/test_flask_api.py index 10ed271..817b34f 100644 --- a/tests/test_flask_api.py +++ b/tests/test_flask_api.py @@ -85,8 +85,8 @@ def test_new_data_wakeword(mock_db, mock_formatter, mock_validator, mock_create_ data={"test": "foo", 'wav_file': (BytesIO(b'dummyText'), 'dummyfile.txt')}) # Verify that db client was told to save data, and that the newly generated filename was returned - mock_db.save_audio_sample_meta_data.assert_called_once() - assert resp.data == b"test_filename" + mock_db.insert_entity.assert_called_once() + assert resp.data == b"Successfully stored audiofile as 'test_filename'" @patch("flask_api.WakeWordValidator") @@ -96,7 +96,7 @@ def test_new_data_wakeword_validator_issues(mock_validator, client): mock_validator.return_value = mock_validator_instance # Verify that the client will catch and throw an error if the validator fails - resp = client.post('/new_data/wakeword', data={"dummy1": "dummy2"}) + resp = client.post('/new_data/wakeword', data={"dummy1": "dummy2", 'wav_file': (BytesIO(b'dummyText'), 'dummyfile.txt')}) assert resp.status_code == BAD_REQUEST assert resp.data == TEST_ERROR.encode() From 6531966751c34ec157c0c5c78235e9c7e352c1d5 Mon Sep 17 00:00:00 2001 From: Waidhoferj Date: Sun, 17 May 2020 14:19:38 -0700 Subject: [PATCH 5/5] Tested validation in the WakeWordValidator --- modules/validators.py | 4 ++-- tests/test_flask_api.py | 34 ---------------------------------- tests/test_validators.py | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 36 deletions(-) create mode 100644 tests/test_validators.py diff --git a/modules/validators.py b/modules/validators.py index 6462725..99b9a0c 100644 --- a/modules/validators.py +++ b/modules/validators.py @@ -100,8 +100,8 @@ def validate(self, data): value = data[key] if not validator(value): issues[key] = WakeWordValidatorIssue.INVALID - except BadRequestKeyError as e: - print("caught BadRequestKeyError: ", e.args) + except (KeyError, BadRequestKeyError) as e: + print("Couldn't find", e.args, "when validating data") issues[key] = WakeWordValidatorIssue.DOES_NOT_EXIST return issues diff --git a/tests/test_flask_api.py b/tests/test_flask_api.py index 817b34f..fc96e4a 100644 --- a/tests/test_flask_api.py +++ b/tests/test_flask_api.py @@ -99,37 +99,3 @@ def test_new_data_wakeword_validator_issues(mock_validator, client): resp = client.post('/new_data/wakeword', data={"dummy1": "dummy2", 'wav_file': (BytesIO(b'dummyText'), 'dummyfile.txt')}) assert resp.status_code == BAD_REQUEST assert resp.data == TEST_ERROR.encode() - - -@patch("flask_api.save_audiofile") -@patch("flask_api.create_filename", return_value="test_filename") -@patch("flask_api.WakeWordValidator") -@patch("flask_api.WakeWordFormatter") -@patch("flask_api.db") -def test_new_data_wakeword_db_error(mock_db, mock_formatter, mock_validator, mock_create_filename, - mock_save_audiofile, client): - mock_formatter_instance = Mock() - mock_formatter_instance.format.return_value = {"filename": "dummy"} - mock_formatter.return_value = mock_formatter_instance - - # Verify that the client will catch and throw an error for specific exceptions - mock_db.save_audio_sample_meta_data.side_effect = BadDictionaryKeyError(TEST_ERROR) - resp = client.post( - '/new_data/wakeword', - data={"test": "foo", 'wav_file': (BytesIO(b'dummyText'), 'dummyfile.txt')}) - assert resp.status_code == BAD_REQUEST - assert resp.data == TEST_ERROR.encode() - - mock_db.save_audio_sample_meta_data.side_effect = BadDictionaryValueError(TEST_ERROR) - resp = client.post( - '/new_data/wakeword', - data={"test": "foo", 'wav_file': (BytesIO(b'dummyText'), 'dummyfile.txt')}) - assert resp.status_code == BAD_REQUEST - assert resp.data == TEST_ERROR.encode() - - mock_db.save_audio_sample_meta_data.side_effect = NimbusDatabaseError(TEST_ERROR) - resp = client.post( - '/new_data/wakeword', - data={"test": "foo", 'wav_file': (BytesIO(b'dummyText'), 'dummyfile.txt')}) - assert resp.status_code == BAD_REQUEST - assert resp.data == TEST_ERROR.encode() diff --git a/tests/test_validators.py b/tests/test_validators.py new file mode 100644 index 0000000..6802184 --- /dev/null +++ b/tests/test_validators.py @@ -0,0 +1,40 @@ +import json +import pytest + +from modules.validators import WakeWordValidator, WakeWordValidatorError +wake_word_data = { +"isWakeWord":"true", +"noiseLevel":"l", +"tone":"serious", +"location":"Cal Poly San Luis Obispo", +"gender":"m", +"lastName":"Waidhofer", +"firstName":"John", +"timestamp": "1589744893", +"username":"waidhofer", +"emphasis":"Emphasized", +"script":"testing 123", +"test":"foo" +} + +important_fields = ["isWakeWord", +"noiseLevel", +"tone", +"location", +"gender", +"lastName", +"firstName", +"emphasis", +"script"] +def test_wake_word_missing_values(): + validator = WakeWordValidator() + for field in important_fields: + data = wake_word_data.copy() + data.pop(field) + issues = validator.validate(data) + print(issues) + + assert len(issues) == 1 + + with pytest.raises(WakeWordValidatorError): + data = validator.fix(data,issues)