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

Politician.party_is_in_parties()   A

Complexity

Conditions 3

Size

Total Lines 13
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 12
dl 0
loc 13
rs 9.8
c 0
b 0
f 0
cc 3
nop 4
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 os
7
import sys
8
from dataclasses import dataclass, field
9
from typing import List, Optional
10
11
PACKAGE_PARENT = ".."
12
SCRIPT_DIR = os.path.dirname(
13
    os.path.realpath(os.path.join(os.getcwd(), os.path.expanduser(__file__)))
14
)  # isort:skip # noqa # pylint: disable=wrong-import-position
15
sys.path.append(
16
    os.path.normpath(os.path.join(SCRIPT_DIR, PACKAGE_PARENT))
17
)  # isort: skip # noqa # pylint: disable=wrong-import-position
18
19
from roles.person import Person  # type: ignore  # noqa
20
from roles.resources import helpers  # type: ignore  # noqa
21
from roles.resources.constants import GERMAN_PARTIES  # type: ignore  # noqa
22
from roles.resources.constants import PEER_PREPOSITIONS  # type: ignore # noqa
0 ignored issues
show
Unused Code introduced by
Unused PEER_PREPOSITIONS imported from roles.resources.constants
Loading history...
23
from roles.resources.constants import PEERTITLES  # type: ignore # noqa
0 ignored issues
show
Unused Code introduced by
Unused PEERTITLES imported from roles.resources.constants
Loading history...
24
from roles.resources.helpers import (  # type: ignore # noqa
0 ignored issues
show
Unused Code introduced by
Unused NotInRange imported from roles.resources.helpers
Loading history...
Unused Code introduced by
Unused TooManyFirstNames 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 "_Politician_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 _Politician_default:
34
    electoral_ward: str = field(default="ew")
35
    ward_no: Optional[int] = field(default=None)
36
    voter_count: Optional[int] = field(default=None)
37
    minister: Optional[str] = field(default=None)
38
    offices: List[str] = field(default_factory=lambda: [])
39
    parties: List[str] = field(default_factory=lambda: [])
40
41
    def renamed_wards(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
42
        wards = {
43
            "Kreis Aachen I": "Aachen III",
44
            "Hochsauerlandkreis II – Soest III": "Hochsauerlandkreis II",
45
            "Kreis Aachen II": "Aachen IV"
46
            if self.last_name in ["Wirtz", "Weidenhaupt"]
0 ignored issues
show
Coding Style introduced by
Wrong continued indentation (add 19 spaces).
Loading history...
Bug introduced by
The Instance of _Politician_default does not seem to have a member named last_name.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
47
            else "Kreis Aachen I",
0 ignored issues
show
Coding Style introduced by
Wrong continued indentation (add 19 spaces).
Loading history...
48
        }
49
        if self.electoral_ward in wards.keys():
50
            self.electoral_ward = wards[self.electoral_ward]
51
52
    def scrape_wiki_for_ward(self) -> None:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
53
        import requests
0 ignored issues
show
introduced by
Import outside toplevel (requests)
Loading history...
introduced by
Unable to import 'requests'
Loading history...
54
        from bs4 import BeautifulSoup  # type: ignore
0 ignored issues
show
introduced by
Unable to import 'bs4'
Loading history...
introduced by
Import outside toplevel (bs4.BeautifulSoup)
Loading history...
55
56
        URL_base = "https://de.wikipedia.org/wiki/Landtagswahlkreis_{}"
0 ignored issues
show
Coding Style Naming introduced by
Variable name "URL_base" 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...
57
        URL = URL_base.format(self.electoral_ward)
0 ignored issues
show
Coding Style Naming introduced by
Variable name "URL" 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...
58
        req = requests.get(URL)
59
        bsObj = BeautifulSoup(req.text, "lxml")
0 ignored issues
show
Coding Style Naming introduced by
Variable name "bsObj" 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...
60
        table = bsObj.find(class_="infobox float-right toptextcells")
61
        self.scrape_wiki_table_for_ward(table)
62
63
    def scrape_wiki_table_for_ward(self, table) -> None:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
64
        for td in table.find_all("td"):
0 ignored issues
show
Coding Style Naming introduced by
Variable name "td" 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...
65
            if "Wahlkreisnummer" in td.text:
66
                ward_no = td.find_next().text.strip()
67
                ward_no = ward_no.split(" ")[0]
68
                self.ward_no = int(ward_no)
69
            elif "Wahlberechtigte" in td.text:
70
                voter_count = td.find_next().text.strip()
71
                voter_count = self.fix_voter_count(voter_count)
72
                self.voter_count = int(voter_count)
73
74
    def fix_voter_count(self, voter_count):
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...
75
        if voter_count[-1] == "]":
76
            voter_count = voter_count[:-3]
77
        if " " in voter_count:
78
            voter_count = "".join(voter_count.split(" "))
79
        else:
80
            voter_count = "".join(voter_count.split("."))
81
        return voter_count
82
83
84
@dataclass
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
best-practice introduced by
Too many ancestors (12/7)
Loading history...
85
class Politician(
86
    _Politician_default,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
87
    helpers._Party_default,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
Coding Style Best Practice introduced by
It seems like _Party_default was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
88
    Person,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
89
    helpers._Party_base,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
Coding Style Best Practice introduced by
It seems like _Party_base was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
90
    AttrDisplay,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
91
):
92
    def __post_init__(self):
93
        Party.__post_init__(self)
94
        Person.__post_init__(self)
95
        Person.get_sex(self)
96
        Person.get_age(self)
97
        self.change_ward()
98
        if self.party_name in GERMAN_PARTIES:
99
            self.parties.append(
100
                Party(self.party_name, self.party_entry, self.party_exit)
101
            )
102
        if self.minister and self.minister not in self.offices:
103
            self.offices.append(self.minister)
104
105
    def add_Party(
0 ignored issues
show
Coding Style Naming introduced by
Method name "add_Party" 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...
introduced by
Missing function or method docstring
Loading history...
106
        self, party_name, party_entry="unknown", party_exit="unknown"
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
107
    ):  # noqa
108
        if party_name in GERMAN_PARTIES:
109
            if self.party_is_in_parties(party_name, party_entry, party_exit):
110
                pass
111
            else:
112
                self.parties.append(Party(party_name, party_entry, party_exit))
113
                self.party_name = party_name
0 ignored issues
show
Coding Style introduced by
The attribute party_name was defined outside __init__.

It is generally a good practice to initialize all attributes to default values in the __init__ method:

class Foo:
    def __init__(self, x=None):
        self.x = x
Loading history...
114
                self.party_entry = party_entry
115
                self.party_exit = party_exit
116
117
    def align_party_entries(
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
        self, party, party_name, party_entry, party_exit
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
Unused Code introduced by
The argument party_name seems to be unused.
Loading history...
119
    ) -> Party:  # noqa
120
        if party_entry != "unknown" and party.party_entry == "unknown":
121
            party.party_entry = party_entry
122
        if party_exit != "unknown" and party.party_exit == "unknown":
123
            party.party_exit = party_exit
124
        return party
125
126
    def party_is_in_parties(self, party_name, party_entry, party_exit):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
127
        parties_tmp = self.parties[:]
128
        for party in parties_tmp:
129
            if party_name == party.party_name:
130
                party_updated = self.align_party_entries(
131
                    party, party_name, party_entry, party_exit
132
                )
133
                self.parties.remove(party)
134
                self.parties.append(party_updated)
135
                self.party_entry = party_updated.party_entry
136
                self.party_exit = party_updated.party_exit
137
                return True
138
        return False
139
140
    def change_ward(self, ward=None):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
141
        if ward:
142
            self.electoral_ward = ward
143
        if self.electoral_ward not in ["ew", "Landesliste"]:
144
            self.renamed_wards()
145
            self.scrape_wiki_for_ward()
146
        else:
147
            self.electoral_ward = "ew"
148
149
150
if __name__ == "__main__":
151
152
    politician = Politician(
153
        "SPD",
154
        "Bärbel",
155
        "Gutherz",
156
        academic_title="Dr.",
157
        date_of_birth="1980",
158
        electoral_ward="Köln I",
159
    )
160
    print(politician)
161