diff --git a/static/script.js b/static/script.js index aebc922..2bfc262 100644 --- a/static/script.js +++ b/static/script.js @@ -350,9 +350,10 @@ if (isIndexPage) { } function syncSkillsHiddenInput() { - // Keep the hidden in sync for form serialisation - // The API expects a comma-separated string, so join the array that way - skillsHidden.value = selectedSkills.join(", "); + // Keep the hidden in sync for form serialisation. + // JSON.stringify preserves skill names that contain commas (e.g. "HTML, CSS") + // so the backend can reconstruct the exact array without mis-splitting. + skillsHidden.value = JSON.stringify(selectedSkills); } updateQuickPickState(); diff --git a/utils/recommender.py b/utils/recommender.py index 308c14f..d8cdf84 100644 --- a/utils/recommender.py +++ b/utils/recommender.py @@ -32,18 +32,32 @@ def parse_skills(skills_string): """ - Convert a raw comma-separated skills string into - a normalized lowercase list. + Convert a skills string into a normalized lowercase list. - Example: - "JS, HTML5, CSS3" -> ["javascript", "html", "css"] - """ + Accepts two formats: + 1. JSON array (preferred): '["HTML, CSS", "JavaScript"]' + Handles skill names that contain commas without mis-splitting. + 2. Comma-separated string (legacy fallback): "HTML, CSS, JavaScript" - raw_skills = [ - s.strip().lower() - for s in skills_string.split(",") - if s.strip() - ] + Example: + '["JS", "HTML5", "CSS3"]' -> ["javascript", "html", "css"] + """ + import json + + try: + # Preferred path: frontend sends a JSON-serialized array + parsed = json.loads(skills_string) + if isinstance(parsed, list): + raw_skills = [s.strip().lower() for s in parsed if isinstance(s, str) and s.strip()] + else: + raise ValueError("Parsed JSON is not a list") + except (json.JSONDecodeError, ValueError, TypeError): + # Fallback: handle plain comma-separated strings + raw_skills = [ + s.strip().lower() + for s in skills_string.split(",") + if s.strip() + ] normalized_skills = [ SKILL_ALIASES.get(skill, skill)