Skip to content

Commit

Permalink
enable mass presign support
Browse files Browse the repository at this point in the history
  • Loading branch information
devksingh4 committed Sep 6, 2024
1 parent 5cbe789 commit 4caf4a6
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 4 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ __pycache__
test.json
build
.vscode/settings.json
node_modules/
node_modules/
api/.mise.toml
3 changes: 2 additions & 1 deletion api/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
aws-lambda-powertools
pydantic[email]
psycopg[binary]
openai
openai
aioboto3
20 changes: 20 additions & 0 deletions api/util/s3.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,28 @@
from util.logging import get_logger
import boto3
import re
import aioboto3
import asyncio
logger = get_logger()

async def async_generate_presigned_urls(bucket_name, keys, expiration=14400):
async with aioboto3.client('s3') as s3_client:
tasks = []

for key in keys:
tasks.append(
s3_client.generate_presigned_url(
'get_object',
Params={'Bucket': bucket_name, 'Key': key},
ExpiresIn=expiration
)
)

# Gather all presigned URL creation tasks asynchronously
presigned_urls = await asyncio.gather(*tasks)

return presigned_urls

def create_presigned_url_from_s3_url(s3_client, s3_url, expiration=60):
"""
Generate a presigned URL to share an S3 object
Expand Down
36 changes: 35 additions & 1 deletion api/util/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,19 @@
from util.postgres import get_db_connection
from util.structs import (
DEFAULT_USER_PROFILE,
MassDownloadResumesRequest,
ProfileSearchRequest,
ResumeUploadPresignedRequest,
StudentProfileDetails,
GenerateProfileRequest,
convert_dict_keys_snake_to_camel,
)
from util.environ import get_run_environment
from util.s3 import create_presigned_url_for_put, create_presigned_url_from_s3_url
from util.s3 import create_presigned_url_for_put, create_presigned_url_from_s3_url, async_generate_presigned_urls
from util.logging import get_logger
from util.secretsmanager import get_parameter_from_sm
from psycopg.rows import dict_row
import asyncio

RUN_ENV = get_run_environment()
logger = get_logger()
Expand Down Expand Up @@ -312,3 +314,35 @@ def recruiter_perform_search():
content_type=content_types.APPLICATION_JSON,
body=search_result,
)

@app.post("/api/v1/recruiter/mass_download")
def recruiter_perform_search():
json_body: dict = app.current_event.json_body or {}
urls = []
try:
data = MassDownloadResumesRequest(**json_body).model_dump()
if len(keys) > 10:
logger.info("Using asyncio loop")
keys = [f"resume_{username}.pdf" for username in data['usernames']]
urls = asyncio.run(async_generate_presigned_urls(S3_BUCKET, keys))
else:
logger.info("Using blocking loop")
urls = [create_presigned_url_from_s3_url(s3_client, f"s3://{S3_BUCKET}/resume_{username}.pdf") for username in data['usernames'] ]
except pydantic.ValidationError as e:
return Response(
status_code=403,
content_type=content_types.APPLICATION_JSON,
body={"message": "Error validating payload", "details": str(e)},
)
except Exception as e:
logger.error(traceback.format_exc())
return Response(
status_code=500,
content_type=content_types.APPLICATION_JSON,
body={"message": "Error performing profile search", "details": str(e)},
)
return Response(
status_code=200,
content_type=content_types.APPLICATION_JSON,
body=urls,
)
5 changes: 4 additions & 1 deletion api/util/structs.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from typing import List, Literal, Optional
from pydantic import BaseModel, ConfigDict, EmailStr, AnyUrl, Field, HttpUrl
from pydantic import BaseModel, ConfigDict, EmailStr, AnyUrl, Field, HttpUrl, validator
from .oai import LENGTH_LIMIT

class DegreeListing(BaseModel):
Expand Down Expand Up @@ -41,6 +41,9 @@ class GenerateProfileRequest(BaseModel):
roleType: Literal["Internship"] | Literal['Full-Time'] | Literal['Research Assistant']
roleKeywords: List[str]

class MassDownloadResumesRequest(BaseModel):
usernames: List[str]

DEFAULT_USER_PROFILE = {
"defaultResponse": True,
"username": "someone@illinois.edu",
Expand Down

0 comments on commit 4caf4a6

Please sign in to comment.