Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 95 additions & 15 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
import re
from dataclasses import asdict
from flask import Flask, jsonify, request
from spellchecker import SpellChecker
from models import Experience, Education, Skill, UserInfo

app = Flask(__name__)
spellchecker = SpellChecker()

data = {
"user_info": UserInfo(
Expand Down Expand Up @@ -46,13 +48,15 @@ def hello_world():
return jsonify({"message": "Hello, World!"})


@app.route('/resume/experience', methods=['GET', 'POST', 'DELETE'])
@app.route('/resume/experience', methods=['GET', 'POST', 'PUT', 'DELETE'])
def experience():
'''
Handle experience requests
'''
response = {}
status = 200
if request.method == 'GET':
experience_list = [
response = [
{
"title": exp.title,
"company": exp.company,
Expand All @@ -63,9 +67,7 @@ def experience():
}
for exp in data['experience']
]
return jsonify(experience_list)

if request.method == 'POST':
elif request.method == 'POST':
new_experience = Experience(
title=request.json['title'],
company=request.json['company'],
Expand All @@ -75,12 +77,41 @@ def experience():
logo=request.json['logo']
)
data['experience'].append(new_experience)
index = len(data['experience']) - 1
return jsonify({"id": index})

if request.method == 'DELETE':
return _delete_experience(request.get_json())
return jsonify({})
response = {"id": len(data['experience']) - 1}
elif request.method == 'PUT':
body = request.get_json()
if not body or 'id' not in body:
response = {"error": "ID is required for update"}
status = 400
else:
try:
item_id = int(body['id'])
except (ValueError, TypeError):
response = {"error": "ID must be an integer"}
status = 400
else:
if item_id < 0 or item_id >= len(data['experience']):
response = {"error": "ID is out of range"}
status = 400
else:
try:
updated_experience = Experience(
title=body['title'],
company=body['company'],
start_date=body['start_date'],
end_date=body['end_date'],
description=body['description'],
logo=body['logo']
)
except KeyError as exc:
response = {"error": f"Missing field: {exc}"}
status = 400
else:
data['experience'][item_id] = updated_experience
response = {"id": item_id}
elif request.method == 'DELETE':
response, status = _delete_experience(request.get_json())
return jsonify(response), status


@app.route('/resume/experience/<int:index>', methods=['GET'])
Expand Down Expand Up @@ -159,15 +190,15 @@ def skill(): # pylint: disable=too-many-return-statements

def _delete_experience(body):
if not body or 'id' not in body:
return jsonify({"error": "ID is required for deletion"}), 400
return {"error": "ID is required for deletion"}, 400
try:
item_id = int(body['id'])
except (ValueError, TypeError):
return jsonify({"error": "ID must be an integer"}), 400
return {"error": "ID must be an integer"}, 400
if item_id < 0 or item_id >= len(data["experience"]):
return jsonify({"error": "ID is out of range"}), 400
return {"error": "ID is out of range"}, 400
data["experience"].pop(item_id)
return jsonify({"deleted": item_id}), 200
return {"deleted": item_id}, 200

def _delete_skill(body):
if not body or 'id' not in body:
Expand Down Expand Up @@ -214,3 +245,52 @@ def user_info():
"data": asdict(data['user_info'])
})
return jsonify({})


def _apply_spelling_correction(word):
correction = spellchecker.correction(word)
if correction is None:
return word
if word.isupper():
return correction.upper()
if word[0].isupper():
return correction.capitalize()
return correction


def _correct_text_spelling(text):
return re.sub(r"[A-Za-z]+", lambda m: _apply_spelling_correction(m.group(0)), text)


def _collect_spelling_results():
results = []

def check_value(value):
if not isinstance(value, str):
return
corrected = _correct_text_spelling(value)
if corrected != value:
results.append({"before": value, "after": corrected})

for exp in data["experience"]:
check_value(exp.title)
check_value(exp.company)
check_value(exp.description)

for edu in data["education"]:
check_value(edu.course)
check_value(edu.school)

for skill_entry in data["skill"]:
check_value(skill_entry.name)
check_value(skill_entry.proficiency)

return results


@app.route('/resume/spellcheck', methods=['GET'])
def spellcheck():
'''
Returns spelling correction suggestions for resume entries
'''
return jsonify(_collect_spelling_results())
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
flask
pytest
pylint
pylint
pyspellchecker
65 changes: 65 additions & 0 deletions test_pytest.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,3 +222,68 @@ def test_user_info_invalid_phone():
response = client.put('/resume/user_info', json=invalid_phone_info)
assert response.status_code == 400
assert "Phone number must include international country code" in response.json["error"]

def test_spellcheck_endpoint():
'''
Check that spelling corrections are returned for resume entries
'''
client = app.test_client()
example_experience = {
"title": "Tester",
"company": "Speling Co",
"start_date": "October 2022",
"end_date": "Present",
"description": "Teh dog",
"logo": "example-logo.png"
}

client.post('/resume/experience', json=example_experience)

response = client.get('/resume/spellcheck')
assert response.status_code == 200
assert any(
item["before"] == "Teh dog" and item["after"] == "The dog"
for item in response.json
)
def test_update_experience():
'''
Update an experience by index using PUT
'''
client = app.test_client()

new_experience = {
"title": "Original Title",
"company": "Original Company",
"start_date": "October 2022",
"end_date": "Present",
"description": "Original description",
"logo": "example-logo.png"
}

post_response = client.post('/resume/experience', json=new_experience)
assert post_response.status_code == 200
item_id = post_response.json['id']

updated_experience = {
"id": item_id,
"title": "Updated Title",
"company": "Updated Company",
"start_date": "October 2022",
"end_date": "Present",
"description": "Updated description",
"logo": "example-logo.png"
}

put_response = client.put('/resume/experience', json=updated_experience)
assert put_response.status_code == 200
assert put_response.json["id"] == item_id

get_response = client.get('/resume/experience')
assert get_response.json[item_id] == {
"title": "Updated Title",
"company": "Updated Company",
"start_date": "October 2022",
"end_date": "Present",
"description": "Updated description",
"logo": "example-logo.png"
}
Loading