Skip to content

Commit 7caaefc

Browse files
committed
Merge branch 'main' into issue17
2 parents 52445de + 019780c commit 7caaefc

File tree

7 files changed

+245
-49
lines changed

7 files changed

+245
-49
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
.venv
33
.pytest_cache/
44
__pycache__
5-
.vscode
5+
.vscode
6+
.env

app.py

Lines changed: 59 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
'''
44

55
from flask import Flask, jsonify, request
6-
7-
from models import Education, Experience, Skill
6+
from models import Experience, Education, Skill
7+
from gpt_connection import get_improvement
8+
from validation import validate_experience, validate_education, validate_skill
9+
from spell_check import spell_check
810

911
app = Flask(__name__)
1012

@@ -20,14 +22,13 @@
2022
)
2123
],
2224
"education": [
23-
Education(
24-
"Computer Science",
25-
"University of Tech",
26-
"September 2019",
27-
"July 2022",
28-
"80%",
29-
"example-logo.png",
30-
)
25+
Education("Computer Science",
26+
"University of Tech",
27+
"September 2019",
28+
"July 2022",
29+
"80%",
30+
"example-logo.png",
31+
"I was head of the debate team at university")
3132
],
3233
"skill": [Skill("Python", "1-2 Years", "example-logo.png")],
3334
}
@@ -57,35 +58,33 @@ def experience(index=None):
5758
return jsonify({"error": "Experience not found"}), 404
5859
return jsonify(data["experience"]), 200
5960

60-
if request.method == "POST":
61+
if request.method == 'POST':
62+
json_data = request.json
6163
try:
62-
new_experience = request.get_json()
63-
if not new_experience:
64-
return jsonify({"error": "No data provided"}), 400
65-
# validate required fields
66-
required_fields = [
67-
"title",
68-
"company",
69-
"start_date",
70-
"end_date",
71-
"description",
72-
"logo",
73-
]
74-
if not all(field in new_experience for field in required_fields):
75-
return jsonify({"error": "Missing required fields"}), 400
76-
77-
experience_obj = Experience(**new_experience)
78-
data["experience"].append(experience_obj)
64+
validated_data = validate_experience(json_data)
65+
66+
data["experience"].append(validated_data)
7967
return jsonify({"id": len(data["experience"]) - 1}), 201
8068

81-
except TypeError as e:
69+
except (ValueError, TypeError, KeyError) as e:
8270
return jsonify({"error": f"Invalid data format: {str(e)}"}), 400
8371
except Exception as e:
8472
return jsonify({"error": f"Internal error: {str(e)}"}), 500
8573

8674

8775
return jsonify({"error": "Method not allowed"}), 405
8876

77+
78+
@app.route('/resume/spell_check', methods=['POST'])
79+
def spell_check():
80+
json_data = request.json
81+
if json_data.get('description') and isinstance(json_data.get('description'), str):
82+
json_data['description'] = spell_check(json_data['description'])
83+
return jsonify({
84+
"before": request.json,
85+
"after": json_data
86+
})
87+
8988
@app.route("/resume/education", methods=["GET", "POST"])
9089
@app.route("/resume/education/<int:index>", methods=["GET"])
9190
def education(index=None):
@@ -100,12 +99,36 @@ def education(index=None):
10099
return jsonify(data["education"][index])
101100
except IndexError:
102101
return jsonify({"error": "Index out of range"})
102+
103+
if request.method == 'POST':
104+
json_data = request.json
105+
try:
106+
validated_data = validate_education(json_data)
107+
return jsonify(validated_data)
108+
except ValueError as e:
109+
return jsonify({"error": str(e)}), 400
110+
111+
return jsonify({})
103112

104-
if request.method == "POST":
105-
return jsonify({})
113+
@app.route('/resume/reword_description', methods=['GET'])
114+
def reword_description():
115+
'''
116+
Rewords the description using GPT
117+
'''
118+
model = None
119+
try:
120+
model = Experience(**request.json)
121+
except:
122+
model = Education(**request.json)
106123

107-
return jsonify({})
124+
if model is None:
125+
return jsonify({"error": "Invalid request"}), 400
108126

127+
response = get_improvement(model)
128+
if response is None:
129+
return jsonify({"error": "Failed to get improvement"}), 500
130+
131+
return jsonify({"response": response})
109132

110133
@app.route("/resume/skill", methods=["GET", "POST"])
111134
def skill():
@@ -118,25 +141,17 @@ def skill():
118141
if request.method == 'POST':
119142
json_data = request.json
120143
try:
121-
# extract the data from the request
122-
name = json_data["name"]
123-
proficiency = json_data["proficiency"]
124-
logo = json_data["logo"]
125-
126-
new_skill = Skill(name, proficiency, logo)
144+
validated_data = validate_skill(json_data)
127145

128-
data["skill"].append(new_skill)
146+
data["skill"].append(validated_data)
129147

130148
# return ID of new skill
131149
return jsonify(
132150
{"id": len(data["skill"]) - 1}
133151
), 201
134152

135-
except KeyError:
136-
return jsonify({"error": "Invalid request"}), 400
137-
138-
except TypeError as e:
139-
return jsonify({"error": str(e)}), 400
153+
except (ValueError, TypeError, KeyError) as e:
154+
return jsonify({"error": f"Invalid request: {str(e)}"}), 400
140155

141156
return jsonify({})
142157

gpt_connection.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
2+
3+
from openai import OpenAI
4+
import os
5+
from dotenv import load_dotenv
6+
from models import Experience, Education
7+
load_dotenv()
8+
9+
client = OpenAI(
10+
api_key=os.getenv("OPENAI_API_KEY")
11+
)
12+
13+
def get_improvement(model : Experience | Education):
14+
prompt = f"Improve the following description for the {model.__class__.__name__}: {model.description}"
15+
16+
response = client.chat.completions.create(
17+
model="gpt-4o-mini",
18+
messages=[
19+
{"role": "system", "content": "You are a helpful assistant that improves descriptions for resumes."},
20+
{"role": "assistant", "content": "Here is the improved description:"},
21+
{"role": "system", "content": f"Consider the following information about the {model.__class__.__name__}: {model.model_dump_json()}"},
22+
{"role": "user", "content": prompt},
23+
]
24+
)
25+
try:
26+
return response.choices[0].message.content
27+
except:
28+
return None
29+
30+
31+

models.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
'''
66

77
from dataclasses import dataclass
8+
import json
89

910

1011
@dataclass
@@ -19,6 +20,11 @@ class Experience:
1920
description: str
2021
logo: str
2122

23+
def model_dump_json(self):
24+
'''
25+
Dumps the class into a JSON string
26+
'''
27+
return json.dumps(self, default=lambda o: o.__dict__)
2228

2329
@dataclass
2430
class Education:
@@ -31,6 +37,13 @@ class Education:
3137
end_date: str
3238
grade: str
3339
logo: str
40+
description: str
41+
42+
def model_dump_json(self):
43+
'''
44+
Dumps the class into a JSON string
45+
'''
46+
return json.dumps(self, default=lambda o: o.__dict__)
3447

3548

3649
@dataclass

spell_check.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
"""
2+
Module for spell checking functionality using autocorrect library.
3+
"""
4+
5+
from autocorrect import Speller
6+
7+
spell = Speller(lang='en')
8+
9+
def spell_check(text):
10+
'''
11+
Spell checks the text
12+
'''
13+
return spell(text)

test_pytest.py

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
'''
44
from app import app
55

6-
6+
77
def test_client():
88
'''
99
Makes a request and checks the message received is the same
@@ -70,8 +70,66 @@ def test_skill():
7070
item_id = app.test_client().post('/resume/skill',
7171
json=example_skill).json['id']
7272

73-
response = app.test_client().get('/resume/skill')
74-
assert response.json[item_id] == example_skill
75-
7673
response = app.test_client().get(f'/resume/skill/{item_id}')
7774
assert response.json == example_skill
75+
76+
77+
def test_model_validation():
78+
'''
79+
Test that the model validation returns a valid response
80+
'''
81+
data = {
82+
"experience": {
83+
"title": "Software Developer",
84+
"company": "A Cooler Company",
85+
"start_date": "October 2022",
86+
"end_date": "Present",
87+
"description": "Writing JavaScript Code",
88+
"logo": "example-logo.png"
89+
},
90+
"education": {
91+
"course": "Engineering",
92+
"school": "NYU",
93+
"start_date": "October 2022",
94+
"end_date": "August 2024",
95+
"grade": "86%",
96+
"logo": "example-logo.png",
97+
"description": "I was head of the debate team at university"
98+
},
99+
"skill": {
100+
"name": "JavaScript",
101+
"proficiency": "2-4 years",
102+
"logo": "example-logo.png"
103+
}
104+
}
105+
response_education = app.test_client().post('/resume/education',
106+
json=data['education'])
107+
response_experience = app.test_client().post('/resume/experience',
108+
json=data['experience'])
109+
response_skill = app.test_client().post('/resume/skill',
110+
json=data['skill'])
111+
assert response_education.status_code == 200
112+
assert response_experience.status_code == 200
113+
assert response_skill.status_code == 200
114+
115+
def test_spell_check():
116+
'''
117+
Test that the spell check endpoint returns a valid response
118+
'''
119+
120+
example_education = {
121+
"course": "Engineering",
122+
"school": "NYU",
123+
"start_date": "October 2022",
124+
"end_date": "August 2024",
125+
"grade": "86%",
126+
"logo": "example-logo.png",
127+
"description": "I was head of the debaite team at university",
128+
"spell_check": True
129+
}
130+
131+
response = app.test_client().post('/resume/education',
132+
json=example_education)
133+
134+
assert response.json['description'] == "I was head of the debate team at university"
135+

validation.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
from models import Experience, Education, Skill
2+
3+
def validate_experience(json_data: dict):
4+
'''
5+
Validates the experience
6+
'''
7+
if 'spell_check' in json_data:
8+
del json_data['spell_check']
9+
try:
10+
return Experience(**json_data)
11+
except Exception as e:
12+
raise ValueError(f"Invalid experience format: {e}")
13+
14+
def validate_education(json_data: dict):
15+
'''
16+
Validates the education
17+
'''
18+
if 'spell_check' in json_data:
19+
del json_data['spell_check']
20+
try:
21+
return Education(**json_data)
22+
except Exception as e:
23+
raise ValueError(f"Invalid education format: {e}")
24+
25+
def validate_skill(json_data: dict):
26+
'''
27+
Validates the skill
28+
'''
29+
if 'spell_check' in json_data:
30+
del json_data['spell_check']
31+
try:
32+
return Skill(**json_data)
33+
except Exception as e:
34+
raise ValueError(f"Invalid skill format: {e}")
35+
36+
37+
data = {
38+
"experience": {
39+
"title": "Software Developer",
40+
"company": "A Cooler Company",
41+
"start_date": "October 2022",
42+
"end_date": "Present",
43+
"description": "Writing JavaScript Code",
44+
"logo": "example-logo.png",
45+
"spell_check": True
46+
},
47+
"education": {
48+
"course": "Engineering",
49+
"school": "NYU",
50+
"start_date": "October 2022",
51+
"end_date": "August 2024",
52+
"grade": "86%",
53+
"logo": "example-logo.png",
54+
"description": "I was head of the debate team at university"
55+
},
56+
"skill": {
57+
"name": "JavaScript",
58+
"proficiency": "2-4 years",
59+
"logo": "example-logo.png"
60+
}
61+
}
62+
63+
print(validate_experience(data['experience']))
64+
print(validate_education(data['education']))
65+
print(validate_skill(data['skill']))

0 commit comments

Comments
 (0)