Skip to content

Commit abc5261

Browse files
committed
get resume works
1 parent 9a905c8 commit abc5261

File tree

4 files changed

+101
-13
lines changed

4 files changed

+101
-13
lines changed

clientv2/src/components/ProfileViewer/index.tsx

Lines changed: 57 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import React from 'react';
2-
import { Container, Text, Title, Group, Stack, Badge, Avatar, Anchor, List, ThemeIcon, Grid, Box } from '@mantine/core';
3-
import { IconBrandLinkedin, IconMail, IconSchool, IconBriefcase, IconUser, IconCertificate } from '@tabler/icons-react';
4-
import { Document, pdfjs } from "react-pdf";
1+
import React, { useState } from 'react';
2+
import { Container, Text, Title, Group, Stack, Badge, Anchor, List, ThemeIcon, Grid, Box, Button } from '@mantine/core';
3+
import { IconBrandLinkedin, IconMail, IconSchool, IconBriefcase, IconUser, IconCertificate, IconChevronLeft, IconChevronRight } from '@tabler/icons-react';
4+
import { Document, Page, pdfjs } from 'react-pdf';
55
pdfjs.GlobalWorkerOptions.workerSrc = new URL(
66
'pdfjs-dist/build/pdf.worker.min.mjs',
77
import.meta.url,
@@ -36,9 +36,15 @@ interface StudentProfilePageProps {
3636
}
3737

3838
const StudentProfilePage: React.FC<StudentProfilePageProps> = ({studentProfile: mockStudentProfile}) => {
39+
const [numPages, setNumPages] = useState<number>(0);
40+
const [pageNumber, setPageNumber] = useState<number>(1);
41+
42+
function onDocumentLoadSuccess({ numPages }: { numPages: number }): void {
43+
setNumPages(numPages);
44+
}
3945
return (
40-
<Container fluid style={{marginTop: '2vh'}}>
41-
<Grid gutter="xl">
46+
<Container fluid style={{marginTop: "2vh"}}>
47+
<Grid gutter="sm">
4248
<Grid.Col span={4}>
4349
<Container>
4450
<Stack spacing="md">
@@ -128,12 +134,53 @@ const StudentProfilePage: React.FC<StudentProfilePageProps> = ({studentProfile:
128134
</Grid.Col>
129135

130136
<Grid.Col span={8}>
131-
<Container>
132-
<Document file={mockStudentProfile.resumePdfUrl} onLoadError={console.error}/>
133-
</Container>
137+
<Box style={{ height: '100vh', minHeight: '600px', display: 'flex', flexDirection: 'column' }}>
138+
<Document
139+
file={mockStudentProfile.resumePdfUrl}
140+
onLoadSuccess={onDocumentLoadSuccess}
141+
options={{
142+
cMapUrl: 'https://unpkg.com/pdfjs-dist@3.4.120/cmaps/',
143+
cMapPacked: true,
144+
}}
145+
>
146+
<Box
147+
style={{
148+
border: '2px solid #FF5F05',
149+
borderRadius: '4px',
150+
padding: '8px',
151+
display: 'inline-block'
152+
}}
153+
>
154+
<Page
155+
pageNumber={pageNumber}
156+
renderTextLayer={false}
157+
renderAnnotationLayer={false}
158+
/>
159+
</Box>
160+
</Document>
161+
<Group position="apart" mt="md">
162+
<Button
163+
onClick={() => setPageNumber(page => Math.max(page - 1, 1))}
164+
disabled={pageNumber <= 1}
165+
leftIcon={<IconChevronLeft size={14} />}
166+
>
167+
Previous
168+
</Button>
169+
<Text>
170+
Page {pageNumber} of {numPages}
171+
</Text>
172+
<Button
173+
onClick={() => setPageNumber(page => Math.min(page + 1, numPages))}
174+
disabled={pageNumber >= numPages}
175+
rightIcon={<IconChevronRight size={14} />}
176+
>
177+
Next
178+
</Button>
179+
</Group>
180+
</Box>
134181
</Grid.Col>
135182
</Grid>
136-
</Container>
183+
</Container>
137184
);
138185
};
139186

code/util/s3.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import boto3
2+
import re
3+
import os
4+
5+
session = boto3.Session(region_name=os.environ.get('AWS_REGION', 'us-east-1'))
6+
s3_client = session.client('s3')
7+
8+
def create_presigned_url_from_s3_url(s3_url, expiration=60):
9+
"""
10+
Generate a presigned URL to share an S3 object
11+
12+
:param s3_url: S3 URL (e.g., 's3://bucket_name/object_key')
13+
:param expiration: Time in seconds for the presigned URL to remain valid
14+
:return: Presigned URL as string. If error, returns None.
15+
"""
16+
# Extract bucket name and object key from the S3 URL
17+
match = re.match(r's3://([^/]+)/(.+)', s3_url)
18+
if not match:
19+
raise ValueError(f"Invalid S3 URL: {s3_url}")
20+
21+
bucket_name, object_key = match.groups()
22+
23+
try:
24+
# Generate the presigned URL
25+
response = s3_client.generate_presigned_url('get_object',
26+
Params={'Bucket': bucket_name,
27+
'Key': object_key},
28+
ExpiresIn=expiration)
29+
except boto3.exceptions.S3UploadFailedError as e:
30+
print(e)
31+
return None
32+
33+
return response

code/util/server.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import os
77
from util.dynamo import convert_to_dynamodb_json, convert_from_dynamodb_json
88
from util.structs import StudentProfileDetails
9+
from util.environ import get_run_environment
10+
from util.s3 import create_presigned_url_from_s3_url
911
import json
1012
from decimal import Decimal
1113

@@ -16,6 +18,8 @@
1618
dynamodb = session.client('dynamodb')
1719

1820
PROFILE_TABLE_NAME = "infra-resume-book-profile-data"
21+
RUN_ENV = get_run_environment()
22+
S3_BUCKET = f"infra-resume-book-pdfs-{RUN_ENV}"
1923

2024
@app.get("/api/v1/healthz")
2125
def healthz():
@@ -38,14 +42,18 @@ def student_get_profile():
3842
except Exception as e:
3943
print(traceback.format_exc(), flush=True)
4044
return Response(status_code=500, content_type=content_types.APPLICATION_JSON, body={"message": "Error getting profile data", "details": str(e)})
41-
return Response(status_code=200, content_type=content_types.APPLICATION_JSON, body=convert_from_dynamodb_json(profile_data))
45+
# generate presigned url for resume pdf
46+
profile_data = convert_from_dynamodb_json(profile_data)
47+
profile_data['resumePdfUrl'] = create_presigned_url_from_s3_url(profile_data['resumePdfUrl'])
48+
return Response(status_code=200, content_type=content_types.APPLICATION_JSON, body=profile_data)
4249

4350
@app.post("/api/v1/student/profile")
4451
def student_post_profile():
4552
email = app.current_event.request_context.authorizer['username']
4653
json_body: dict = app.current_event.json_body
4754
json_body['email'] = email
4855
json_body['username'] = email
56+
json_body['resumePdfUrl'] = f"s3://{RUN_ENV}/resume_{email}.pdf"
4957
try:
5058
data = json.loads(StudentProfileDetails(**json_body).model_dump_json(), parse_float=Decimal)
5159
dynamo_data = convert_to_dynamodb_json(data)

code/util/structs.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from typing import List, Optional
2-
from pydantic import BaseModel, EmailStr, HttpUrl
2+
from pydantic import BaseModel, EmailStr, AnyUrl, HttpUrl
33

44
class DegreeListing(BaseModel):
55
level: str
@@ -20,7 +20,7 @@ class StudentProfileDetails(BaseModel):
2020
skills: List[str]
2121
work_auth_required: bool
2222
sponsorship_required: bool
23-
resumePdfUrl: HttpUrl
23+
resumePdfUrl: AnyUrl
2424

2525
if __name__ == "__main__":
2626
degree = DegreeListing(

0 commit comments

Comments
 (0)