Issues (105)

personroles/person.py (28 issues)

1
#!/usr/bin/env python
2
# -*- coding: utf-8 -*-
3
"""A set of dataclasses concerning roles of persons and their particulars."""
4
import datetime
5
import os
6
import sys
7
from dataclasses import dataclass, field
8
from typing import Optional, Tuple
9
10
from gender_guesser import detector as sex  # type: ignore
0 ignored issues
show
Unable to import 'gender_guesser'
Loading history...
11
12
PACKAGE_PARENT = ".."
13
SCRIPT_DIR = os.path.dirname(
14
    os.path.realpath(os.path.join(os.getcwd(), os.path.expanduser(__file__)))
15
)  # isort:skip # noqa # pylint: disable=wrong-import-position
16
sys.path.append(
17
    os.path.normpath(os.path.join(SCRIPT_DIR, PACKAGE_PARENT))
18
)  # isort: skip # noqa # pylint: disable=wrong-import-position
19
20
from personroles.resources.constants import (  # type: ignore # noqa
21
    PEER_PREPOSITIONS,
22
    PEERTITLES,
23
)
24
from personroles.resources.helpers import AttrDisplay  # type: ignore # noqa
25
from personroles.resources.helpers import TooManyFirstNames  # noqa
26
27
28
@dataclass
0 ignored issues
show
Coding Style Naming introduced by
Class name "_Name_default" doesn't conform to PascalCase naming style ('[^\\W\\da-z][^\\W_]+$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
29
class _Name_default:
30
    middle_name_1: Optional[str] = field(default=None)
31
    middle_name_2: Optional[str] = field(default=None)
32
    maiden_name: Optional[str] = field(default=None)
33
    divorcée: Optional[str] = field(default=None)
34
35
36
@dataclass
0 ignored issues
show
Coding Style Naming introduced by
Class name "_Name_base" doesn't conform to PascalCase naming style ('[^\\W\\da-z][^\\W_]+$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
37
class _Name_base:
38
    first_name: str
39
    last_name: str
40
41
42
@dataclass
43
class Name(_Name_default, _Name_base, AttrDisplay):
44
    """A person's names: first, middle_1, middle_2, last name"""
45
46
    def __post_init__(self):
47
        """
48
        In case a Name instance is initialized with all first names in one
49
        string, __post_init__ will take care of this and assign each first
50
        name its attribute. Also it will raise TooManyFirstNames if more than
51
        three first names are given.
52
        """
53
        first_names = self.first_name.split(" ")
54
        self.first_name = first_names[0]
55
        if len(first_names) == 2:
56
            self.middle_name_1 = first_names[1]
57
        elif len(first_names) == 3:
58
            self.middle_name_1 = first_names[1]
59
            self.middle_name_2 = first_names[-1]
60
        elif len(first_names) > 3:
61
            print(first_names)
62
            raise TooManyFirstNames("There are more than three first names!")
63
64
65
@dataclass
0 ignored issues
show
Coding Style Naming introduced by
Class name "_Peertitle_default" doesn't conform to PascalCase naming style ('[^\\W\\da-z][^\\W_]+$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
66
class _Peertitle_default:
67
    peer_title: Optional[str] = field(default=None)
68
    peer_preposition: Optional[str] = field(default=None)
69
70
    def nobility_title(self) -> None:
0 ignored issues
show
Missing function or method docstring
Loading history...
71
        if self.peer_title is not None:
72
            title = self.peer_title
73
            self.peer_title, self.peer_preposition = self.title_fix(title)
74
75
    def title_fix(self, title) -> Tuple[str, str]:
0 ignored issues
show
Missing function or method docstring
Loading history...
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
76
        titles = title.split(" ")
77
        title_tmp = ""
78
        preposition_tmp = ""
79
        for prep in titles:
80
            if prep.lower() in PEER_PREPOSITIONS:
81
                preposition_tmp = preposition_tmp + prep.lower() + " "
82
            elif prep in PEERTITLES:
83
                title_tmp = title_tmp + prep + " "
84
        peer_preposition = preposition_tmp.strip()
85
        peer_title = title_tmp.strip()
86
87
        return peer_title, peer_preposition
88
89
90
@dataclass
0 ignored issues
show
Missing class docstring
Loading history...
91
class Noble(_Peertitle_default, Name, AttrDisplay):
92
    def __post_init__(self):
93
        """Initialize names and titles."""
94
        Name.__post_init__(self)
95
        self.nobility_title()
96
97
98
@dataclass
0 ignored issues
show
Coding Style Naming introduced by
Class name "_Academic_title_default" doesn't conform to PascalCase naming style ('[^\\W\\da-z][^\\W_]+$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
99
class _Academic_title_default:
100
    academic_title: Optional[str] = field(default=None)
101
102
    def degree_title(self) -> None:
0 ignored issues
show
Missing function or method docstring
Loading history...
103
        if self.academic_title is not None:
104
            title = self.academic_title
105
            self.academic_title = self.title_repair(title)
106
107
    def title_repair(self, title) -> str:
0 ignored issues
show
Missing function or method docstring
Loading history...
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
108
        if ".D" in title:
109
            title = ". ".join(c for c in title.split("."))
110
        if ".A" in title:
111
            title = ". ".join(c for c in title.split("."))
112
        if title.endswith("Dr"):
113
            title = title[:-2] + "Dr."
114
        while "  " in title:
115
            title = title.replace("  ", " ")
116
        title = title.strip()
117
118
        return title
119
120
121
@dataclass
0 ignored issues
show
Missing class docstring
Loading history...
122
class Academic(_Academic_title_default, Name, AttrDisplay):
123
    def __post_init__(self):
124
        """Initialize names of Name and degree."""
125
        Name.__post_init__(self)
126
        self.degree_title()
127
128
129
@dataclass
0 ignored issues
show
Coding Style Naming introduced by
Class name "_Person_default" doesn't conform to PascalCase naming style ('[^\\W\\da-z][^\\W_]+$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Too many instance attributes (8/7)
Loading history...
130
class _Person_default:
131
    gender: str = field(default="unknown")
132
    year_of_birth: str = field(default="unknown")
133
    date_of_birth: str = field(default="unknown")
134
    age: str = field(default="unknown")
135
    deceased: bool = field(default=False)
136
    year_of_death: str = field(default="unknown")
137
    date_of_death: str = field(default="unknown")
138
    profession: str = field(default="unknown")
139
140
141
@dataclass
0 ignored issues
show
Missing class docstring
Loading history...
Too many ancestors (8/7)
Loading history...
142
class Person(
143
    _Peertitle_default,
0 ignored issues
show
Wrong hanging indentation before block (add 4 spaces).
Loading history...
144
    _Academic_title_default,
0 ignored issues
show
Wrong hanging indentation before block (add 4 spaces).
Loading history...
145
    _Person_default,
0 ignored issues
show
Wrong hanging indentation before block (add 4 spaces).
Loading history...
146
    Name,
0 ignored issues
show
Wrong hanging indentation before block (add 4 spaces).
Loading history...
147
    AttrDisplay,  # noqa
0 ignored issues
show
Wrong hanging indentation before block (add 4 spaces).
Loading history...
148
):
149
    def __post_init__(self):
150
        """
151
        Initializing names, titles (academic and peer), age, sex, and year of
152
        birth."""
153
        Name.__post_init__(self)
154
        Noble.__post_init__(self)
155
        Academic.__post_init__(self)
156
        self.get_sex()
157
        self.get_age()
158
        self.get_year_of_birth()
159
160
    def get_sex(self) -> None:
0 ignored issues
show
Missing function or method docstring
Loading history...
161
        if "-" in self.first_name:
162
            first_name = self.first_name.split("-")[0]
163
        else:
164
            first_name = self.first_name
165
        d = sex.Detector()
0 ignored issues
show
Coding Style Naming introduced by
Variable name "d" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
166
        gender = d.get_gender(f"{first_name}")
167
        if "female" in gender:
168
            self.gender = "female"
169
        elif "male" in gender:
170
            self.gender = "male"
171
172
    def get_year_of_birth(self) -> None:
0 ignored issues
show
Missing function or method docstring
Loading history...
173
        if "." in self.date_of_birth:
174
            self.year_of_birth = self.date_of_birth.split(".")[-1]
175
        elif len(self.date_of_birth.strip()) == 4:
176
            self.year_of_birth = self.date_of_birth
177
            self.date_of_birth = "unknown"
178
179
    def get_age(self) -> None:
0 ignored issues
show
Missing function or method docstring
Loading history...
180
        today = datetime.date.today()
181
        if self.date_of_birth != "unknown":
182
            born = str(self.date_of_birth)
183
            if len(born) > 4 and len(born) < 12 and "-" in born:
184
                self.get_yob_and_yod(born)
185
            elif len(born) > 8 and "-" in born:
186
                self.get_dob_and_dod(born)
187
            elif "." in born:
188
                born = born.split(".")[-1]
189
                self.age = str(int(today.year) - int(born.strip()))
190
            else:
191
                self.age = str(int(today.year) - int(born.strip()))
192
193
    def get_yob_and_yod(self, born) -> None:
0 ignored issues
show
Missing function or method docstring
Loading history...
194
        self.year_of_death = born.strip()[5:]
195
        self.year_of_birth = born[:4]
196
        self.deceased = True
197
198
    def get_dob_and_dod(self, born) -> None:
0 ignored issues
show
Missing function or method docstring
Loading history...
199
        self.date_of_death = born.split("-")[-1].strip()
200
        self.date_of_birth = born.split("-")[0].strip()
201
        self.deceased = True
202
203
204
if __name__ == "__main__":
205
206
    name = Name("Hans Hermann", "Werner")
207
    print('name = Name("Hans Hermann", "Werner")')
208
    print(name)
209
210
    noble = Noble("Dagmara", "Bodelschwingh", peer_title="Gräfin von")
211
    print('noble = Noble("Dagmara", "Bodelschwingh", peer_title="Gräfin von")')
212
    print(noble)
213
214
    academic = Academic("Horst Heiner", "Wiekeiner", academic_title="Dr.")  # noqa
215
    print(
216
        'academic = Academic("Horst Heiner", "Wiekeiner", academic_title="Dr.")'  # noqa
217
    )  # noqa
218
    print(academic)
219
220
    person_1 = Person(
221
        "Sven", "Rübennase", academic_title="MBA", date_of_birth="1990"
222
    )  # noqa
223
    print(
224
        'person_1 = Person("Sven", "Rübennase", academic_title="MBA", date_of_birth="1990")'  # noqa
225
    )  # noqa
226
    print(person_1)
227