Skip to content

Commit 8fe4795

Browse files
committed
feat: added close match suggestions for parameter values
1 parent 50f7ed0 commit 8fe4795

21 files changed

+178
-77
lines changed

Makefile

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ help: ## Display this help message with available make commands.
2020
@egrep -h '\s##\s' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
2121

2222
hello: ## Display a welcome message for contributors.
23-
@echo "Your face show, your shoe shine. Thank you for contributing to Fakernaija. E go better for you!"
23+
@echo "Hey, $(USER)! Your face show, your shoe shine. Thank you for contributing to Fakernaija. E go better for you!"
2424

2525
venv: ## Create a virtual environment.
2626
$(PYTHON) -m venv $(VENV_DIR)
@@ -49,15 +49,10 @@ check: ## Run code quality checks with Tox and Pre-commit
4949

5050
clean: ## Clean up all generated files and directories.
5151
@echo "Cleaning up the project..."
52-
@rm -rf $(VENV_DIR)
53-
@rm -rf .cache
54-
@rm -rf htmlcov coverage.xml .coverage
55-
@rm -rf .tox
56-
@rm -rf .mypy_cache
57-
@rm -rf .ruff_cache
58-
@rm -rf *.egg-info
59-
@rm -rf dist
52+
@rm -rf $(VENV_DIR) .cache htmlcov coverage.xml .coverage
53+
@rm -rf .tox .mypy_cache .ruff_cache *.egg-info dist build
6054
@find . -name "*.pyc" -delete
6155
@find . -type d -name "__pycache__" -exec rm -rf {} +
56+
@find . -type d -name "*.egg-info" -exec rm -rf {} +
6257
@find . -type d -name "build" -exec rm -rf {} +
6358
@echo "Cleanup completed."

docs/source/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ You can also generate complete data objects for more complex integrations:
107107
{'name': 'Bachelor of Science', 'degree_type': 'undergraduate', 'abbr': 'B.Sc.'}
108108
109109
>>> print(naija.school())
110-
{'name': 'Lagos State University', 'acronym': 'LASU', 'state': 'Lagos', 'type': 'university', 'ownership': 'State'}
110+
{'name': 'Lagos State University', 'acronym': 'LASU', 'state': 'Lagos', 'type': 'University', 'ownership': 'State'}
111111
112112
Command Line Usage
113113
------------------

fakernaija/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
This package provides utilities to generate random data with a Nigerian context.
44
"""
55

6-
__version__ = "1.0.6"
6+
__version__ = "1.0.0"
77

88
from .naija import Naija
99

fakernaija/commands/__init__.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,23 +31,23 @@
3131
"degree",
3232
"degree_abbr",
3333
"degree_name",
34+
"department_name",
3435
# Email command
3536
"email",
3637
# Faculty commands
3738
"faculty",
3839
"faculty_name",
39-
"department_name",
40-
# License plate command
41-
"license_plate",
42-
# Marital status command
43-
"marital_status",
4440
# Name commands
4541
"first_name",
4642
"full_name",
4743
"last_name",
48-
"prefix",
44+
# License plate command
45+
"license_plate",
46+
# Marital status command
47+
"marital_status",
4948
# PhoneNumber command
5049
"phone_number",
50+
"prefix",
5151
# Religion command
5252
"religion",
5353
# School commands

fakernaija/mixins/license_plate.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,4 @@ def license_plate(self, state: str | None = None) -> str:
4646
>>> print(f"Random license plate number from a specific state: {license_plate}")
4747
Random license plate number from a specific state: EZA-352CC
4848
"""
49-
try:
50-
return self.license_plate_provider.generate_license_plate(state=state)
51-
except ValueError as e:
52-
msg = f"Invalid state name: {state}. {e!s}"
53-
raise ValueError(msg) from e
49+
return self.license_plate_provider.generate_license_plate(state=state)

fakernaija/providers/degree.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""This module provides a DegreeProvider class for accessing information about degrees awarded in Nigerian schools from a JSON file."""
22

3+
import difflib
34
from pathlib import Path
45

56
from fakernaija.utils import load_json, normalize_input
@@ -38,7 +39,15 @@ def validate_degree_type(self, degree_type: str | None) -> str | None:
3839
"""
3940
degree_type = normalize_input(degree_type)
4041
if degree_type and degree_type not in self.valid_degree_types:
41-
msg = f"Invalid degree_type. Must be one of {self.valid_degree_types}."
42+
# Find close matches to the input degree type
43+
suggestions = difflib.get_close_matches(
44+
degree_type, self.valid_degree_types, n=3, cutoff=0.6
45+
)
46+
msg = (
47+
f"Invalid degree_type: '{degree_type}'. Did you mean: {', '.join(suggestions)}?"
48+
if suggestions
49+
else f"Invalid degree_type: '{degree_type}'. Valid types are: {', '.join(self.valid_degree_types)}."
50+
)
4251
raise ValueError(msg)
4352
return degree_type
4453

fakernaija/providers/email.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""This module provides an EmailProvider class for generating email addresses with Nigerian name combinations."""
22

3+
import difflib
34
import random
45
import re
56

@@ -74,11 +75,34 @@ def generate_email(
7475
Raises:
7576
ValueError: If the domain is invalid or if no matching data is found for the given tribe or gender.
7677
"""
77-
# Normalize the inputs to lowercase
78+
# Normalize the inputs
7879
tribe = normalize_input(tribe)
7980
gender = normalize_input(gender)
8081
domain = normalize_input(domain)
8182

83+
if tribe and tribe not in self.name_provider.tribes:
84+
# Check if the tribe exists in the list and suggest close matches
85+
suggestions = difflib.get_close_matches(
86+
tribe, self.name_provider.tribes, n=3, cutoff=0.6
87+
)
88+
msg = (
89+
f"Unsupported tribe: '{tribe}'. Did you mean: {', '.join(suggestions)}?"
90+
if suggestions
91+
else f"Unsupported tribe: '{tribe}'. Available tribes are: {', '.join(self.name_provider.tribes)}."
92+
)
93+
raise ValueError(msg)
94+
95+
if gender and gender not in self.name_provider.genders:
96+
suggestions = difflib.get_close_matches(
97+
gender, self.name_provider.genders, n=3, cutoff=0.6
98+
)
99+
msg = (
100+
f"Invalid gender: '{gender}'. Did you mean: {', '.join(suggestions)}?"
101+
if suggestions
102+
else f"Invalid gender: '{gender}'. Valid genders are: {', '.join(self.name_provider.genders)}."
103+
)
104+
raise ValueError(msg)
105+
82106
if domain and not self.validate_domain(domain):
83107
msg = f"Invalid domain: {domain}"
84108
raise ValueError(msg)

fakernaija/providers/faculty.py

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
Resource link: https://punchng.com/full-list-newly-restructured-programmes-in-nigerian-varsities/
44
"""
55

6+
import difflib
67
from pathlib import Path
78

89
from fakernaija.utils import load_json
@@ -24,14 +25,15 @@ def __init__(self) -> None:
2425
"departments",
2526
],
2627
)
28+
self.faculty_names = [faculty["name"] for faculty in self.faculties_data]
2729

2830
def get_faculty_names(self) -> list[str]:
2931
"""Get a list of all faculty names.
3032
3133
Returns:
3234
list[str]: A list of faculty names.
3335
"""
34-
return [faculty["name"] for faculty in self.faculties_data]
36+
return self.faculty_names
3537

3638
def get_department_names(self, faculty: str | None = None) -> list[str]:
3739
"""Get a list of department names. Optionally filter by faculty.
@@ -41,14 +43,33 @@ def get_department_names(self, faculty: str | None = None) -> list[str]:
4143
4244
Returns:
4345
list[str]: A list of department names.
46+
47+
Raises:
48+
ValueError: If the faculty name is invalid.
4449
"""
45-
departments = []
46-
if faculty:
47-
for fac in self.faculties_data:
48-
if fac["name"].lower() == faculty.lower():
49-
departments.extend(fac["departments"])
50-
break
51-
else:
50+
if faculty and faculty is not None:
51+
# Normalize faculty names for case-insensitive comparison
52+
faculty_dict = {f.lower(): f for f in self.faculty_names}
53+
54+
if faculty.lower() not in faculty_dict:
55+
# Find close matches to the input faculty name
56+
suggestions = difflib.get_close_matches(
57+
faculty, self.faculty_names, n=3, cutoff=0.6
58+
)
59+
msg = (
60+
f"Invalid faculty name: {faculty}. Did you mean: {', '.join(suggestions)}?"
61+
if suggestions
62+
else f"Invalid faculty name: {faculty}. Valid faculties are: {', '.join(self.faculty_names)}"
63+
)
64+
raise ValueError(msg)
65+
66+
faculty_name = faculty_dict[faculty.lower()]
5267
for fac in self.faculties_data:
53-
departments.extend(fac["departments"])
68+
if fac["name"] == faculty_name:
69+
return fac["departments"]
70+
71+
# Return all departments if no faculty is specified
72+
departments = []
73+
for fac in self.faculties_data:
74+
departments.extend(fac["departments"])
5475
return departments

fakernaija/providers/license_plate.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@
77
from fakernaija.providers.state import StateProvider
88
from fakernaija.utils import load_json, normalize_input
99

10-
DIGIT_COUNT = 3
11-
LETTER_COUNT = 2
12-
1310

1411
class LicensePlateProvider:
1512
"""Provider class to generate Nigerian license plates."""
@@ -57,10 +54,11 @@ def generate_license_plate(self, state: str | None = None) -> str:
5754
suggestions = difflib.get_close_matches(
5855
state, self.state_names, n=3, cutoff=0.6
5956
)
60-
if suggestions:
61-
msg = f"Invalid state name: {state}. Did you mean: {', '.join(suggestions)}?"
62-
else:
63-
msg = f"Invalid state name: {state}. Valid states are: {', '.join(self.state_names)}"
57+
msg = (
58+
f"Invalid state name: {state}. Did you mean: {', '.join(suggestions)}?"
59+
if suggestions
60+
else f"Invalid state name: {state}. Valid states are: {', '.join(self.state_names)}"
61+
)
6462
raise ValueError(msg)
6563
# Use the correctly cased state name to access LGA codes
6664
state_name = state_dict[state.lower()]
@@ -70,6 +68,6 @@ def generate_license_plate(self, state: str | None = None) -> str:
7068
all_lgas = [code for codes in self.lga_codes.values() for code in codes]
7169
lga_code = random.choice(all_lgas)
7270

73-
digits = "".join(random.choices("0123456789", k=DIGIT_COUNT))
74-
letters = "".join(random.choices("ABCDEFGHIJKLMNOPQRSTUVWXYZ", k=LETTER_COUNT))
71+
digits = "".join(random.choices("0123456789", k=3))
72+
letters = "".join(random.choices("ABCDEFGHIJKLMNOPQRSTUVWXYZ", k=2))
7573
return f"{lga_code}-{digits}{letters}"

fakernaija/providers/name.py

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""This module provides a NameProvider class for generating Nigerian name combinations."""
22

3+
import difflib
34
import random
45
from pathlib import Path
56

@@ -104,11 +105,23 @@ def generate_first_name(
104105
gender = normalize_input(gender)
105106

106107
if tribe and tribe not in self.tribes:
107-
msg = f"Unsupported tribe: {tribe}. Supported values are: {', '.join(self.tribes)}"
108+
suggestions = difflib.get_close_matches(tribe, self.tribes, n=3, cutoff=0.6)
109+
msg = (
110+
f"Unsupported tribe: {tribe}. Did you mean: {', '.join(suggestions)}?"
111+
if suggestions
112+
else f"Unsupported tribe: {tribe}. Supported values are: {', '.join(self.tribes)}"
113+
)
108114
raise ValueError(msg)
109115

110116
if gender and gender not in self.genders:
111-
msg = f"Unsupported gender: {gender}. Supported values are: {', '.join(self.genders)}"
117+
suggestions = difflib.get_close_matches(
118+
gender, self.genders, n=3, cutoff=0.6
119+
)
120+
msg = (
121+
f"Unsupported gender: {gender}. Did you mean: {', '.join(suggestions)}?"
122+
if suggestions
123+
else f"Unsupported gender: {gender}. Supported values are: {', '.join(self.genders)}"
124+
)
112125
raise ValueError(msg)
113126

114127
first_names = self.get_first_names(tribe, gender)
@@ -132,7 +145,12 @@ def generate_last_name(self, tribe: str | None = None) -> str:
132145
tribe = normalize_input(tribe)
133146

134147
if tribe and tribe not in self.tribes:
135-
msg = f"Unsupported tribe: {tribe}. Supported values are: {', '.join(self.tribes)}"
148+
suggestions = difflib.get_close_matches(tribe, self.tribes, n=3, cutoff=0.6)
149+
msg = (
150+
f"Unsupported tribe: {tribe}. Did you mean: {', '.join(suggestions)}?"
151+
if suggestions
152+
else f"Unsupported tribe: {tribe}. Supported values are: {', '.join(self.tribes)}"
153+
)
136154
raise ValueError(msg)
137155

138156
last_names = self.get_last_names(tribe)
@@ -164,13 +182,25 @@ def generate_full_name(
164182
gender = normalize_input(gender)
165183

166184
if gender and gender not in self.genders:
167-
msg = f"Unsupported gender: {gender}. Supported values are: {', '.join(self.genders)}"
185+
suggestions = difflib.get_close_matches(
186+
gender, self.genders, n=3, cutoff=0.6
187+
)
188+
msg = (
189+
f"Unsupported gender: {gender}. Did you mean: {', '.join(suggestions)}?"
190+
if suggestions
191+
else f"Unsupported gender: {gender}. Supported values are: {', '.join(self.genders)}"
192+
)
168193
raise ValueError(msg)
169194

170195
if tribe is None:
171196
tribe = random.choice(self.tribes)
172197
elif tribe not in self.tribes:
173-
msg = f"Unsupported tribe: {tribe}. Supported values are: {', '.join(self.tribes)}"
198+
suggestions = difflib.get_close_matches(tribe, self.tribes, n=3, cutoff=0.6)
199+
msg = (
200+
f"Unsupported tribe: {tribe}. Did you mean: {', '.join(suggestions)}?"
201+
if suggestions
202+
else f"Unsupported tribe: {tribe}. Supported values are: {', '.join(self.tribes)}"
203+
)
174204
raise ValueError(msg)
175205

176206
first_name = self.generate_first_name(tribe, gender)

0 commit comments

Comments
 (0)