personalitygen 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- personalitygen-0.1.0/LICENSE +21 -0
- personalitygen-0.1.0/PKG-INFO +77 -0
- personalitygen-0.1.0/README.md +51 -0
- personalitygen-0.1.0/pyproject.toml +71 -0
- personalitygen-0.1.0/src/personalitygen/__init__.py +30 -0
- personalitygen-0.1.0/src/personalitygen/__init__.pyi +5 -0
- personalitygen-0.1.0/src/personalitygen/constants.py +4 -0
- personalitygen-0.1.0/src/personalitygen/constants.pyi +2 -0
- personalitygen-0.1.0/src/personalitygen/enums.py +17 -0
- personalitygen-0.1.0/src/personalitygen/enums.pyi +11 -0
- personalitygen-0.1.0/src/personalitygen/personality.py +228 -0
- personalitygen-0.1.0/src/personalitygen/personality.pyi +40 -0
- personalitygen-0.1.0/src/personalitygen/py.typed +0 -0
- personalitygen-0.1.0/src/personalitygen/randomness.py +50 -0
- personalitygen-0.1.0/src/personalitygen/randomness.pyi +7 -0
- personalitygen-0.1.0/src/personalitygen/traits.py +345 -0
- personalitygen-0.1.0/src/personalitygen/traits.pyi +60 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 B.T. Franklin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: personalitygen
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Generate and manage simulated human-like personalities based on the Big Five model.
|
|
5
|
+
Keywords: personality,big-five,ocean,simulation
|
|
6
|
+
Author-Email: "B.T. Franklin" <brandon.franklin@gmail.com>
|
|
7
|
+
License: MIT
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
14
|
+
Classifier: Typing :: Typed
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Intended Audience :: Developers
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Classifier: Topic :: Utilities
|
|
20
|
+
Project-URL: Homepage, https://github.com/btfranklin/personalitygen
|
|
21
|
+
Project-URL: Issues, https://github.com/btfranklin/personalitygen/issues
|
|
22
|
+
Project-URL: Changelog, https://github.com/btfranklin/personalitygen/releases
|
|
23
|
+
Project-URL: Repository, https://github.com/btfranklin/personalitygen.git
|
|
24
|
+
Requires-Python: >=3.11
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# personalitygen
|
|
28
|
+
|
|
29
|
+

|
|
30
|
+
|
|
31
|
+
`personalitygen` generates and manages simulated human-like personalities based on the Big Five (OCEAN) model. It is designed for
|
|
32
|
+
simulation, storytelling, and testing scenarios where you want plausible, varied personality profiles without running surveys.
|
|
33
|
+
|
|
34
|
+
## Intent and scope
|
|
35
|
+
|
|
36
|
+
- Generate full Big Five profiles with sub-trait components and aggregate scores.
|
|
37
|
+
- Bias outputs by life stage using tuned Gaussian distributions (child, young adult, adult).
|
|
38
|
+
- Derive a conflict-resolution style from trait weights, plus mapped concern-for-self/others.
|
|
39
|
+
- Support deterministic generation by accepting a seeded random source.
|
|
40
|
+
- Stay lightweight and dependency-free (pure Python).
|
|
41
|
+
|
|
42
|
+
This package is not a clinical assessment tool and does not implement questionnaires or scoring rubrics.
|
|
43
|
+
|
|
44
|
+
## Model overview
|
|
45
|
+
|
|
46
|
+
- Big Five traits: openness, conscientiousness, extraversion, agreeableness, neuroticism.
|
|
47
|
+
- Each trait is composed of three sub-traits and a weighted aggregate score.
|
|
48
|
+
- Life stage influences distribution means and standard deviations for sampling.
|
|
49
|
+
- Conflict-resolution style is selected from avoiding, obliging, integrating, dominating, or compromising based on trait scores.
|
|
50
|
+
|
|
51
|
+
## Usage
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
from personalitygen import BigFivePersonality, LifeStage
|
|
55
|
+
|
|
56
|
+
personality = BigFivePersonality.random(LifeStage.ADULT)
|
|
57
|
+
print(personality.trait_configuration)
|
|
58
|
+
print(personality.conflict_resolution_configuration)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
If you want deterministic output, pass a seeded random number generator:
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
import random
|
|
65
|
+
from personalitygen import BigFiveTraitConfiguration, LifeStage
|
|
66
|
+
|
|
67
|
+
rng = random.Random(42)
|
|
68
|
+
traits = BigFiveTraitConfiguration.random(LifeStage.YOUNG_ADULT, rng=rng)
|
|
69
|
+
print(traits)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Development
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
pdm install --group dev
|
|
76
|
+
pdm run test
|
|
77
|
+
```
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# personalitygen
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
`personalitygen` generates and manages simulated human-like personalities based on the Big Five (OCEAN) model. It is designed for
|
|
6
|
+
simulation, storytelling, and testing scenarios where you want plausible, varied personality profiles without running surveys.
|
|
7
|
+
|
|
8
|
+
## Intent and scope
|
|
9
|
+
|
|
10
|
+
- Generate full Big Five profiles with sub-trait components and aggregate scores.
|
|
11
|
+
- Bias outputs by life stage using tuned Gaussian distributions (child, young adult, adult).
|
|
12
|
+
- Derive a conflict-resolution style from trait weights, plus mapped concern-for-self/others.
|
|
13
|
+
- Support deterministic generation by accepting a seeded random source.
|
|
14
|
+
- Stay lightweight and dependency-free (pure Python).
|
|
15
|
+
|
|
16
|
+
This package is not a clinical assessment tool and does not implement questionnaires or scoring rubrics.
|
|
17
|
+
|
|
18
|
+
## Model overview
|
|
19
|
+
|
|
20
|
+
- Big Five traits: openness, conscientiousness, extraversion, agreeableness, neuroticism.
|
|
21
|
+
- Each trait is composed of three sub-traits and a weighted aggregate score.
|
|
22
|
+
- Life stage influences distribution means and standard deviations for sampling.
|
|
23
|
+
- Conflict-resolution style is selected from avoiding, obliging, integrating, dominating, or compromising based on trait scores.
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
from personalitygen import BigFivePersonality, LifeStage
|
|
29
|
+
|
|
30
|
+
personality = BigFivePersonality.random(LifeStage.ADULT)
|
|
31
|
+
print(personality.trait_configuration)
|
|
32
|
+
print(personality.conflict_resolution_configuration)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
If you want deterministic output, pass a seeded random number generator:
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
import random
|
|
39
|
+
from personalitygen import BigFiveTraitConfiguration, LifeStage
|
|
40
|
+
|
|
41
|
+
rng = random.Random(42)
|
|
42
|
+
traits = BigFiveTraitConfiguration.random(LifeStage.YOUNG_ADULT, rng=rng)
|
|
43
|
+
print(traits)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Development
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pdm install --group dev
|
|
50
|
+
pdm run test
|
|
51
|
+
```
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "personalitygen"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Generate and manage simulated human-like personalities based on the Big Five model."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.11"
|
|
7
|
+
authors = [
|
|
8
|
+
{ name = "B.T. Franklin", email = "brandon.franklin@gmail.com" },
|
|
9
|
+
]
|
|
10
|
+
keywords = [
|
|
11
|
+
"personality",
|
|
12
|
+
"big-five",
|
|
13
|
+
"ocean",
|
|
14
|
+
"simulation",
|
|
15
|
+
]
|
|
16
|
+
dependencies = []
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
20
|
+
"Programming Language :: Python :: 3.11",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Programming Language :: Python :: 3.13",
|
|
23
|
+
"Programming Language :: Python :: 3.14",
|
|
24
|
+
"Typing :: Typed",
|
|
25
|
+
"License :: OSI Approved :: MIT License",
|
|
26
|
+
"Operating System :: OS Independent",
|
|
27
|
+
"Intended Audience :: Developers",
|
|
28
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
29
|
+
"Topic :: Utilities",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.license]
|
|
33
|
+
text = "MIT"
|
|
34
|
+
|
|
35
|
+
[project.urls]
|
|
36
|
+
Homepage = "https://github.com/btfranklin/personalitygen"
|
|
37
|
+
Issues = "https://github.com/btfranklin/personalitygen/issues"
|
|
38
|
+
Changelog = "https://github.com/btfranklin/personalitygen/releases"
|
|
39
|
+
Repository = "https://github.com/btfranklin/personalitygen.git"
|
|
40
|
+
|
|
41
|
+
[build-system]
|
|
42
|
+
requires = [
|
|
43
|
+
"pdm-backend",
|
|
44
|
+
]
|
|
45
|
+
build-backend = "pdm.backend"
|
|
46
|
+
|
|
47
|
+
[tool.pdm]
|
|
48
|
+
distribution = true
|
|
49
|
+
packages = [
|
|
50
|
+
{ include = "personalitygen", from = "src" },
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
[tool.pdm.build]
|
|
54
|
+
excludes = [
|
|
55
|
+
"tests/**",
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
[tool.pdm.scripts]
|
|
59
|
+
test = "pytest"
|
|
60
|
+
lint = "flake8 src tests"
|
|
61
|
+
|
|
62
|
+
[tool.pytest.ini_options]
|
|
63
|
+
testpaths = [
|
|
64
|
+
"tests",
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
[dependency-groups]
|
|
68
|
+
dev = [
|
|
69
|
+
"pytest>=9.0.2",
|
|
70
|
+
"flake8>=7.3.0",
|
|
71
|
+
]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Public interface for personalitygen."""
|
|
2
|
+
|
|
3
|
+
from personalitygen.enums import LifeStage, PriorityLevel
|
|
4
|
+
from personalitygen.personality import (
|
|
5
|
+
BigFiveConflictResolutionConfiguration,
|
|
6
|
+
BigFiveConflictResolutionStyle,
|
|
7
|
+
BigFivePersonality,
|
|
8
|
+
BigFiveTraitConfiguration,
|
|
9
|
+
)
|
|
10
|
+
from personalitygen.traits import (
|
|
11
|
+
BigFiveAgreeableness,
|
|
12
|
+
BigFiveConscientiousness,
|
|
13
|
+
BigFiveExtraversion,
|
|
14
|
+
BigFiveNeuroticism,
|
|
15
|
+
BigFiveOpenness,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"BigFiveAgreeableness",
|
|
20
|
+
"BigFiveConscientiousness",
|
|
21
|
+
"BigFiveConflictResolutionConfiguration",
|
|
22
|
+
"BigFiveConflictResolutionStyle",
|
|
23
|
+
"BigFiveExtraversion",
|
|
24
|
+
"BigFiveNeuroticism",
|
|
25
|
+
"BigFiveOpenness",
|
|
26
|
+
"BigFivePersonality",
|
|
27
|
+
"BigFiveTraitConfiguration",
|
|
28
|
+
"LifeStage",
|
|
29
|
+
"PriorityLevel",
|
|
30
|
+
]
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
from personalitygen.enums import LifeStage as LifeStage, PriorityLevel as PriorityLevel
|
|
2
|
+
from personalitygen.personality import BigFiveConflictResolutionConfiguration as BigFiveConflictResolutionConfiguration, BigFiveConflictResolutionStyle as BigFiveConflictResolutionStyle, BigFivePersonality as BigFivePersonality, BigFiveTraitConfiguration as BigFiveTraitConfiguration
|
|
3
|
+
from personalitygen.traits import BigFiveAgreeableness as BigFiveAgreeableness, BigFiveConscientiousness as BigFiveConscientiousness, BigFiveExtraversion as BigFiveExtraversion, BigFiveNeuroticism as BigFiveNeuroticism, BigFiveOpenness as BigFiveOpenness
|
|
4
|
+
|
|
5
|
+
__all__ = ['BigFiveAgreeableness', 'BigFiveConscientiousness', 'BigFiveConflictResolutionConfiguration', 'BigFiveConflictResolutionStyle', 'BigFiveExtraversion', 'BigFiveNeuroticism', 'BigFiveOpenness', 'BigFivePersonality', 'BigFiveTraitConfiguration', 'LifeStage', 'PriorityLevel']
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Enums for personalitygen."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from enum import Enum
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class LifeStage(str, Enum):
|
|
9
|
+
CHILD = "child"
|
|
10
|
+
YOUNG_ADULT = "young_adult"
|
|
11
|
+
ADULT = "adult"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class PriorityLevel(str, Enum):
|
|
15
|
+
LOW = "low"
|
|
16
|
+
MODERATE = "moderate"
|
|
17
|
+
HIGH = "high"
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"""Top-level personality configuration models."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import random
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from enum import Enum
|
|
8
|
+
from typing import Self
|
|
9
|
+
|
|
10
|
+
from personalitygen.enums import LifeStage, PriorityLevel
|
|
11
|
+
from personalitygen.randomness import RandomSource
|
|
12
|
+
from personalitygen.traits import (
|
|
13
|
+
BigFiveAgreeableness,
|
|
14
|
+
BigFiveConscientiousness,
|
|
15
|
+
BigFiveExtraversion,
|
|
16
|
+
BigFiveNeuroticism,
|
|
17
|
+
BigFiveOpenness,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _weighted_choice(
|
|
22
|
+
weights: dict["BigFiveConflictResolutionStyle", float],
|
|
23
|
+
*,
|
|
24
|
+
rng: RandomSource | None = None,
|
|
25
|
+
) -> "BigFiveConflictResolutionStyle":
|
|
26
|
+
if not weights:
|
|
27
|
+
raise ValueError("weights must be non-empty")
|
|
28
|
+
if any(weight < 0.0 for weight in weights.values()):
|
|
29
|
+
raise ValueError("weights must be non-negative")
|
|
30
|
+
source = rng if rng is not None else random
|
|
31
|
+
total = sum(weights.values())
|
|
32
|
+
if total <= 0.0:
|
|
33
|
+
weights = {style: 1.0 for style in weights}
|
|
34
|
+
total = float(len(weights))
|
|
35
|
+
|
|
36
|
+
threshold = source.uniform(0.0, total)
|
|
37
|
+
for style, weight in weights.items():
|
|
38
|
+
threshold -= weight
|
|
39
|
+
if threshold <= 0.0:
|
|
40
|
+
return style
|
|
41
|
+
return next(iter(weights))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class BigFiveConflictResolutionStyle(str, Enum):
|
|
45
|
+
# Concern for self: low. Concern for others: low. Tries to avoid conflict.
|
|
46
|
+
AVOIDING = "avoiding"
|
|
47
|
+
# Concern for self: low. Concern for others: high. Accommodates others.
|
|
48
|
+
OBLIGING = "obliging"
|
|
49
|
+
# Concern for self: high. Concern for others: high. Collaborates.
|
|
50
|
+
INTEGRATING = "integrating"
|
|
51
|
+
# Concern for self: high. Concern for others: low. Competes to win.
|
|
52
|
+
DOMINATING = "dominating"
|
|
53
|
+
# Concern for self: moderate. Concern for others: moderate. Trades off.
|
|
54
|
+
COMPROMISING = "compromising"
|
|
55
|
+
|
|
56
|
+
@classmethod
|
|
57
|
+
def random(
|
|
58
|
+
cls,
|
|
59
|
+
trait_configuration: BigFiveTraitConfiguration,
|
|
60
|
+
*,
|
|
61
|
+
rng: RandomSource | None = None,
|
|
62
|
+
) -> Self:
|
|
63
|
+
# These weights are loosely based on:
|
|
64
|
+
# Priyadarshini, S. (2017). Effect of Personality on Conflict
|
|
65
|
+
# Resolution Styles. IRA-International Journal of Management &
|
|
66
|
+
# Social Sciences, 7(2), 196-207.
|
|
67
|
+
style_levels = {
|
|
68
|
+
cls.AVOIDING: trait_configuration.neuroticism.score * 0.7
|
|
69
|
+
+ trait_configuration.openness.score * -0.1
|
|
70
|
+
+ trait_configuration.agreeableness.score * 0.2
|
|
71
|
+
+ trait_configuration.conscientiousness.score * -0.2,
|
|
72
|
+
cls.OBLIGING: trait_configuration.neuroticism.score * 0.2
|
|
73
|
+
+ trait_configuration.extraversion.score * -0.2
|
|
74
|
+
+ trait_configuration.openness.score * -0.1
|
|
75
|
+
+ trait_configuration.agreeableness.score * 0.3,
|
|
76
|
+
cls.INTEGRATING: trait_configuration.openness.score * 0.1
|
|
77
|
+
+ trait_configuration.agreeableness.score * 0.2
|
|
78
|
+
+ trait_configuration.conscientiousness.score * 0.1,
|
|
79
|
+
cls.DOMINATING: trait_configuration.neuroticism.score * -0.2
|
|
80
|
+
+ trait_configuration.extraversion.score * 0.2
|
|
81
|
+
+ trait_configuration.openness.score * -0.2
|
|
82
|
+
+ trait_configuration.agreeableness.score * -0.4
|
|
83
|
+
+ trait_configuration.conscientiousness.score * 0.2,
|
|
84
|
+
cls.COMPROMISING: trait_configuration.neuroticism.score * 0.1
|
|
85
|
+
+ trait_configuration.extraversion.score * 0.1
|
|
86
|
+
+ trait_configuration.conscientiousness.score * -0.2,
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
# Keep a small chance of selecting counter-indicated styles.
|
|
90
|
+
minimum_weight = 0.1
|
|
91
|
+
weights = {
|
|
92
|
+
style: max(level, minimum_weight)
|
|
93
|
+
for style, level in style_levels.items()
|
|
94
|
+
}
|
|
95
|
+
return _weighted_choice(weights, rng=rng)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@dataclass(frozen=True, slots=True)
|
|
99
|
+
class BigFiveTraitConfiguration:
|
|
100
|
+
# Appreciation for art, emotion, adventure, and curiosity.
|
|
101
|
+
# Opposite: closedness.
|
|
102
|
+
openness: BigFiveOpenness
|
|
103
|
+
# Self-discipline, dutifulness, and achievement orientation.
|
|
104
|
+
# Opposite: undisciplined.
|
|
105
|
+
conscientiousness: BigFiveConscientiousness
|
|
106
|
+
# Energy, sociability, and stimulation-seeking.
|
|
107
|
+
# Opposite: introversion.
|
|
108
|
+
extraversion: BigFiveExtraversion
|
|
109
|
+
# Compassion and cooperation toward others.
|
|
110
|
+
# Opposite: antagonism.
|
|
111
|
+
agreeableness: BigFiveAgreeableness
|
|
112
|
+
# Tendency toward unpleasant emotions and instability.
|
|
113
|
+
# Opposite: emotional stability.
|
|
114
|
+
neuroticism: BigFiveNeuroticism
|
|
115
|
+
|
|
116
|
+
@classmethod
|
|
117
|
+
def random(
|
|
118
|
+
cls, life_stage: LifeStage, *, rng: RandomSource | None = None
|
|
119
|
+
) -> Self:
|
|
120
|
+
return cls(
|
|
121
|
+
openness=BigFiveOpenness.random(life_stage, rng=rng),
|
|
122
|
+
conscientiousness=BigFiveConscientiousness.random(
|
|
123
|
+
life_stage, rng=rng
|
|
124
|
+
),
|
|
125
|
+
extraversion=BigFiveExtraversion.random(life_stage, rng=rng),
|
|
126
|
+
agreeableness=BigFiveAgreeableness.random(life_stage, rng=rng),
|
|
127
|
+
neuroticism=BigFiveNeuroticism.random(life_stage, rng=rng),
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
def __str__(self) -> str:
|
|
131
|
+
return (
|
|
132
|
+
"openness: "
|
|
133
|
+
f"{self.openness}\n"
|
|
134
|
+
"conscientiousness: "
|
|
135
|
+
f"{self.conscientiousness}\n"
|
|
136
|
+
"extraversion: "
|
|
137
|
+
f"{self.extraversion}\n"
|
|
138
|
+
"agreeableness: "
|
|
139
|
+
f"{self.agreeableness}\n"
|
|
140
|
+
"neuroticism: "
|
|
141
|
+
f"{self.neuroticism}"
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
_STYLE_TO_CONCERNS: dict[
|
|
146
|
+
BigFiveConflictResolutionStyle, tuple[PriorityLevel, PriorityLevel]
|
|
147
|
+
] = {
|
|
148
|
+
BigFiveConflictResolutionStyle.AVOIDING: (
|
|
149
|
+
PriorityLevel.LOW,
|
|
150
|
+
PriorityLevel.LOW,
|
|
151
|
+
),
|
|
152
|
+
BigFiveConflictResolutionStyle.OBLIGING: (
|
|
153
|
+
PriorityLevel.LOW,
|
|
154
|
+
PriorityLevel.HIGH,
|
|
155
|
+
),
|
|
156
|
+
BigFiveConflictResolutionStyle.INTEGRATING: (
|
|
157
|
+
PriorityLevel.HIGH,
|
|
158
|
+
PriorityLevel.HIGH,
|
|
159
|
+
),
|
|
160
|
+
BigFiveConflictResolutionStyle.DOMINATING: (
|
|
161
|
+
PriorityLevel.HIGH,
|
|
162
|
+
PriorityLevel.LOW,
|
|
163
|
+
),
|
|
164
|
+
BigFiveConflictResolutionStyle.COMPROMISING: (
|
|
165
|
+
PriorityLevel.MODERATE,
|
|
166
|
+
PriorityLevel.MODERATE,
|
|
167
|
+
),
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _validate_style_concerns() -> None:
|
|
172
|
+
expected = set(BigFiveConflictResolutionStyle)
|
|
173
|
+
actual = set(_STYLE_TO_CONCERNS)
|
|
174
|
+
if expected != actual:
|
|
175
|
+
missing = {style.value for style in expected - actual}
|
|
176
|
+
extra = {style.value for style in actual - expected}
|
|
177
|
+
raise ValueError(
|
|
178
|
+
"Conflict resolution styles and concern mapping are out of sync. "
|
|
179
|
+
f"Missing: {sorted(missing)}. Extra: {sorted(extra)}."
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
_validate_style_concerns()
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
@dataclass(frozen=True, slots=True)
|
|
187
|
+
class BigFiveConflictResolutionConfiguration:
|
|
188
|
+
conflict_resolution_style: BigFiveConflictResolutionStyle
|
|
189
|
+
concern_for_self: PriorityLevel
|
|
190
|
+
concern_for_others: PriorityLevel
|
|
191
|
+
|
|
192
|
+
@classmethod
|
|
193
|
+
def random(
|
|
194
|
+
cls,
|
|
195
|
+
trait_configuration: BigFiveTraitConfiguration,
|
|
196
|
+
*,
|
|
197
|
+
rng: RandomSource | None = None,
|
|
198
|
+
) -> Self:
|
|
199
|
+
style = BigFiveConflictResolutionStyle.random(
|
|
200
|
+
trait_configuration, rng=rng
|
|
201
|
+
)
|
|
202
|
+
concern_for_self, concern_for_others = _STYLE_TO_CONCERNS[style]
|
|
203
|
+
return cls(
|
|
204
|
+
conflict_resolution_style=style,
|
|
205
|
+
concern_for_self=concern_for_self,
|
|
206
|
+
concern_for_others=concern_for_others,
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
@dataclass(frozen=True, slots=True)
|
|
211
|
+
class BigFivePersonality:
|
|
212
|
+
trait_configuration: BigFiveTraitConfiguration
|
|
213
|
+
conflict_resolution_configuration: BigFiveConflictResolutionConfiguration
|
|
214
|
+
|
|
215
|
+
@classmethod
|
|
216
|
+
def random(
|
|
217
|
+
cls, life_stage: LifeStage, *, rng: RandomSource | None = None
|
|
218
|
+
) -> Self:
|
|
219
|
+
trait_configuration = BigFiveTraitConfiguration.random(
|
|
220
|
+
life_stage, rng=rng
|
|
221
|
+
)
|
|
222
|
+
conflict_configuration = BigFiveConflictResolutionConfiguration.random(
|
|
223
|
+
trait_configuration, rng=rng
|
|
224
|
+
)
|
|
225
|
+
return cls(
|
|
226
|
+
trait_configuration=trait_configuration,
|
|
227
|
+
conflict_resolution_configuration=conflict_configuration,
|
|
228
|
+
)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from personalitygen.enums import LifeStage as LifeStage, PriorityLevel as PriorityLevel
|
|
4
|
+
from personalitygen.randomness import RandomSource as RandomSource
|
|
5
|
+
from personalitygen.traits import BigFiveAgreeableness as BigFiveAgreeableness, BigFiveConscientiousness as BigFiveConscientiousness, BigFiveExtraversion as BigFiveExtraversion, BigFiveNeuroticism as BigFiveNeuroticism, BigFiveOpenness as BigFiveOpenness
|
|
6
|
+
from typing import Self
|
|
7
|
+
|
|
8
|
+
class BigFiveConflictResolutionStyle(str, Enum):
|
|
9
|
+
AVOIDING = 'avoiding'
|
|
10
|
+
OBLIGING = 'obliging'
|
|
11
|
+
INTEGRATING = 'integrating'
|
|
12
|
+
DOMINATING = 'dominating'
|
|
13
|
+
COMPROMISING = 'compromising'
|
|
14
|
+
@classmethod
|
|
15
|
+
def random(cls, trait_configuration: BigFiveTraitConfiguration, *, rng: RandomSource | None = None) -> Self: ...
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True, slots=True)
|
|
18
|
+
class BigFiveTraitConfiguration:
|
|
19
|
+
openness: BigFiveOpenness
|
|
20
|
+
conscientiousness: BigFiveConscientiousness
|
|
21
|
+
extraversion: BigFiveExtraversion
|
|
22
|
+
agreeableness: BigFiveAgreeableness
|
|
23
|
+
neuroticism: BigFiveNeuroticism
|
|
24
|
+
@classmethod
|
|
25
|
+
def random(cls, life_stage: LifeStage, *, rng: RandomSource | None = None) -> Self: ...
|
|
26
|
+
|
|
27
|
+
@dataclass(frozen=True, slots=True)
|
|
28
|
+
class BigFiveConflictResolutionConfiguration:
|
|
29
|
+
conflict_resolution_style: BigFiveConflictResolutionStyle
|
|
30
|
+
concern_for_self: PriorityLevel
|
|
31
|
+
concern_for_others: PriorityLevel
|
|
32
|
+
@classmethod
|
|
33
|
+
def random(cls, trait_configuration: BigFiveTraitConfiguration, *, rng: RandomSource | None = None) -> Self: ...
|
|
34
|
+
|
|
35
|
+
@dataclass(frozen=True, slots=True)
|
|
36
|
+
class BigFivePersonality:
|
|
37
|
+
trait_configuration: BigFiveTraitConfiguration
|
|
38
|
+
conflict_resolution_configuration: BigFiveConflictResolutionConfiguration
|
|
39
|
+
@classmethod
|
|
40
|
+
def random(cls, life_stage: LifeStage, *, rng: RandomSource | None = None) -> Self: ...
|
|
File without changes
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Random utilities used by personalitygen."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import random
|
|
6
|
+
import statistics
|
|
7
|
+
from typing import Protocol
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class RandomSource(Protocol):
|
|
11
|
+
"""Minimal interface needed for deterministic sampling."""
|
|
12
|
+
|
|
13
|
+
def gauss(self, mu: float, sigma: float) -> float: ...
|
|
14
|
+
|
|
15
|
+
def uniform(self, a: float, b: float) -> float: ...
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _coerce_rng(rng: RandomSource | None) -> RandomSource:
|
|
19
|
+
return rng if rng is not None else random
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def random_gaussian(
|
|
23
|
+
*,
|
|
24
|
+
mean: float,
|
|
25
|
+
stddev: float,
|
|
26
|
+
min_value: float,
|
|
27
|
+
max_value: float,
|
|
28
|
+
rng: RandomSource | None = None,
|
|
29
|
+
) -> float:
|
|
30
|
+
"""Draw a truncated Gaussian sample within the provided bounds."""
|
|
31
|
+
if stddev <= 0:
|
|
32
|
+
raise ValueError("stddev must be positive")
|
|
33
|
+
if min_value > max_value:
|
|
34
|
+
raise ValueError("min_value must be <= max_value")
|
|
35
|
+
|
|
36
|
+
source = _coerce_rng(rng)
|
|
37
|
+
distribution = statistics.NormalDist(mean, stddev)
|
|
38
|
+
lower = distribution.cdf(min_value)
|
|
39
|
+
upper = distribution.cdf(max_value)
|
|
40
|
+
if lower >= upper:
|
|
41
|
+
return max(min_value, min(max_value, mean))
|
|
42
|
+
|
|
43
|
+
cdf_epsilon = 1e-12
|
|
44
|
+
lower = max(lower, cdf_epsilon)
|
|
45
|
+
upper = min(upper, 1.0 - cdf_epsilon)
|
|
46
|
+
if lower >= upper:
|
|
47
|
+
return max(min_value, min(max_value, mean))
|
|
48
|
+
|
|
49
|
+
u = source.uniform(lower, upper)
|
|
50
|
+
return distribution.inv_cdf(u)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
from typing import Protocol
|
|
2
|
+
|
|
3
|
+
class RandomSource(Protocol):
|
|
4
|
+
def gauss(self, mu: float, sigma: float) -> float: ...
|
|
5
|
+
def uniform(self, a: float, b: float) -> float: ...
|
|
6
|
+
|
|
7
|
+
def random_gaussian(*, mean: float, stddev: float, min_value: float, max_value: float, rng: RandomSource | None = None) -> float: ...
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
"""Big Five trait models."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from typing import Self
|
|
7
|
+
|
|
8
|
+
from personalitygen.constants import UNIT_RANGE_MAX, UNIT_RANGE_MIN
|
|
9
|
+
from personalitygen.enums import LifeStage
|
|
10
|
+
from personalitygen.randomness import RandomSource, random_gaussian
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _validate_unit_range(*values: float) -> None:
|
|
14
|
+
for value in values:
|
|
15
|
+
if not (UNIT_RANGE_MIN <= value <= UNIT_RANGE_MAX):
|
|
16
|
+
raise ValueError(
|
|
17
|
+
"All trait components must be in the range 0.0...1.0"
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _format_score(value: float) -> str:
|
|
22
|
+
return format(value, ".2g")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
_TRAIT_SAMPLE_MIN = 0.01
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass(frozen=True, slots=True)
|
|
29
|
+
class _TraitConfig:
|
|
30
|
+
stddev: float
|
|
31
|
+
means_by_stage: dict[LifeStage, tuple[float, float, float]]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _sample_trait(
|
|
35
|
+
life_stage: LifeStage,
|
|
36
|
+
config: _TraitConfig,
|
|
37
|
+
*,
|
|
38
|
+
rng: RandomSource | None = None,
|
|
39
|
+
) -> tuple[float, float, float]:
|
|
40
|
+
means = config.means_by_stage.get(life_stage)
|
|
41
|
+
if means is None:
|
|
42
|
+
raise ValueError(f"Unsupported life stage: {life_stage}")
|
|
43
|
+
|
|
44
|
+
mean_a, mean_b, mean_c = means
|
|
45
|
+
return (
|
|
46
|
+
random_gaussian(
|
|
47
|
+
stddev=config.stddev,
|
|
48
|
+
mean=mean_a,
|
|
49
|
+
max_value=UNIT_RANGE_MAX,
|
|
50
|
+
min_value=_TRAIT_SAMPLE_MIN,
|
|
51
|
+
rng=rng,
|
|
52
|
+
),
|
|
53
|
+
random_gaussian(
|
|
54
|
+
stddev=config.stddev,
|
|
55
|
+
mean=mean_b,
|
|
56
|
+
max_value=UNIT_RANGE_MAX,
|
|
57
|
+
min_value=_TRAIT_SAMPLE_MIN,
|
|
58
|
+
rng=rng,
|
|
59
|
+
),
|
|
60
|
+
random_gaussian(
|
|
61
|
+
stddev=config.stddev,
|
|
62
|
+
mean=mean_c,
|
|
63
|
+
max_value=UNIT_RANGE_MAX,
|
|
64
|
+
min_value=_TRAIT_SAMPLE_MIN,
|
|
65
|
+
rng=rng,
|
|
66
|
+
),
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
_OPENNESS_CONFIG = _TraitConfig(
|
|
71
|
+
stddev=0.16,
|
|
72
|
+
means_by_stage={
|
|
73
|
+
LifeStage.CHILD: (0.80, 0.85, 0.85),
|
|
74
|
+
LifeStage.YOUNG_ADULT: (0.70, 0.75, 0.75),
|
|
75
|
+
LifeStage.ADULT: (0.60, 0.65, 0.65),
|
|
76
|
+
},
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
_CONSCIENTIOUSNESS_CONFIG = _TraitConfig(
|
|
80
|
+
stddev=0.22,
|
|
81
|
+
means_by_stage={
|
|
82
|
+
LifeStage.CHILD: (0.50, 0.55, 0.50),
|
|
83
|
+
LifeStage.YOUNG_ADULT: (0.60, 0.65, 0.60),
|
|
84
|
+
LifeStage.ADULT: (0.70, 0.75, 0.70),
|
|
85
|
+
},
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
_EXTRAVERSION_CONFIG = _TraitConfig(
|
|
89
|
+
stddev=0.27,
|
|
90
|
+
means_by_stage={
|
|
91
|
+
LifeStage.CHILD: (0.72, 0.70, 0.72),
|
|
92
|
+
LifeStage.YOUNG_ADULT: (0.62, 0.60, 0.62),
|
|
93
|
+
LifeStage.ADULT: (0.52, 0.50, 0.52),
|
|
94
|
+
},
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
_AGREEABLENESS_CONFIG = _TraitConfig(
|
|
98
|
+
stddev=0.18,
|
|
99
|
+
means_by_stage={
|
|
100
|
+
LifeStage.CHILD: (0.55, 0.55, 0.40),
|
|
101
|
+
LifeStage.YOUNG_ADULT: (0.65, 0.65, 0.50),
|
|
102
|
+
LifeStage.ADULT: (0.75, 0.75, 0.60),
|
|
103
|
+
},
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
_NEUROTICISM_CONFIG = _TraitConfig(
|
|
107
|
+
stddev=0.32,
|
|
108
|
+
means_by_stage={
|
|
109
|
+
LifeStage.CHILD: (0.70, 0.60, 0.55),
|
|
110
|
+
LifeStage.YOUNG_ADULT: (0.60, 0.50, 0.45),
|
|
111
|
+
LifeStage.ADULT: (0.50, 0.40, 0.35),
|
|
112
|
+
},
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@dataclass(frozen=True, slots=True)
|
|
117
|
+
class BigFiveOpenness:
|
|
118
|
+
aesthetic_sensitivity_score: float
|
|
119
|
+
creative_imagination_score: float
|
|
120
|
+
intellectual_curiosity_score: float
|
|
121
|
+
score: float = field(init=False)
|
|
122
|
+
|
|
123
|
+
def __post_init__(self) -> None:
|
|
124
|
+
_validate_unit_range(
|
|
125
|
+
self.aesthetic_sensitivity_score,
|
|
126
|
+
self.creative_imagination_score,
|
|
127
|
+
self.intellectual_curiosity_score,
|
|
128
|
+
)
|
|
129
|
+
object.__setattr__(
|
|
130
|
+
self,
|
|
131
|
+
"score",
|
|
132
|
+
(
|
|
133
|
+
self.aesthetic_sensitivity_score
|
|
134
|
+
+ self.creative_imagination_score
|
|
135
|
+
+ self.intellectual_curiosity_score
|
|
136
|
+
)
|
|
137
|
+
/ 3,
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
@classmethod
|
|
141
|
+
def random(
|
|
142
|
+
cls, life_stage: LifeStage, *, rng: RandomSource | None = None
|
|
143
|
+
) -> Self:
|
|
144
|
+
(
|
|
145
|
+
aesthetic_sensitivity,
|
|
146
|
+
creative_imagination,
|
|
147
|
+
intellectual_curiosity,
|
|
148
|
+
) = _sample_trait(life_stage, _OPENNESS_CONFIG, rng=rng)
|
|
149
|
+
return cls(
|
|
150
|
+
aesthetic_sensitivity_score=aesthetic_sensitivity,
|
|
151
|
+
creative_imagination_score=creative_imagination,
|
|
152
|
+
intellectual_curiosity_score=intellectual_curiosity,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
def __str__(self) -> str:
|
|
156
|
+
return (
|
|
157
|
+
f"{_format_score(self.score)} "
|
|
158
|
+
f"{{A:{_format_score(self.aesthetic_sensitivity_score)} "
|
|
159
|
+
f"C:{_format_score(self.creative_imagination_score)} "
|
|
160
|
+
f"I:{_format_score(self.intellectual_curiosity_score)}}}"
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
@dataclass(frozen=True, slots=True)
|
|
165
|
+
class BigFiveConscientiousness:
|
|
166
|
+
organization_score: float
|
|
167
|
+
responsibility_score: float
|
|
168
|
+
productivity_score: float
|
|
169
|
+
score: float = field(init=False)
|
|
170
|
+
|
|
171
|
+
def __post_init__(self) -> None:
|
|
172
|
+
_validate_unit_range(
|
|
173
|
+
self.organization_score,
|
|
174
|
+
self.responsibility_score,
|
|
175
|
+
self.productivity_score,
|
|
176
|
+
)
|
|
177
|
+
object.__setattr__(
|
|
178
|
+
self,
|
|
179
|
+
"score",
|
|
180
|
+
(
|
|
181
|
+
self.organization_score
|
|
182
|
+
+ self.responsibility_score
|
|
183
|
+
+ self.productivity_score
|
|
184
|
+
)
|
|
185
|
+
/ 3,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
@classmethod
|
|
189
|
+
def random(
|
|
190
|
+
cls, life_stage: LifeStage, *, rng: RandomSource | None = None
|
|
191
|
+
) -> Self:
|
|
192
|
+
organization, responsibility, productivity = _sample_trait(
|
|
193
|
+
life_stage, _CONSCIENTIOUSNESS_CONFIG, rng=rng
|
|
194
|
+
)
|
|
195
|
+
return cls(
|
|
196
|
+
organization_score=organization,
|
|
197
|
+
responsibility_score=responsibility,
|
|
198
|
+
productivity_score=productivity,
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
def __str__(self) -> str:
|
|
202
|
+
return (
|
|
203
|
+
f"{_format_score(self.score)} "
|
|
204
|
+
f"{{O:{_format_score(self.organization_score)} "
|
|
205
|
+
f"R:{_format_score(self.responsibility_score)} "
|
|
206
|
+
f"P:{_format_score(self.productivity_score)}}}"
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
@dataclass(frozen=True, slots=True)
|
|
211
|
+
class BigFiveExtraversion:
|
|
212
|
+
assertiveness_score: float
|
|
213
|
+
sociability_score: float
|
|
214
|
+
energy_level_score: float
|
|
215
|
+
score: float = field(init=False)
|
|
216
|
+
|
|
217
|
+
def __post_init__(self) -> None:
|
|
218
|
+
_validate_unit_range(
|
|
219
|
+
self.assertiveness_score,
|
|
220
|
+
self.sociability_score,
|
|
221
|
+
self.energy_level_score,
|
|
222
|
+
)
|
|
223
|
+
object.__setattr__(
|
|
224
|
+
self,
|
|
225
|
+
"score",
|
|
226
|
+
(
|
|
227
|
+
self.assertiveness_score
|
|
228
|
+
+ self.sociability_score
|
|
229
|
+
+ self.energy_level_score
|
|
230
|
+
)
|
|
231
|
+
/ 3,
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
@classmethod
|
|
235
|
+
def random(
|
|
236
|
+
cls, life_stage: LifeStage, *, rng: RandomSource | None = None
|
|
237
|
+
) -> Self:
|
|
238
|
+
assertiveness, sociability, energy_level = _sample_trait(
|
|
239
|
+
life_stage, _EXTRAVERSION_CONFIG, rng=rng
|
|
240
|
+
)
|
|
241
|
+
return cls(
|
|
242
|
+
assertiveness_score=assertiveness,
|
|
243
|
+
sociability_score=sociability,
|
|
244
|
+
energy_level_score=energy_level,
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
def __str__(self) -> str:
|
|
248
|
+
return (
|
|
249
|
+
f"{_format_score(self.score)} "
|
|
250
|
+
f"{{A:{_format_score(self.assertiveness_score)} "
|
|
251
|
+
f"S:{_format_score(self.sociability_score)} "
|
|
252
|
+
f"E:{_format_score(self.energy_level_score)}}}"
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
@dataclass(frozen=True, slots=True)
|
|
257
|
+
class BigFiveAgreeableness:
|
|
258
|
+
compassion_score: float
|
|
259
|
+
respectfulness_score: float
|
|
260
|
+
trust_score: float
|
|
261
|
+
score: float = field(init=False)
|
|
262
|
+
|
|
263
|
+
def __post_init__(self) -> None:
|
|
264
|
+
_validate_unit_range(
|
|
265
|
+
self.compassion_score,
|
|
266
|
+
self.respectfulness_score,
|
|
267
|
+
self.trust_score,
|
|
268
|
+
)
|
|
269
|
+
object.__setattr__(
|
|
270
|
+
self,
|
|
271
|
+
"score",
|
|
272
|
+
(
|
|
273
|
+
self.compassion_score
|
|
274
|
+
+ self.respectfulness_score
|
|
275
|
+
+ self.trust_score
|
|
276
|
+
)
|
|
277
|
+
/ 3,
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
@classmethod
|
|
281
|
+
def random(
|
|
282
|
+
cls, life_stage: LifeStage, *, rng: RandomSource | None = None
|
|
283
|
+
) -> Self:
|
|
284
|
+
compassion, respectfulness, trust = _sample_trait(
|
|
285
|
+
life_stage, _AGREEABLENESS_CONFIG, rng=rng
|
|
286
|
+
)
|
|
287
|
+
return cls(
|
|
288
|
+
compassion_score=compassion,
|
|
289
|
+
respectfulness_score=respectfulness,
|
|
290
|
+
trust_score=trust,
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
def __str__(self) -> str:
|
|
294
|
+
return (
|
|
295
|
+
f"{_format_score(self.score)} "
|
|
296
|
+
f"{{C:{_format_score(self.compassion_score)} "
|
|
297
|
+
f"R:{_format_score(self.respectfulness_score)} "
|
|
298
|
+
f"T:{_format_score(self.trust_score)}}}"
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
@dataclass(frozen=True, slots=True)
|
|
303
|
+
class BigFiveNeuroticism:
|
|
304
|
+
anxiety_score: float
|
|
305
|
+
emotional_volatility_score: float
|
|
306
|
+
depression_score: float
|
|
307
|
+
score: float = field(init=False)
|
|
308
|
+
|
|
309
|
+
def __post_init__(self) -> None:
|
|
310
|
+
_validate_unit_range(
|
|
311
|
+
self.anxiety_score,
|
|
312
|
+
self.emotional_volatility_score,
|
|
313
|
+
self.depression_score,
|
|
314
|
+
)
|
|
315
|
+
object.__setattr__(
|
|
316
|
+
self,
|
|
317
|
+
"score",
|
|
318
|
+
(
|
|
319
|
+
self.anxiety_score
|
|
320
|
+
+ self.emotional_volatility_score
|
|
321
|
+
+ self.depression_score
|
|
322
|
+
)
|
|
323
|
+
/ 3,
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
@classmethod
|
|
327
|
+
def random(
|
|
328
|
+
cls, life_stage: LifeStage, *, rng: RandomSource | None = None
|
|
329
|
+
) -> Self:
|
|
330
|
+
anxiety, emotional_volatility, depression = _sample_trait(
|
|
331
|
+
life_stage, _NEUROTICISM_CONFIG, rng=rng
|
|
332
|
+
)
|
|
333
|
+
return cls(
|
|
334
|
+
anxiety_score=anxiety,
|
|
335
|
+
emotional_volatility_score=emotional_volatility,
|
|
336
|
+
depression_score=depression,
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
def __str__(self) -> str:
|
|
340
|
+
return (
|
|
341
|
+
f"{_format_score(self.score)} "
|
|
342
|
+
f"{{A:{_format_score(self.anxiety_score)} "
|
|
343
|
+
f"E:{_format_score(self.emotional_volatility_score)} "
|
|
344
|
+
f"D:{_format_score(self.depression_score)}}}"
|
|
345
|
+
)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from personalitygen.constants import UNIT_RANGE_MAX as UNIT_RANGE_MAX, UNIT_RANGE_MIN as UNIT_RANGE_MIN
|
|
3
|
+
from personalitygen.enums import LifeStage as LifeStage
|
|
4
|
+
from personalitygen.randomness import RandomSource as RandomSource, random_gaussian as random_gaussian
|
|
5
|
+
from typing import Self
|
|
6
|
+
|
|
7
|
+
@dataclass(frozen=True, slots=True)
|
|
8
|
+
class _TraitConfig:
|
|
9
|
+
stddev: float
|
|
10
|
+
means_by_stage: dict[LifeStage, tuple[float, float, float]]
|
|
11
|
+
|
|
12
|
+
@dataclass(frozen=True, slots=True)
|
|
13
|
+
class BigFiveOpenness:
|
|
14
|
+
aesthetic_sensitivity_score: float
|
|
15
|
+
creative_imagination_score: float
|
|
16
|
+
intellectual_curiosity_score: float
|
|
17
|
+
score: float = field(init=False)
|
|
18
|
+
def __post_init__(self) -> None: ...
|
|
19
|
+
@classmethod
|
|
20
|
+
def random(cls, life_stage: LifeStage, *, rng: RandomSource | None = None) -> Self: ...
|
|
21
|
+
|
|
22
|
+
@dataclass(frozen=True, slots=True)
|
|
23
|
+
class BigFiveConscientiousness:
|
|
24
|
+
organization_score: float
|
|
25
|
+
responsibility_score: float
|
|
26
|
+
productivity_score: float
|
|
27
|
+
score: float = field(init=False)
|
|
28
|
+
def __post_init__(self) -> None: ...
|
|
29
|
+
@classmethod
|
|
30
|
+
def random(cls, life_stage: LifeStage, *, rng: RandomSource | None = None) -> Self: ...
|
|
31
|
+
|
|
32
|
+
@dataclass(frozen=True, slots=True)
|
|
33
|
+
class BigFiveExtraversion:
|
|
34
|
+
assertiveness_score: float
|
|
35
|
+
sociability_score: float
|
|
36
|
+
energy_level_score: float
|
|
37
|
+
score: float = field(init=False)
|
|
38
|
+
def __post_init__(self) -> None: ...
|
|
39
|
+
@classmethod
|
|
40
|
+
def random(cls, life_stage: LifeStage, *, rng: RandomSource | None = None) -> Self: ...
|
|
41
|
+
|
|
42
|
+
@dataclass(frozen=True, slots=True)
|
|
43
|
+
class BigFiveAgreeableness:
|
|
44
|
+
compassion_score: float
|
|
45
|
+
respectfulness_score: float
|
|
46
|
+
trust_score: float
|
|
47
|
+
score: float = field(init=False)
|
|
48
|
+
def __post_init__(self) -> None: ...
|
|
49
|
+
@classmethod
|
|
50
|
+
def random(cls, life_stage: LifeStage, *, rng: RandomSource | None = None) -> Self: ...
|
|
51
|
+
|
|
52
|
+
@dataclass(frozen=True, slots=True)
|
|
53
|
+
class BigFiveNeuroticism:
|
|
54
|
+
anxiety_score: float
|
|
55
|
+
emotional_volatility_score: float
|
|
56
|
+
depression_score: float
|
|
57
|
+
score: float = field(init=False)
|
|
58
|
+
def __post_init__(self) -> None: ...
|
|
59
|
+
@classmethod
|
|
60
|
+
def random(cls, life_stage: LifeStage, *, rng: RandomSource | None = None) -> Self: ...
|