Skip to content

Commit 262c29a

Browse files
authored
Create utility method that uses ai model to suggest imrpoved description (#31)
* feat: use google gemini model to suggest improved description for education and experience * fix: update method descriptor * feat: add api endpoint for getting description suggestion * fix: fix lint errors * fix: fix more lint errors * Merge branch 'main' of https://github.com/citruscai/orientation-project-python-24.FAL.B into feat/suggest-description-updates # Conflicts: # app.py # requirements.txt # utils.py * fix:resolve lint errors * fix: resolve lint errors part 2 * fix: resolve lint errors part 3 * fix: resolve lint errors part 4 * test: create test cases for description suggestion endpoint * fix:lint fix * fix: add patch import for mocking external dependencies in unit tests * fix: fixing lint errors again part idk * fix: lint errors agin gosh * i hate my life
1 parent 3aaf225 commit 262c29a

File tree

4 files changed

+180
-82
lines changed

4 files changed

+180
-82
lines changed

app.py

+100-75
Original file line numberDiff line numberDiff line change
@@ -1,171 +1,196 @@
1-
'''
1+
"""
22
Flask Application
3-
'''
3+
"""
4+
45
from flask import Flask, jsonify, request
56
from models import Experience, Education, Skill, User
6-
from utils import check_phone_number, correct_spelling
7+
from utils import get_suggestion, check_phone_number, correct_spelling
8+
9+
710
app = Flask(__name__)
811

912
data = {
10-
"user": [
11-
User("Jackie Stewart",
12-
"+4478322678",
13-
14-
],
13+
"user": [User("Jackie Stewart", "+4478322678", "[email protected]")],
1514
"experience": [
16-
Experience("Software Developer",
17-
"A Cool Company",
18-
"October 2022",
19-
"Present",
20-
"Writing Python Code",
21-
"example-logo.png"),
22-
Experience("Intern",
23-
"A Nice Company",
24-
"October 2021",
25-
"December 2021",
26-
"Writing Scripts",
27-
"example-logo.png")
15+
Experience(
16+
"Software Developer",
17+
"A Cool Company",
18+
"October 2022",
19+
"Present",
20+
"Writing Python Code",
21+
"example-logo.png",
22+
),
23+
Experience(
24+
"Intern",
25+
"A Nice Company",
26+
"October 2021",
27+
"December 2021",
28+
"Writing Scripts",
29+
"example-logo.png",
30+
),
2831
],
2932
"education": [
30-
Education("Computer Science",
31-
"University of Tech",
32-
"September 2019",
33-
"July 2022",
34-
"80%",
35-
"example-logo.png")
33+
Education(
34+
"Computer Science",
35+
"University of Tech",
36+
"September 2019",
37+
"July 2022",
38+
"80%",
39+
"example-logo.png",
40+
)
3641
],
37-
"skill": [
38-
Skill("Python",
39-
"1-2 Years",
40-
"example-logo.png")
41-
]
42+
"skill": [Skill("Python", "1-2 Years", "example-logo.png")],
4243
}
4344

4445

45-
@app.route('/test')
46+
@app.route("/test")
4647
def hello_world():
47-
'''
48+
"""
4849
Returns a JSON test message
49-
'''
50+
"""
5051
return jsonify({"message": "Hello, World!"})
5152

52-
@app.route('/resume/user', methods=['GET', 'POST', 'PUT'])
53+
54+
@app.route("/resume/user", methods=["GET", "POST", "PUT"])
5355
def user():
54-
'''
56+
"""
5557
Handles User information
56-
'''
58+
"""
59+
5760
# defining sub function to reduce number of returns
5861
def get_users():
59-
return jsonify([user.__dict__ for user in data['user']]), 200
62+
return jsonify([user.__dict__ for user in data["user"]]), 200
6063

6164
def add_user(body):
6265
# retrieve user's information.
63-
name = body['name']
64-
phone_number = body['phone_number']
65-
email = body['email_address']
66+
name = body["name"]
67+
phone_number = body["phone_number"]
68+
email = body["email_address"]
6669
# store the new user information.
6770
if not check_phone_number(phone_number):
6871
return jsonify({"error": "Incorrect phone number !"}), 400
6972
new_user = User(name, phone_number, email)
70-
data['user'].append(new_user)
73+
data["user"].append(new_user)
7174
return jsonify(new_user.__dict__), 201
7275

7376
# edit the user information.
7477
def edit_user(body):
75-
name = body['name']
76-
phone_number = body['phone_number']
77-
email = body['email_address']
78-
for i, user_ in enumerate(data['user']):
78+
name = body["name"]
79+
phone_number = body["phone_number"]
80+
email = body["email_address"]
81+
for i, user_ in enumerate(data["user"]):
7982
if user_.email_address == email:
8083
if not check_phone_number(phone_number):
8184
return jsonify({"error": "Incorrect phone number !"}), 400
82-
data['user'][i] = User(name, phone_number, email)
83-
return jsonify(data['user'][i].__dict__), 200
85+
data["user"][i] = User(name, phone_number, email)
86+
return jsonify(data["user"][i].__dict__), 200
8487
return jsonify({"error": "User not found !"}), 404
8588

86-
if request.method == 'GET':
89+
if request.method == "GET":
8790
return get_users()
88-
if request.method == 'POST':
91+
if request.method == "POST":
8992
body = request.get_json()
9093
return add_user(body)
91-
if request.method == 'PUT':
94+
if request.method == "PUT":
9295
body = request.get_json()
9396
return edit_user(body)
9497
return jsonify({"error": "Unsupported request method !"}), 405
9598

96-
@app.route('/resume/experience', methods=['GET', 'POST'])
99+
100+
@app.route("/resume/experience", methods=["GET", "POST"])
97101
def experience():
98-
'''
102+
"""
99103
Handle experience requests
100-
'''
101-
if request.method == 'GET':
102-
return jsonify({"experience": [exp.__dict__ for exp in data["experience"]]})
104+
"""
103105

104-
if request.method == 'POST':
106+
if request.method == "GET":
107+
return jsonify(
108+
{"experience": [exp.__dict__ for exp in data["experience"]]})
109+
110+
if request.method == "POST":
105111
new_experience = request.json
106112
experience_instance = Experience(
107113
new_experience["title"],
108114
new_experience["company"],
109115
new_experience["start_date"],
110116
new_experience["end_date"],
111117
new_experience["description"],
112-
new_experience["logo"]
118+
new_experience["logo"],
113119
)
114120
data["experience"].append(experience_instance)
115121
return jsonify({"id": len(data["experience"]) - 1})
116122

117123
return jsonify({})
118124

119-
@app.route('/resume/education', methods=['GET', 'POST'])
125+
126+
@app.route("/resume/education", methods=["GET", "POST"])
120127
def education():
121-
'''
128+
"""
122129
Handles education requests
123-
'''
124-
if request.method == 'GET':
125-
return jsonify({"education": [edu.__dict__ for edu in data["education"]]})
130+
"""
131+
if request.method == "GET":
132+
return jsonify(
133+
{"education": [edu.__dict__ for edu in data["education"]]})
126134

127-
if request.method == 'POST':
135+
if request.method == "POST":
128136
new_education = request.json
129137
education_instance = Education(
130138
new_education["course"],
131139
new_education["school"],
132140
new_education["start_date"],
133141
new_education["end_date"],
134142
new_education["grade"],
135-
new_education["logo"]
143+
new_education["logo"],
136144
)
137145
data["education"].append(education_instance)
138146
return jsonify({"id": len(data["education"]) - 1})
139147

140148
return jsonify({})
141149

142150

143-
@app.route('/resume/skill', methods=['GET', 'POST'])
151+
@app.route("/resume/skill", methods=["GET", "POST"])
144152
def skill():
145-
'''
153+
"""
146154
Handles Skill requests
147-
'''
148-
if request.method == 'GET':
155+
"""
156+
157+
if request.method == "GET":
149158
return jsonify({"skills": [skill.__dict__ for skill in data["skill"]]})
150159

151-
if request.method == 'POST':
160+
if request.method == "POST":
152161
new_skill = request.json
153-
skill_instance = Skill(new_skill["name"], new_skill["proficiency"], new_skill["logo"])
162+
skill_instance = Skill(
163+
new_skill["name"], new_skill["proficiency"], new_skill["logo"]
164+
)
154165
data["skill"].append(skill_instance)
155166
return jsonify({"id": len(data["skill"]) - 1})
156167

157168
return jsonify({})
158169

159-
@app.route('/resume/spellcheck', methods=['POST'])
170+
171+
@app.route("/resume/spellcheck", methods=["POST"])
160172
def spellcheck():
161-
'''
173+
"""
162174
Corrects the spelling of a text
163-
'''
175+
"""
164176
body = request.get_json()
165177
try:
166-
text = body['text']
178+
text = body["text"]
167179
corrected_text = correct_spelling(text)
168180

169181
return jsonify({"before": text, "after": corrected_text}), 200
170182
except KeyError:
171183
return jsonify({"error": "Missing text parameter"}), 400
184+
185+
186+
@app.route("/suggestion", methods=["POST"])
187+
def get_description_suggestion():
188+
"""
189+
Handles suggestion requests
190+
"""
191+
description = request.json.get("description")
192+
description_type = request.json.get("type")
193+
if not description or not description_type:
194+
return jsonify({"error": "Description and type are required"}), 400
195+
suggestion = get_suggestion(description, description_type)
196+
return jsonify({"suggestion": suggestion})

requirements.txt

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
flask
22
pytest
33
pylint
4-
pyspellchecker
4+
google-generativeai
5+
python-dotenv
6+
pyspellchecker
7+

test_pytest.py

+38
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
'''
22
Tests in Pytest
33
'''
4+
from unittest.mock import patch
45
import pytest
56
from app import app
67

8+
9+
710
def test_client():
811
'''
912
Makes a request and checks the message received is the same
@@ -136,3 +139,38 @@ def test_correct_spelling(text, expected):
136139
response = app.test_client().post('/resume/spellcheck', json={'text': text})
137140
assert response.status_code == 200
138141
assert response.json['after'] == expected
142+
143+
# testcases for ai suggested imrpvoed descriptions
144+
@patch('app.get_suggestion')
145+
def test_get_description_suggestion(mock_get_suggestion):
146+
'''
147+
Test the /suggestion route with valid inputs
148+
'''
149+
mock_get_suggestion.return_value = "Improved description"
150+
151+
response = app.test_client().post('/suggestion', json={
152+
'description': 'This is a sample description.',
153+
'type': 'experience'
154+
})
155+
156+
assert response.status_code == 200
157+
assert response.json['suggestion'] == 'Improved description'
158+
159+
160+
def test_get_description_suggestion_missing_fields():
161+
'''
162+
Test the /suggestion route with missing fields
163+
'''
164+
# Missing 'type'
165+
response = app.test_client().post('/suggestion', json={
166+
'description': 'This is a sample description.'
167+
})
168+
assert response.status_code == 400
169+
assert response.json['error'] == 'Description and type are required'
170+
171+
# Missing 'description'
172+
response = app.test_client().post('/suggestion', json={
173+
'type': 'experience'
174+
})
175+
assert response.status_code == 400
176+
assert response.json['error'] == 'Description and type are required'

utils.py

+38-6
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,51 @@
1-
""" Util functions for the Flask App."""
1+
"""
2+
Utility Methods File
3+
"""
4+
5+
import os
26
import re
37
from spellchecker import SpellChecker
8+
import google.generativeai as genai
9+
from dotenv import load_dotenv
10+
11+
12+
load_dotenv()
13+
14+
15+
GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY")
16+
genai.configure(api_key=GOOGLE_API_KEY)
17+
18+
19+
def get_suggestion(description, description_type):
20+
"""
21+
give suggestions for description section using gemini (free alternative to openai's chatgpt api)
22+
"""
23+
prompt = ""
24+
if description_type == "education":
25+
prompt = f"Improve the following education \
26+
experience description for resume: {description}"
27+
elif description_type == "experience":
28+
prompt = f"Improve the following professional \
29+
experience description for resume: {description}"
30+
31+
model = genai.GenerativeModel("gemini-pro")
32+
response = model.generate_content(prompt)
33+
return response.text
34+
435

536
def check_phone_number(phone_number):
6-
""" Checks if the phone number is valid and follows
7-
the international country code
37+
"""Checks if the phone number is valid and follows
38+
the international country code
839
"""
9-
regex = re.compile(r'^\+\d{1,3}\d{1,14}$')
40+
regex = re.compile(r"^\+\d{1,3}\d{1,14}$")
1041
return bool(regex.match(phone_number))
1142

43+
1244
def correct_spelling(text: str):
13-
""" Corrects the spelling of a text"""
45+
"""Corrects the spelling of a text"""
1446

1547
spell_checker = SpellChecker()
16-
word_pattern = r'\w+|[^\w\s]'
48+
word_pattern = r"\w+|[^\w\s]"
1749

1850
misspelled = spell_checker.unknown(re.findall(word_pattern, text))
1951
corrected_text = text

0 commit comments

Comments
 (0)