Test Failed
Push — master ( aab9da...98f324 )
by Oliver
01:53
created

roles.person.Person.get_year_of_birth()   A

Complexity

Conditions 3

Size

Total Lines 6
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 6
dl 0
loc 6
rs 10
c 0
b 0
f 0
cc 3
nop 1
1
#!/usr/bin/env python
2
# -*- coding: utf-8 -*-
3
"""
4
A set of dataclasses concerning roles of persons and their particulars.
5
"""
6
import datetime
7
import os
8
import sys
9
from dataclasses import dataclass, field
10
from typing import Optional, Tuple
11
12
from gender_guesser import detector as sex  # type: ignore
0 ignored issues
show
introduced by
Unable to import 'gender_guesser'
Loading history...
13
14
PACKAGE_PARENT = ".."
15
SCRIPT_DIR = os.path.dirname(
16
    os.path.realpath(os.path.join(os.getcwd(), os.path.expanduser(__file__)))
17
)  # isort:skip # noqa # pylint: disable=wrong-import-position
18
sys.path.append(
19
    os.path.normpath(os.path.join(SCRIPT_DIR, PACKAGE_PARENT))
20
)  # isort: skip # noqa # pylint: disable=wrong-import-position
21
22
from roles.resources.constants import PEER_PREPOSITIONS  # type: ignore # noqa
23
from roles.resources.constants import PEERTITLES  # type: ignore # noqa
24
from roles.resources.helpers import (  # type: ignore # noqa; type: ignore # noqa; type: ignore  # noqa; type: ignore # noqa
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (124/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
Unused Code introduced by
Unused NotInRange imported from roles.resources.helpers
Loading history...
Unused Code introduced by
Unused Party imported from roles.resources.helpers
Loading history...
25
    AttrDisplay,
26
    NotInRange,
27
    Party,
28
    TooManyFirstNames,
29
)
30
31
32
@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...
33
class _Name_default:
34
    middle_name_1: Optional[str] = field(default=None)
35
    middle_name_2: Optional[str] = field(default=None)
36
    maiden_name: Optional[str] = field(default=None)
37
    divorcée: Optional[str] = field(default=None)
38
39
40
@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...
41
class _Name_base:
42
    first_name: str
43
    last_name: str
44
45
46
@dataclass
47
class Name(_Name_default, _Name_base, AttrDisplay):
48
49
    """
50
    The most basic part to describe a person.
51
    To add more middle names, dataclass _Name_default has to be given further
52
    middle_name attributes. Since this project currently focusses on German
53
    politicians, the limit of three given names is preserved.
54
    """
55
56
    def __post_init__(self):
57
        """
58
        In case a Name instance is initialized with all first names in one
59
        string, __post_init__ will take care of this and assign each first
60
        name its attribute. Also it will raise TooManyFirstNames if more than
61
        three first names are given.
62
        """
63
        first_names = self.first_name.split(" ")
64
        self.first_name = first_names[0]
65
        if len(first_names) == 2:
66
            self.middle_name_1 = first_names[1]
67
        elif len(first_names) == 3:
68
            self.middle_name_1 = first_names[1]
69
            self.middle_name_2 = first_names[-1]
70
        elif len(first_names) > 3:
71
            print(first_names)
72
            raise TooManyFirstNames("There are more than three first names!")
73
74
75
@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...
76
class _Peertitle_default:
77
    peer_title: Optional[str] = field(default=None)
78
    peer_preposition: Optional[str] = field(default=None)
79
80
    def nobility_title(self) -> None:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
81
        if self.peer_title is not None:
82
            title = self.peer_title
83
            self.peer_title, self.peer_preposition = self.title_fix(title)
84
85
    def title_fix(self, title) -> Tuple[str, str]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
Coding Style introduced by
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...
86
        titles = title.split(" ")
87
        title_tmp = ""
88
        preposition_tmp = ""
89
        for prep in titles:
90
            if prep.lower() in PEER_PREPOSITIONS:
91
                preposition_tmp = preposition_tmp + prep.lower() + " "
92
            elif prep in PEERTITLES:
93
                title_tmp = title_tmp + prep + " "
94
        peer_preposition = preposition_tmp.strip()
95
        peer_title = title_tmp.strip()
96
97
        return peer_title, peer_preposition
98
99
100
@dataclass
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
101
class Noble(_Peertitle_default, Name, AttrDisplay):
102
    def __post_init__(self):
103
        """Initialize names and titles."""
104
        Name.__post_init__(self)
105
        self.nobility_title()
106
107
108
@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...
109
class _Academic_title_default:
110
    academic_title: Optional[str] = field(default=None)
111
112
    def degree_title(self) -> None:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
113
        if self.academic_title is not None:
114
            title = self.academic_title
115
            self.academic_title = self.title_repair(title)
116
117
    def title_repair(self, title) -> str:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
Coding Style introduced by
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...
118
        if ".D" in title:
119
            title = ". ".join(c for c in title.split("."))
120
        if ".A" in title:
121
            title = ". ".join(c for c in title.split("."))
122
        if title.endswith("Dr"):
123
            title = title[:-2] + "Dr."
124
        while "  " in title:
125
            title = title.replace("  ", " ")
126
        title = title.strip()
127
128
        return title
129
130
131
@dataclass
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
132
class Academic(_Academic_title_default, Name, AttrDisplay):
133
    def __post_init__(self):
134
        Name.__post_init__(self)
135
        self.degree_title()
136
137
138
@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...
best-practice introduced by
Too many instance attributes (8/7)
Loading history...
139
class _Person_default:
140
    gender: str = field(default="unknown")
141
    year_of_birth: str = field(default="unknown")
142
    date_of_birth: str = field(default="unknown")
143
    age: str = field(default="unknown")
144
    deceased: bool = field(default=False)
145
    year_of_death: str = field(default="unknown")
146
    date_of_death: str = field(default="unknown")
147
    profession: str = field(default="unknown")
148
149
150
@dataclass
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
best-practice introduced by
Too many ancestors (8/7)
Loading history...
151
class Person(
152
    _Peertitle_default,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
153
    _Academic_title_default,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
154
    _Person_default,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
155
    Name,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
156
    AttrDisplay,  # noqa
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
157
):
158
    def __post_init__(self):
159
        Name.__post_init__(self)
160
        Noble.__post_init__(self)
161
        Academic.__post_init__(self)
162
        self.get_sex()
163
        self.get_age()
164
        self.get_year_of_birth()
165
166
    def get_sex(self) -> None:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
167
        if "-" in self.first_name:
168
            first_name = self.first_name.split("-")[0]
169
        else:
170
            first_name = self.first_name
171
        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...
172
        gender = d.get_gender(f"{first_name}")
173
        if "female" in gender:
174
            self.gender = "female"
175
        elif "male" in gender:
176
            self.gender = "male"
177
178
    def get_year_of_birth(self) -> None:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
179
        if "." in self.date_of_birth:
180
            self.year_of_birth = self.date_of_birth.split(".")[-1]
181
        elif len(self.date_of_birth.strip()) == 4:
182
            self.year_of_birth = self.date_of_birth
183
            self.date_of_birth = "unknown"
184
185
    def get_age(self) -> None:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
186
        today = datetime.date.today()
187
        if self.date_of_birth != "unknown":
188
            born = str(self.date_of_birth)
189
            if len(born) > 4 and len(born) < 12 and "-" in born:
190
                self.get_yob_and_yod(born)
191
            elif len(born) > 8 and "-" in born:
192
                self.get_dob_and_dod(born)
193
            elif "." in born:
194
                born = born.split(".")[-1]
195
                self.age = str(int(today.year) - int(born.strip()))
196
            else:
197
                self.age = str(int(today.year) - int(born.strip()))
198
199
    def get_yob_and_yod(self, born) -> None:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
200
        self.year_of_death = born.strip()[5:]
201
        self.year_of_birth = born[:4]
202
        self.deceased = True
203
204
    def get_dob_and_dod(self, born) -> None:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
205
        self.date_of_death = born.split("-")[-1].strip()
206
        self.date_of_birth = born.split("-")[0].strip()
207
        self.deceased = True
208
209
210
if __name__ == "__main__":
211
212
    name = Name("Hans Hermann", "Werner")
213
    print(name)
214
215
    noble = Noble("Dagmara", "Bodelschwingh", peer_title="Gräfin von")
216
    print(noble)
217
218
    academic = Academic("Horst Heiner", "Wiekeiner", academic_title="Dr.")  # noqa
219
    print(academic)
220
221
    person_1 = Person(
222
        "Sven", "Rübennase", academic_title="MBA", date_of_birth="1990"
223
    )  # noqa
224
    print(person_1)
225