Skip to content

Commit

Permalink
feat: Support IPIP-300 👽
Browse files Browse the repository at this point in the history
  • Loading branch information
edersoncorbari committed Nov 13, 2022
1 parent 143b189 commit 84af7ec
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 33 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ Notes:

* Dr John A. Johnson
* Dhiru Kholia
* Chris Hunt

### License 🙋

Expand Down
33 changes: 17 additions & 16 deletions ipipneo/ipipneo.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,14 @@
__version__ = "1.0.0"
__status__ = "production"

import copy
import uuid

from ipipneo.facet import Facet
from ipipneo.model import QuestionNumber
from ipipneo.norm import Norm
from ipipneo.utility import (
apply_reverse_scored_120,
organize_list_json,
raise_if_age_is_invalid,
raise_if_sex_is_invalid,
)
from ipipneo.utility import (apply_reverse_scored_120, organize_list_json,
raise_if_age_is_invalid, raise_if_sex_is_invalid)


class IpipNeo(Facet):
Expand Down Expand Up @@ -96,36 +93,40 @@ def evaluator(self, sex: str, age: int, score: list) -> dict:
},
}

def compute(self, sex: str, age: int, answers: dict, selects: bool = False) -> dict:
def compute(self, sex: str, age: int, answers: dict, compare: bool = False) -> dict:
"""
Compute the answers and generate the data with the results.
Args:
- sex: Gender of the individual (M or F).
- age: The age of the individual.
- answers: Standardized dictionary with answers.
- compare: If true, it shows the user's answers and reverse score.
"""
raise_if_sex_is_invalid(sex=sex)
raise_if_age_is_invalid(age=age)
assert isinstance(answers, dict), "answers 1 must be a dict"
assert isinstance(answers, dict), "answers must be a dict"

reverse = apply_reverse_scored_120(answers=answers)
assert isinstance(reverse, dict), "answers 2 must be a dict"
original = copy.deepcopy(answers)
assert isinstance(original, dict), "original must be a dict"

score = self.score(answers=organize_list_json(answers=reverse))
reversed = apply_reverse_scored_120(answers=answers)
assert isinstance(reversed, dict), "reversed must be a dict"

score = self.score(answers=organize_list_json(answers=reversed))
assert isinstance(score, list), "score must be a list"

result = self.evaluator(sex=sex, age=age, score=score)
assert isinstance(result, dict), "result 1 must be a dict"

if selects:
result["person"]["result"]["answers"] = {
"original": [x.get("id_select", 0) for x in answers.get("answers", [])],
"reverse": [x.get("id_select", 0) for x in reverse.get("answers", [])],
if compare:
result["person"]["result"]["compare"] = {
"user_answers_original": original.get("answers", []),
"user_answers_reversed": reversed.get("answers", []),
}
assert isinstance(result, dict), "result 2 must be a dict"

return result
return result or {}

def compute2(self, sex: str, age: int, answers: list) -> dict:
"""
Expand Down
1 change: 0 additions & 1 deletion ipipneo/utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,6 @@ def big5_ocean_is_valid(label: str) -> bool or BaseException:
return True
if label == "N":
return True

raise BaseException("The Big-Five label is invalid!")


Expand Down
46 changes: 36 additions & 10 deletions quiz.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,24 @@ def question_translate() -> list:
return ["0. IPIP-NEO Original", "1. EN-US", "2. PT-BR", "3. ES-ES"]


def quiz(sex: str, age: int, shuffle: str, lang: int) -> None:
def quiz(inventory: int, sex: str, age: int, shuffle: str, lang: int) -> None:
"""
Inventory of questions.
Args:
- inventory: Inventory model 120 or 300.
- sex: The sex of the person.
- age: The age of the person.
- shuffle: Shuffle the questions.
- lang: Then number of language.
"""
questions, answers = (get_questions(lang=lang), [])
answers = questions = []
if inventory == 120:
questions = get_questions(lang=lang)
elif inventory == 300:
pass
else:
raise BaseException(f"Invalid inventory model: {inventory}")

print("\n *** Big Five IPIP-NEO Personality Test ***\n")

Expand All @@ -95,7 +102,7 @@ def quiz(sex: str, age: int, shuffle: str, lang: int) -> None:

answers.append({"id_question": q.get("id"), "id_select": option})

result = IpipNeo(question=120).compute(
result = IpipNeo(question=inventory).compute(
sex=sex, age=age, answers={"answers": answers}
)

Expand All @@ -104,24 +111,37 @@ def quiz(sex: str, age: int, shuffle: str, lang: int) -> None:

def main() -> None:
"""Initialize the test."""
print("\n *** Welcome to Big Five IPIP-NEO Personality Test ***\n")
print(
"The following test contains 120 questions which is estimated to take you about 10 minutes to complete!"
)
print("\n *** Welcome to Big Five IPIP-NEO Personality Test ***")

go = str(input("\nDo you wish to continue? Yes / No: "))
if go.upper() == "NO" or go.upper() == "N":
sys.exit(0)

inventory = int(input("\nWhat inventory model will you make? 120 / 300: "))
assert isinstance(inventory, int), "The (inventory) field must be an integer!"
if inventory != 120 and inventory != 300:
print(f"Invalid inventory model: {inventory}!")
sys.exit(0)

print("\n====================================================================")
if inventory == 120:
print(
"The following test contains 120 questions which is estimated to take you about 15 minutes to complete!"
)
elif inventory == 300:
print(
"The following test contains 300 questions which is estimated to take you about 35 minutes to complete!"
)

sex = str(input("\nWhat is your sex Male or Female? M / F: "))
assert (
sex.upper() == "M" or sex.upper() == "F"
), "The (sex) field must contain (M or F)!"

age = int(input("\nWhat is your age? "))
assert isinstance(age, int), "The (age) field must be an integer!"
if not (10 <= age <= 120):
print(f"The age ({age}) must be between 10 and 120!")
if not (18 <= age <= 100):
print(f"The age ({age}) must be between 18 and 100!")
sys.exit(0)

shuffle = str(input("\nDo you want to shuffle the questions? Yes / No: "))
Expand All @@ -137,7 +157,13 @@ def main() -> None:
)
assert isinstance(age, int), "The (lang) field must be an integer!"

quiz(sex=sex.upper(), age=int(age), shuffle=shuffle, lang=int(lang))
quiz(
inventory=int(inventory),
sex=sex.upper(),
age=int(age),
shuffle=shuffle,
lang=int(lang),
)


if __name__ == "__main__":
Expand Down
4 changes: 2 additions & 2 deletions run-test
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ run_command ()
# Run test suite in all supported versions of Python.
#-----------------------------------------------------------------------
run_command "python3 -B -m unittest $*"
# coverage run --source . -m unittest discover && coverage report
# coverage run --source . -m unittest discover && coverage html
# run_command coverage run --source . -m unittest discover && coverage report
# run_command coverage run --source . -m unittest discover && coverage html
echo "" >&2
echo "All commands successful." >&2
2 changes: 2 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
"Personality",
"Assessment",
"Psychometrics",
"Personality-Insights",
"People-Analytics",
"Python",
],
classifiers=[
Expand Down
132 changes: 129 additions & 3 deletions test/test_ipipneo.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ def load_mock_answers() -> dict:


class TestIpipNeo(unittest.TestCase):

maxDiff = None

def test_invalid_params(self) -> None:
with self.assertRaises(BaseException):
IpipNeo(question="")
Expand All @@ -35,10 +38,14 @@ def test_compute(self) -> None:
with self.assertRaises(TypeError):
big5.compute("")

big5 = None

#############################################
# 1. Test with 40 year old man.
#############################################
result = big5.compute(sex="M", age=40, answers=load_mock_answers())
result = IpipNeo(question=120).compute(
sex="M", age=40, answers=load_mock_answers()
)

self.assertTrue(len(result.get("id")))
self.assertTrue(len(result.get("theory")))
Expand All @@ -63,9 +70,11 @@ def test_compute(self) -> None:
self.assertEqual(personalities[4]["Neuroticism"].get("N"), 58.6632925696303)

#############################################
# 1. Test with 30 year old woman.
# 2. Test with 30 year old woman.
#############################################
result = big5.compute(sex="F", age=30, answers=load_mock_answers())
result = IpipNeo(question=120).compute(
sex="F", age=30, answers=load_mock_answers()
)

self.assertTrue(len(result.get("id")))
self.assertTrue(len(result.get("theory")))
Expand All @@ -88,3 +97,120 @@ def test_compute(self) -> None:
self.assertEqual(personalities[2]["Extraversion"].get("E"), 29.240137885780882)
self.assertEqual(personalities[3]["Agreeableness"].get("A"), 1)
self.assertEqual(personalities[4]["Neuroticism"].get("N"), 47.49034742252866)

#############################################
# 3. Test with 65 year old woman.
#############################################
result = IpipNeo(question=120).compute(
sex="F", age=65, answers=load_mock_answers(), compare=True
)

# print(result)
self.assertIn("id", result)
self.assertIn("theory", result)
self.assertIn("model", result)
self.assertIn("question", result)
self.assertIn("person", result)

self.assertIn("sex", result.get("person"))
self.assertIn("age", result.get("person"))

self.assertTrue(len(result.get("id")))
self.assertTrue(len(result.get("theory")))
self.assertTrue(len(result.get("model")))

self.assertEqual(result.get("question"), 120)
self.assertEqual(result.get("person").get("sex"), "F")
self.assertEqual(result.get("person").get("age"), 65)

self.assertTrue(len(result.get("person").get("result")))
self.assertTrue(len(result.get("person").get("result").get("personalities")))
self.assertTrue(len(result.get("person").get("result").get("compare")))

personalities = result.get("person").get("result").get("personalities")
self.assertEqual(len(personalities), 5)

compare = result.get("person").get("result").get("compare")
self.assertIn("user_answers_original", compare)
self.assertIn("user_answers_reversed", compare)

self.assertEqual(personalities[0]["Openness"].get("O"), 24.29091080263288)
self.assertEqual(personalities[1]["Conscientiousness"].get("C"), 1)
self.assertEqual(personalities[2]["Extraversion"].get("E"), 32.92660962191414)
self.assertEqual(personalities[3]["Agreeableness"].get("A"), 1)
self.assertEqual(personalities[4]["Neuroticism"].get("N"), 67.59105722337824)

original = (
result.get("person")
.get("result")
.get("compare")
.get("user_answers_original")
)
assert isinstance(original, list), "original must be a list"
self.assertEqual(len(original), 120)

reversed = (
result.get("person")
.get("result")
.get("compare")
.get("user_answers_reversed")
)
assert isinstance(reversed, list), "reversed must be a list"
self.assertEqual(len(reversed), 120)

a = [x.get("id_select") for x in original]
assert isinstance(original, list), "reversed must be a list"

b = [x.get("id_select") for x in reversed]
assert isinstance(reversed, list), "reversed must be a list"

self.assertNotEqual(a, b)

self.assertEqual(a[0], b[0])
self.assertEqual(a[1], b[1])
self.assertEqual(a[2], b[2])
self.assertEqual(a[3], b[3])
self.assertEqual(a[4], b[4])
self.assertEqual(a[5], b[5])
self.assertEqual(a[6], b[6])
self.assertEqual(a[7], b[7])

self.assertNotEqual(a[8], b[8])
self.assertEqual(a[8], 5)
self.assertEqual(b[8], 1)

self.assertNotEqual(a[18], b[18])
self.assertEqual(a[18], 2)
self.assertEqual(b[18], 4)

self.assertNotEqual(a[23], b[23])
self.assertEqual(a[23], 2)
self.assertEqual(b[23], 4)

self.assertNotEqual(a[29], b[29])
self.assertEqual(a[29], 4)
self.assertEqual(b[29], 2)

self.assertNotEqual(a[38], b[38])
self.assertEqual(a[38], 5)
self.assertEqual(b[38], 1)

self.assertNotEqual(a[39], b[39])
self.assertEqual(a[39], 5)
self.assertEqual(b[39], 1)

self.assertNotEqual(a[47], b[47])
self.assertEqual(a[47], 4)
self.assertEqual(b[47], 2)

self.assertNotEqual(a[48], b[48])
self.assertEqual(a[48], 5)
self.assertEqual(b[48], 1)

self.assertNotEqual(a[50], b[50])
self.assertEqual(a[50], 2)
self.assertEqual(b[50], 4)

self.assertNotEqual(a[66], b[66])
self.assertEqual(a[66], 2)
self.assertEqual(b[66], 4)
19 changes: 18 additions & 1 deletion test/test_utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
from ipipneo.utility import (answers_is_valid, apply_reverse_scored_120,
big5_ocean_is_valid, big_five_target,
create_big5_dict, organize_list_json,
raise_if_age_is_invalid, raise_if_sex_is_invalid)
raise_if_age_is_invalid, raise_if_sex_is_invalid,
reverse_scored)


def load_mock_answers(idx: int) -> dict:
Expand Down Expand Up @@ -598,3 +599,19 @@ def test_apply_reverse_scored_120(self) -> None:
assert isinstance(b.get("answers", []), list), "b must be a list"

self.assertNotEqual(a, b)

def test_reverse_scored(self) -> None:
with self.assertRaises(TypeError) as e:
reverse_scored(a=1)

with self.assertRaises(BaseException) as e:
reverse_scored(select=900)
self.assertEqual(
str(e.exception), "Something wrong in the selection option: 900"
)

self.assertEqual(reverse_scored(select=1), 5)
self.assertEqual(reverse_scored(select=2), 4)
self.assertEqual(reverse_scored(select=3), 3)
self.assertEqual(reverse_scored(select=4), 2)
self.assertEqual(reverse_scored(select=5), 1)

0 comments on commit 84af7ec

Please sign in to comment.