Skip to content

Commit cda6eb8

Browse files
Merge branch 'main' into delete_existing_skill
2 parents 7cdf0ba + 3d10db0 commit cda6eb8

File tree

7 files changed

+265
-68
lines changed

7 files changed

+265
-68
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: 77 additions & 39 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
}
@@ -42,7 +43,7 @@ def hello_world():
4243

4344

4445
@app.route("/resume/experience", methods=["GET", "POST"])
45-
@app.route("/resume/experience/<int:index>", methods=["GET"])
46+
@app.route("/resume/experience/<int:index>", methods=["GET", "DELETE"])
4647
def experience(index=None):
4748
'''
4849
Handle experience requests
@@ -57,34 +58,31 @@ 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
except TypeError as e:
8169
return jsonify({"error": f"Invalid data format: {str(e)}"}), 400
8270
except Exception as e:
8371
return jsonify({"error": f"Internal error: {str(e)}"}), 500
8472

73+
if request.method == "DELETE":
74+
try:
75+
if index is None or index < 0 or index >= len(data["experience"]):
76+
return jsonify({"message": "Resource doesn't exist"}), 404
77+
else:
78+
data['experience'].pop(index)
79+
return jsonify({"message": "Experience Successfully Deleted"}), 200
80+
except Exception as e:
81+
return jsonify({"error": f"An error occurred: {str(e)}"}), 500
8582

8683
return jsonify({"error": "Method not allowed"}), 405
8784

85+
8886
@app.route('/resume/experience/<int:exp_id>', methods=['DELETE'])
8987
def delete_experience(exp_id):
9088
try:
@@ -97,19 +95,64 @@ def delete_experience(exp_id):
9795
return jsonify({"error": f"An error occurred: {str(e)}"}), 500
9896

9997

98+
99+
@app.route('/resume/spell_check', methods=['POST'])
100+
def spell_check():
101+
json_data = request.json
102+
if json_data.get('description') and isinstance(json_data.get('description'), str):
103+
json_data['description'] = spell_check(json_data['description'])
104+
return jsonify({
105+
"before": request.json,
106+
"after": json_data
107+
})
108+
109+
100110
@app.route("/resume/education", methods=["GET", "POST"])
101-
def education():
111+
@app.route("/resume/education/<int:edu_id>", methods=["GET", "DELETE"])
112+
def education(edu_id=None):
102113
'''
103114
Handles education requests
115+
GET: Returns all educations (unimplemented here)
116+
POST: Creates a new education (unimplemented here)
117+
DELETE: Deletes an education by index
104118
'''
105119
if request.method == "GET":
106120
return jsonify({})
107121

108122
if request.method == "POST":
109123
return jsonify({})
110124

125+
if request.method == "DELETE":
126+
try:
127+
if edu_id is None or edu_id < 0 or edu_id >= len(data["education"]):
128+
return jsonify({"message": "Resource doesn't exist"}), 404
129+
else:
130+
del data["education"][edu_id]
131+
return jsonify({"message": "Education Successfully Deleted"}), 200
132+
except Exception as e:
133+
return jsonify({"error": f"An error occurred: {str(e)}"}), 500
134+
111135
return jsonify({})
112136

137+
@app.route('/resume/reword_description', methods=['GET'])
138+
def reword_description():
139+
'''
140+
Rewords the description using GPT
141+
'''
142+
model = None
143+
try:
144+
model = Experience(**request.json)
145+
except:
146+
model = Education(**request.json)
147+
148+
if model is None:
149+
return jsonify({"error": "Invalid request"}), 400
150+
151+
response = get_improvement(model)
152+
if response is None:
153+
return jsonify({"error": "Failed to get improvement"}), 500
154+
155+
return jsonify({"response": response})
113156

114157
@app.route("/resume/skill", methods=["GET", "POST"])
115158
@app.route('/resume/skill/<int:skill_id>', methods=['DELETE'])
@@ -123,20 +166,14 @@ def skill(skill_id = None):
123166
if request.method == 'POST':
124167
json_data = request.json
125168
try:
126-
# extract the data from the request
127-
name = json_data["name"]
128-
proficiency = json_data["proficiency"]
129-
logo = json_data["logo"]
169+
validated_data = validate_skill(json_data)
130170

131-
new_skill = Skill(name, proficiency, logo)
132-
133-
data["skill"].append(new_skill)
171+
data["skill"].append(validated_data)
134172

135173
# return ID of new skill
136174
return jsonify(
137175
{"id": len(data["skill"]) - 1}
138176
), 201
139-
140177
except KeyError:
141178
return jsonify({"error": "Invalid request"}), 400
142179

@@ -149,7 +186,8 @@ def skill(skill_id = None):
149186
else:
150187
del data['skill'][skill_id]
151188
return jsonify({"message": "Skill Successfully Deleted"}), 200
152-
189+
except (ValueError, TypeError, KeyError) as e:
190+
return jsonify({"error": f"Invalid request: {str(e)}"}), 400
153191
except Exception as e:
154192
return jsonify({"error": f"An error occurred: {str(e)}"}), 500
155193

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: 64 additions & 28 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
@@ -35,33 +35,6 @@ def test_experience():
3535

3636

3737

38-
# def test_delete_experience():
39-
40-
# example_experience = {
41-
# "title": "Mechanic",
42-
# "company": "Decepticons .Ent",
43-
# "start_date": "January 2020",
44-
# "end_date": "Present",
45-
# "description": "Hail Megatron",
46-
# "logo" : "example-log.png"
47-
# }
48-
# response = app.test_client().post('/resume/experience', json= example_experience)
49-
# assert response.status_code == 200
50-
51-
# get_all_experiences = app.test_client().get('/resume/experience')
52-
# experience_id = len(get_all_experiences.json) - 1
53-
54-
# delete_response = app.test_client().delete(f'/resume/experience/{experience_id}')
55-
# assert delete_response.status_code == 200
56-
# deleted_experience = delete_response.json['deleted_experience']
57-
58-
# assert example_experience == deleted_experience
59-
60-
# get_exp = app.test_client().get('/resume/experience')
61-
# for experience in get_exp.json:
62-
# assert experience != deleted_experience
63-
64-
6538
def test_education():
6639
'''
6740
Add a new education and then get all educations.
@@ -98,8 +71,71 @@ def test_skill():
9871
item_id = app.test_client().post('/resume/skill',
9972
json=example_skill).json['id']
10073

74+
10175
response = app.test_client().get('/resume/skill')
10276
assert response.json[item_id] == example_skill
10377

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

0 commit comments

Comments
 (0)