Skip to content
Closed
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
76 changes: 76 additions & 0 deletions .idea/workspace.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

177 changes: 146 additions & 31 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,46 +48,110 @@ 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
'''
if request.method == 'GET':
experience_list = [
{
"title": exp.title,
"company": exp.company,
"start_date": exp.start_date,
"end_date": exp.end_date,
"description": exp.description,
"logo": exp.logo
}
for exp in data['experience']
]
return jsonify(experience_list)
# Use a small dispatch map to reduce branching and keep handlers in
# dedicated helper functions. Handlers may return either a Flask
# response object (from jsonify) or a (payload, status) tuple.
handlers = {
'GET': _get_experience,
'POST': _post_experience,
'PUT': _put_experience,
'DELETE': lambda: _delete_experience(request.get_json()),
}

handler = handlers.get(request.method)
if handler is None:
return jsonify({}), 405

result = handler()

# If a helper returned a (payload, status) tuple, unpack it and
# jsonify the payload unless it is already a Flask response.
if isinstance(result, tuple) and len(result) == 2:
payload, status = result
if hasattr(payload, 'get_data'):
return payload, status
return jsonify(payload), status

# Otherwise assume handler returned a Flask response or raw payload.
if hasattr(result, 'get_data'):
return result
return jsonify(result)


def _get_experience():
"""Return list of experiences as payload, status."""
experience_list = [
{
"title": exp.title,
"company": exp.company,
"start_date": exp.start_date,
"end_date": exp.end_date,
"description": exp.description,
"logo": exp.logo,
}
for exp in data['experience']
]
return experience_list, 200

if request.method == 'POST':
req = request.get_json()

required_fields = ["title", "company", "start_date", "end_date", "description", "logo"]
if not req or not isinstance(req, dict) or any(
field not in req for field in required_fields
):
return jsonify({"error": "Missing required fields"}), 400
def _post_experience():
"""Create a new experience from JSON body."""
req = request.get_json()

try:
new_experience = Experience(**req)
except TypeError:
return jsonify({"error": "Invalid format"}), 400
required_fields = [
"title",
"company",
"start_date",
"end_date",
"description",
"logo",
]

data['experience'].append(new_experience)
index = len(data['experience']) - 1
return jsonify({"id": index})
if not req or not isinstance(req, dict) or any(field not in req for field in required_fields):
return {"error": "Missing required fields"}, 400

if request.method == 'DELETE':
return _delete_experience(request.get_json())
return jsonify({})
try:
new_experience = Experience(**req)
except TypeError:
return {"error": "Invalid format"}, 400

data['experience'].append(new_experience)
return {"id": len(data['experience']) - 1}, 200


def _put_experience():
"""Update existing experience identified by id in JSON body."""
body = request.get_json()
if not body or 'id' not in body:
return {"error": "ID is required for update"}, 400

try:
item_id = int(body['id'])
except (ValueError, TypeError):
return {"error": "ID must be an integer"}, 400

if item_id < 0 or item_id >= len(data['experience']):
return {"error": "ID is out of range"}, 400

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:
return {"error": f"Missing field: {exc}"}, 400

data['experience'][item_id] = updated_experience
return {"id": item_id}, 200


@app.route('/resume/experience/<int:index>', methods=['GET'])
Expand Down Expand Up @@ -255,3 +321,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
Loading