-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fall 2022 Work #53
base: dev
Are you sure you want to change the base?
Fall 2022 Work #53
Changes from 97 commits
18df8ef
3a7b54c
cc017f0
91eedbd
00e55f9
48d888e
6ece7de
d8fe74b
f1308c8
91d3231
eb8003d
9bcf4d5
4b2f6bf
b9564bf
2dc1074
071be96
93af240
0c69486
d6ea23c
a85006a
890dafb
157cc9a
e4f880f
ea41a49
ca212df
43dac51
1da632b
884b5a1
27162a5
bac65bb
1ca2139
f2e8c15
af2fd8d
5755616
ce943bd
ea6f6e6
635964d
da2206b
c178033
c00979e
e06d963
d26e240
60d197a
7d28b2d
2fd0acd
02fc405
06286aa
2845658
360fcbc
4a53f6c
af0b969
49a2d06
2a5695f
05918a1
beac1ca
de7ecb2
17c62ae
728332b
66ad9dd
4d41337
af05eef
41e3052
fa6595f
55a07eb
231e714
9119775
74479e0
2deab2a
24b8aa5
563dc17
36c4376
1e4d165
93e0690
874fb7b
c98c10b
64081a0
e795c7f
6d69914
4319193
cc046bb
2e55f73
80b51f7
da27dad
baa608c
9e9fa3a
d308437
124fb19
db6e8fe
2bf5992
b35557e
33af9e5
0be6995
eb83a79
e973ee0
5662c86
0bc28b3
0ec42d4
3e1bff3
e46ee09
0309546
7f13db3
a01364a
e2fd810
cb64c9a
964f65d
5034f44
275b396
13f5769
065210a
4f3634c
d75698c
ca4d991
36ad9c6
5b39088
ba70210
8326048
2d57af4
cb3df0a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,51 +1,51 @@ | ||
# Author: Rishab | ||
name: Add Collaborators v1.1 | ||
# # Author: Rishab | ||
# name: Add Collaborators v1.1 | ||
|
||
on: | ||
push: | ||
branches: | ||
- master | ||
- main | ||
- dev | ||
# on: | ||
# push: | ||
# branches: | ||
# - master | ||
# - main | ||
# - dev | ||
|
||
jobs: | ||
add-collaborators: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Checkout Repo | ||
uses: actions/checkout@v2 | ||
- name: Add Collaborators | ||
uses: actions/[email protected] | ||
with: | ||
github-token: ${{secrets.ADMIN_GITHUB_TOKEN}} | ||
script: | | ||
const fs = require('fs'); | ||
const readline = require('readline'); | ||
const readInterface = readline.createInterface({ | ||
input: fs.createReadStream('./COLLABORATORS'), | ||
output: process.stdout, | ||
console: false | ||
}); | ||
readInterface.on('line', async function(line) { | ||
try { | ||
await github.repos.checkCollaborator({ | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
username: line, | ||
}); | ||
} catch(err) { | ||
console.log(err) | ||
if(err.toString() === "HttpError: Not Found"){ | ||
await github.repos.addCollaborator({ | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
username: line, | ||
permission: 'push' | ||
}); | ||
} else { | ||
core.setFailed(err.toString()) | ||
} | ||
# jobs: | ||
# add-collaborators: | ||
# runs-on: ubuntu-latest | ||
# steps: | ||
# - name: Checkout Repo | ||
# uses: actions/checkout@v2 | ||
# - name: Add Collaborators | ||
# uses: actions/[email protected] | ||
# with: | ||
# github-token: ${{secrets.ADMIN_GITHUB_TOKEN}} | ||
# script: | | ||
# const fs = require('fs'); | ||
# const readline = require('readline'); | ||
# const readInterface = readline.createInterface({ | ||
# input: fs.createReadStream('./COLLABORATORS'), | ||
# output: process.stdout, | ||
# console: false | ||
# }); | ||
# readInterface.on('line', async function(line) { | ||
# try { | ||
# await github.repos.checkCollaborator({ | ||
# owner: context.repo.owner, | ||
# repo: context.repo.repo, | ||
# username: line, | ||
# }); | ||
# } catch(err) { | ||
# console.log(err) | ||
# if(err.toString() === "HttpError: Not Found"){ | ||
# await github.repos.addCollaborator({ | ||
# owner: context.repo.owner, | ||
# repo: context.repo.repo, | ||
# username: line, | ||
# permission: 'push' | ||
# }); | ||
# } else { | ||
# core.setFailed(err.toString()) | ||
# } | ||
|
||
} | ||
}); | ||
# } | ||
# }); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
# Author: Haowei Li | ||
# (It seems like a trend for yml that people put names, so I do it too) | ||
|
||
# The purpose of this yml file is to run some tests on the client to see if everything runs as supposed to | ||
# client is based on React.JS | ||
name: Unit Test | ||
on: | ||
push: | ||
branches: | ||
- main | ||
- dev | ||
defaults: | ||
run: | ||
working-directory: client | ||
jobs: | ||
tests: | ||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- uses: actions/checkout@v2 | ||
|
||
- name: Set up Node.js version | ||
uses: actions/setup-node@v1 | ||
with: | ||
node-version: '14.x' | ||
|
||
- name: npm install, build, and test | ||
run: | | ||
npm install | ||
npm install [email protected] | ||
npm test | ||
#npm install [email protected] |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -104,11 +104,12 @@ gcloud run deploy api-dev \ | |
|
||
### Next Steps | ||
|
||
- Create different user types, with support for different user permissions | ||
- Motivation: so patients and caretakers can both upload/manage files on behalf of the patient | ||
- Suggested direction | ||
- Currently, the ERD of the system looks as follows: [link](https://excalidraw.com/#json=-LCSG-ShDmak9AprUI9LT,zhR7TQiJovH9fbLHI2MJsA) | ||
- A modification of the database and user flow to follow this general guideline would allow for these features to be built, as this would allow both patients and caretakers to upload/retrieve from the same document within the newly created collections: [link](https://excalidraw.com/#json=21EzZvSgTpRM558zRtxWx,e5qdQqTUEmp2myNCfwgo-g) | ||
- Develop additional audio processing features: | ||
- Background noise reduction | ||
- Improve the clarity of slurred subject voice | ||
- Other features: | ||
- Folder layout in processed files pages | ||
- User is able to delete processed files | ||
- User input in trimming/splitting process | ||
|
||
- User input in trimming/splitting process | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please expand this section to be more verbose and better explain why the user features are needed etc. Talk about use cases etc There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will do There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done! |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,7 +8,6 @@ | |
import os | ||
import datetime | ||
from flask import Flask, request, jsonify | ||
from flask_cors import CORS | ||
from google.cloud import firestore | ||
import google.auth.transport.requests | ||
from google.oauth2 import id_token | ||
|
@@ -158,6 +157,7 @@ def upload_audios(): | |
return 'Unauthorized', 401 | ||
user_ref = users_collection.document(claims['sub']) | ||
doc = user_ref.get() | ||
# return {str(doc)}, 200 | ||
if doc.exists: | ||
doc = doc.to_dict() | ||
for file in files: | ||
|
@@ -171,30 +171,28 @@ def upload_audios(): | |
# upload processed audio | ||
for path in processedFilePaths: | ||
dest_processed_file = f'Audio{uuid.uuid1()}.wav' | ||
processedFileName = path.split('/tmp/')[1] | ||
originalFileName = path.split('/tmp/')[1] | ||
blob = bucket.blob(dest_processed_file) | ||
blob.upload_from_filename(path, content_type='audio/wav') | ||
created = False | ||
if "audio" in doc: | ||
doc["audio"].append({processedFileName: destination_file_name, "processed": {dest_processed_file}}) | ||
# check if file already exists | ||
for audio in doc["audio"]: | ||
if destination_file_name in str(list(audio.values())): | ||
created = True | ||
audio["processed"].append(dest_processed_file) | ||
if not created: | ||
doc["audio"].append({originalFileName: destination_file_name, "processed": [dest_processed_file]}) | ||
user_ref.update({"audio": doc["audio"]}) | ||
else: | ||
user_ref.update({"audio": [{processedFileName: destination_file_name, "processed "+ destination_file_name: {dest_processed_file}}]}) | ||
doc = user_ref.get() | ||
doc = doc.to_dict() | ||
# if "audio" in doc: | ||
# doc["audio"].append({file.filename: destination_file_name}) | ||
# user_ref.update({"audio": doc["audio"]}) | ||
# else: | ||
# user_ref.update({"audio": [{file.filename: destination_file_name}]}) | ||
user_ref.update({"audio": [{originalFileName: destination_file_name, "processed": [dest_processed_file]}]}) | ||
return {'message': 'Files uploaded successfully'}, 200 | ||
else: | ||
print(u'No such document!') | ||
return {'message': 'No such document'}, 400 | ||
|
||
|
||
# Get Audio | ||
|
||
|
||
@app.route('/retrieve_audio', methods=['POST']) | ||
def retrieve_audio(): | ||
fileName = request.json['fileName'] | ||
|
@@ -208,7 +206,78 @@ def retrieve_audio(): | |
print("Generated GET signed URL:") | ||
return url | ||
|
||
@app.route('/delete_unprocessed_audio', methods=['DELETE']) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add documentation on the expected form of the HTTP request. Should provide an example HTTP request. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done! |
||
def delete_unprocessed_audio(): | ||
# In firebase, under audio, there is a list of uploaded audio, | ||
# each uploaded audio has two entries: the original audio file name and "processed". | ||
# The original audio file is by itself like "name":"address", | ||
# while the "processed" is a list containing the names of the processed audio files like "processed":"[address1, address2, address2]". | ||
|
||
cloud_storage_filename = request.json['cloudStorageFileName'] | ||
auth_header = request.json['Authorization'] | ||
|
||
# delete the audio from cloud storage | ||
blob = bucket.blob(cloud_storage_filename) | ||
blob.delete() | ||
|
||
# delete the audio from firebase, and the associated processed audios from cloud storage | ||
idtoken = auth_header.split(' ').pop() | ||
claims = id_token.verify_firebase_token( | ||
idtoken, HTTP_REQUEST, audience=os.environ.get('GOOGLE_CLOUD_PROJECT')) | ||
user_ref = users_collection.document(claims['sub']) | ||
|
||
doc = user_ref.get() | ||
if doc.exists: | ||
doc = doc.to_dict() | ||
new_audios = [] | ||
for audio in doc["audio"]: | ||
if cloud_storage_filename not in list(audio.values()): | ||
new_audios.append(audio) | ||
Comment on lines
+243
to
+244
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do you do this? It seems that the user could pass in arbitrary audio file names and they could get appended to the users list of audio. Which would not be valid since they would not actually exist. It seems you should check if the passed audio is in the list, if it's not then that's an error. Since we must use the database as the single source of truth since it's the only thing that keeps data on what audio belongs to each person. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Audio is an example audio already existing in the user's document (see lines 229-233, these get the user doc, then the audios). So, this line is seeing if the unprocessed audio is part of this given unprocessed/processed audio combination. Then, if it is not it appends it to new_audios which is becomes the new list of audios in the firebase. |
||
else: | ||
# delete the processed audios from cloud storage | ||
for processed_audio in audio["processed"]: | ||
blob = bucket.blob(processed_audio) | ||
blob.delete() | ||
|
||
user_ref.update({'audio': new_audios}) | ||
return {'message': "Success"}, 200 | ||
else: | ||
print(u'No such document!') | ||
return {'message': 'No such document'}, 400 | ||
|
||
|
||
@app.route('/delete_processed_audio', methods=['DELETE']) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function does the same thing as the other one. Please find a way to not duplicate the code. Instead, maybe write an inner function that you pass a parameter of some sort to etc. If you're copying and pasting code you're doing something wrong (: (Normally but not always) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They have some slight differences. We have changed the database a little bit, and yes, there is more elegant way of combining them, but they are essentially deleting different things so we figured it is better having two of them separated to avoid future confusion There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add a comment in the code on what the slight difference is! The next team will need to know. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done! Left extensive comments. |
||
def delete_processed_audio(): | ||
# get request data | ||
cloud_storage_filename = request.json['cloudStorageFileName'] | ||
auth_header = request.json['Authorization'] | ||
|
||
# delete the audio from cloud storage | ||
blob = bucket.blob(cloud_storage_filename) | ||
blob.delete() | ||
|
||
# delete the audio from firebase, and the associated processed audios from cloud storage | ||
idtoken = auth_header.split(' ').pop() | ||
claims = id_token.verify_firebase_token( | ||
idtoken, HTTP_REQUEST, audience=os.environ.get('GOOGLE_CLOUD_PROJECT')) | ||
user_ref = users_collection.document(claims['sub']) | ||
|
||
doc = user_ref.get() | ||
if doc.exists: | ||
doc = doc.to_dict() | ||
for audio in doc["audio"]: | ||
if cloud_storage_filename in audio['processed']: | ||
audio['processed'].remove(cloud_storage_filename) | ||
|
||
user_ref.update({'audio': doc['audio']}) | ||
return {'message': 'File deleted successfully'}, 200 | ||
|
||
else: | ||
print(u'No such document!') | ||
return {'message': 'No such document'}, 400 | ||
|
||
|
||
|
||
port = int(os.environ.get('PORT', 8080)) | ||
if __name__ == '__main__': | ||
app.run(threaded=True, host='0.0.0.0', port=port) | ||
app.run(threaded=True, host='0.0.0.0', port=port, debug=True) | ||
IanSaucy marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,8 +21,8 @@ def deadSpace(filePath): | |
|
||
sample_rate = 48000 | ||
|
||
prepPath = '/tmp/' + re.split('.wav|.WAV', (filePath.split('/tmp/')[1]))[0] + 'Edited.WAV' | ||
|
||
#prepPath = '/tmp/' + re.split('.wav|.WAV', (filePath.split('/tmp/')[1]))[0] + 'Edited.WAV' # the original way that append "Edited" each chunk | ||
prepPath = '/tmp/' + re.split('.wav|.WAV', (filePath.split('/tmp/')[1]))[0] + '.WAV' | ||
Comment on lines
+24
to
+25
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens if the file does not end in .wav? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is still .wav, we just remove "Edited" since there are original clip and processed clip stored and displayed separately There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My question was more, what happens if the user uploads something that is not a .wav file? Recall that any frontend verification can always be bypassed so the API should not trust any data it receives. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. bump @Li-Haowei |
||
# Preparation of wav file for vad function | ||
# pySeg = AudioSegment.from_wav(filePath).set_channels(1).set_frame_rate(sample_rate).export(prepPath, format='wav') | ||
pySeg = AudioSegment.from_file(filePath).set_channels(1).set_frame_rate(sample_rate).export(prepPath, format='wav') | ||
|
@@ -40,7 +40,8 @@ def deadSpace(filePath): | |
|
||
# Writes voice audio chunks + appends valid files | ||
for i, segment in enumerate(segments): | ||
path = prepPath.split('.WAV')[0] + 'chunk-'+ str(i) + '.WAV' | ||
# path = prepPath.split('.WAV')[0] + 'chunk-'+ str(i) + '.WAV' # the original way that indexes each chunk | ||
path = prepPath.split('.WAV')[0] + '.WAV' | ||
print(' Writing %s' % (path)) | ||
vadfuncs.write_wave(path, segment, sample_rate) | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,5 +5,7 @@ google-auth | |
google-cloud-firestore | ||
google-cloud-storage | ||
pydub | ||
ffprobe | ||
ffmpeg | ||
webrtcvad | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
// Reference: https://forum.codewithmosh.com/t/7-social-tests-still-referenceerror-regeneratorruntime-is-not-defined-resolved/1978 | ||
{ | ||
"plugins": ["@babel/plugin-transform-runtime"] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// my-module-resolve.js | ||
module.exports = (request, options) => { | ||
// Call the defaultResolver, so we leverage its cache, error handling, etc. | ||
return options.defaultResolver(request, { | ||
...options, | ||
// Use packageFilter to process parsed `package.json` before the resolution (see https://www.npmjs.com/package/resolve#resolveid-opts-cb) | ||
packageFilter: pkg => { | ||
if(pkg.name.startsWith('@firebase')) { | ||
return { | ||
...pkg, | ||
// Alter the value of `main` before resolving the package | ||
main: pkg.esm5 || pkg.module, | ||
}; | ||
} | ||
|
||
return pkg; | ||
}, | ||
}); | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add a comment in this file on why this file exists There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, just added comments |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please restore the original version of this file
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I commented them out since the github action runs everytime we push and it slow down my testing on my unit test yml. I have reverted it back to older version