PyPaf 0.6.0__tar.gz → 0.7.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.
- {pypaf-0.6.0/src/PyPaf.egg-info → pypaf-0.7.0}/PKG-INFO +68 -1
- {pypaf-0.6.0 → pypaf-0.7.0}/README.md +67 -0
- {pypaf-0.6.0 → pypaf-0.7.0/src/PyPaf.egg-info}/PKG-INFO +68 -1
- {pypaf-0.6.0 → pypaf-0.7.0}/src/PyPaf.egg-info/SOURCES.txt +11 -2
- {pypaf-0.6.0 → pypaf-0.7.0}/src/paf/address.py +11 -11
- pypaf-0.7.0/src/paf/attribute.py +43 -0
- pypaf-0.7.0/src/paf/initiator.py +18 -0
- pypaf-0.7.0/src/paf/lineable.py +33 -0
- pypaf-0.7.0/src/paf/premises/attribute.py +39 -0
- pypaf-0.7.0/src/paf/premises/building_type.py +29 -0
- pypaf-0.7.0/src/paf/premises/dependent_premisable.py +34 -0
- pypaf-0.7.0/src/paf/premises/exception.py +51 -0
- pypaf-0.7.0/src/paf/premises/extender.py +24 -0
- pypaf-0.7.0/src/paf/premises/lineable.py +26 -0
- pypaf-0.7.0/src/paf/premises/premisable.py +149 -0
- pypaf-0.7.0/src/paf/premises/premises.py +97 -0
- pypaf-0.7.0/src/paf/premises/rule000.py +37 -0
- pypaf-0.7.0/src/paf/premises/rule001.py +16 -0
- pypaf-0.7.0/src/paf/premises/rule010.py +20 -0
- pypaf-0.7.0/src/paf/premises/rule011.py +16 -0
- pypaf-0.7.0/src/paf/premises/rule101.py +18 -0
- {pypaf-0.6.0 → pypaf-0.7.0}/src/paf/premises/rule110.py +7 -7
- pypaf-0.7.0/src/paf/premises/rule111.py +24 -0
- pypaf-0.7.0/src/paf/premises/split.py +57 -0
- {pypaf-0.6.0 → pypaf-0.7.0}/src/paf/thoroughfare_locality.py +29 -11
- pypaf-0.7.0/src/paf/version.py +3 -0
- {pypaf-0.6.0 → pypaf-0.7.0}/tests/test_empty.py +5 -0
- {pypaf-0.6.0 → pypaf-0.7.0}/tests/test_exception_i.py +5 -0
- {pypaf-0.6.0 → pypaf-0.7.0}/tests/test_exception_ii.py +5 -0
- {pypaf-0.6.0 → pypaf-0.7.0}/tests/test_exception_iii.py +5 -0
- {pypaf-0.6.0 → pypaf-0.7.0}/tests/test_exception_iv.py +83 -0
- {pypaf-0.6.0 → pypaf-0.7.0}/tests/test_mainfile.py +10 -0
- {pypaf-0.6.0 → pypaf-0.7.0}/tests/test_po_box.py +5 -0
- pypaf-0.7.0/tests/test_premises.py +78 -0
- {pypaf-0.6.0 → pypaf-0.7.0}/tests/test_rule_1.py +5 -0
- {pypaf-0.6.0 → pypaf-0.7.0}/tests/test_rule_2.py +5 -0
- {pypaf-0.6.0 → pypaf-0.7.0}/tests/test_rule_3.py +30 -0
- {pypaf-0.6.0 → pypaf-0.7.0}/tests/test_rule_4.py +5 -0
- {pypaf-0.6.0 → pypaf-0.7.0}/tests/test_rule_5.py +12 -0
- {pypaf-0.6.0 → pypaf-0.7.0}/tests/test_rule_6.py +25 -0
- {pypaf-0.6.0 → pypaf-0.7.0}/tests/test_rule_7.py +32 -0
- pypaf-0.6.0/src/paf/attribute.py +0 -66
- pypaf-0.6.0/src/paf/lineable.py +0 -39
- pypaf-0.6.0/src/paf/premises/common.py +0 -38
- pypaf-0.6.0/src/paf/premises/rule000.py +0 -16
- pypaf-0.6.0/src/paf/premises/rule001.py +0 -16
- pypaf-0.6.0/src/paf/premises/rule010.py +0 -55
- pypaf-0.6.0/src/paf/premises/rule011.py +0 -16
- pypaf-0.6.0/src/paf/premises/rule101.py +0 -30
- pypaf-0.6.0/src/paf/premises/rule111.py +0 -30
- pypaf-0.6.0/src/paf/premises_extender.py +0 -27
- pypaf-0.6.0/src/paf/version.py +0 -3
- {pypaf-0.6.0 → pypaf-0.7.0}/LICENSE.txt +0 -0
- {pypaf-0.6.0 → pypaf-0.7.0}/pyproject.toml +0 -0
- {pypaf-0.6.0 → pypaf-0.7.0}/setup.cfg +0 -0
- {pypaf-0.6.0 → pypaf-0.7.0}/src/PyPaf.egg-info/dependency_links.txt +0 -0
- {pypaf-0.6.0 → pypaf-0.7.0}/src/PyPaf.egg-info/top_level.txt +0 -0
- {pypaf-0.6.0 → pypaf-0.7.0}/src/paf/__init__.py +0 -0
- {pypaf-0.6.0 → pypaf-0.7.0}/src/paf/immutable.py +0 -0
- {pypaf-0.6.0 → pypaf-0.7.0}/src/paf/premises/__init__.py +0 -0
- {pypaf-0.6.0 → pypaf-0.7.0}/tests/test_immutability.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: PyPaf
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: Formats the elements of a Royal Mail Postcode Address File entry
|
|
5
5
|
Author-email: John Bard <johnbard@globalnet.co.uk>
|
|
6
6
|
License: MIT
|
|
@@ -25,6 +25,8 @@ Install it from PyPI:
|
|
|
25
25
|
|
|
26
26
|
## Usage
|
|
27
27
|
|
|
28
|
+
### Formatting
|
|
29
|
+
|
|
28
30
|
May be used to format the PAF Address elements as a list of strings:
|
|
29
31
|
|
|
30
32
|
```python
|
|
@@ -98,6 +100,71 @@ address.as_dict()
|
|
|
98
100
|
}
|
|
99
101
|
```
|
|
100
102
|
|
|
103
|
+
### Premises Attributes
|
|
104
|
+
|
|
105
|
+
The `sub_building_name`, `building_name` and `building_number` supplied in the source PAF Address elements need to be parsed according Programmer's Guide rules in order to correctly identify the premises elements of the address.
|
|
106
|
+
|
|
107
|
+
The Address class includes a parsed premises dictionary that contains key-values that may be used to identify the premises within the thoroughfare and the sub-premises within the premises.
|
|
108
|
+
|
|
109
|
+
The parsing decomposes the premises and sub-premises to its constituent parts:
|
|
110
|
+
|
|
111
|
+
| Key | Notes |
|
|
112
|
+
| ------------------- | ----------- |
|
|
113
|
+
| premises_type | If it is of a known type e.g. BLOCK, BUILDING |
|
|
114
|
+
| premises_number | Building number or leading digits of building name |
|
|
115
|
+
| premises_suffix | Non-numeric characters following leading digits of building name |
|
|
116
|
+
| premises_name | Building name, if it cannot be decomposed |
|
|
117
|
+
| sub_premises_type | If it is of a known type e.g. FLAT, UNIT |
|
|
118
|
+
| sub_premises_number | Leading digits of sub-building name |
|
|
119
|
+
| sub_premises_suffix | Non-numeric characters following leading digits of sub-building name |
|
|
120
|
+
| sub_premises_name | Sub-building name, if it cannot be decomposed |
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
import paf
|
|
124
|
+
self.address = paf.Address({
|
|
125
|
+
'sub_building_name': "FLAT 2B",
|
|
126
|
+
'building_name': "THE TOWER",
|
|
127
|
+
'building_number': "27",
|
|
128
|
+
'thoroughfare_name': "JOHN",
|
|
129
|
+
'thoroughfare_descriptor': "STREET",
|
|
130
|
+
'post_town': "WINCHESTER",
|
|
131
|
+
'postcode': "SO23 9AP"
|
|
132
|
+
})
|
|
133
|
+
address.premises()
|
|
134
|
+
|
|
135
|
+
{
|
|
136
|
+
'premises_number': 27,
|
|
137
|
+
'premises_name': 'THE TOWER',
|
|
138
|
+
'sub_premises_type': 'FLAT',
|
|
139
|
+
'sub_premises_number': 2,
|
|
140
|
+
'sub_premises_suffix': 'B',
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
If there are no `sub_building` or `building` elements supplied the `organisation_name` or `po_box_number` elements will be used populate the premises elements, where available.
|
|
145
|
+
|
|
146
|
+
If there is no `sub_building_name` element and the `dependent_thoroughfare` elements are populated the `building` elements will be used to populate the `sub_premises` elements and the `dependent_thoroughfare` elements the `premises` elements.
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
import paf
|
|
150
|
+
self.address = paf.Address({
|
|
151
|
+
'building_name': "1A",
|
|
152
|
+
'dependent_thoroughfare_name': "SEASTONE",
|
|
153
|
+
'dependent_thoroughfare_descriptor': "COURT",
|
|
154
|
+
'thoroughfare_name': "STATION",
|
|
155
|
+
'thoroughfare_descriptor': "ROAD",
|
|
156
|
+
'post_town': "HOLT",
|
|
157
|
+
'postcode': "NR25 7HG"
|
|
158
|
+
})
|
|
159
|
+
address.premises()
|
|
160
|
+
|
|
161
|
+
{
|
|
162
|
+
'premises_name': 'SEASTONE COURT',
|
|
163
|
+
'sub_premises_number': 1,
|
|
164
|
+
'sub_premises_suffix': 'A'
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
101
168
|
## Development
|
|
102
169
|
|
|
103
170
|
After checking out the repo, run `pytest` to run the tests.
|
|
@@ -10,6 +10,8 @@ Install it from PyPI:
|
|
|
10
10
|
|
|
11
11
|
## Usage
|
|
12
12
|
|
|
13
|
+
### Formatting
|
|
14
|
+
|
|
13
15
|
May be used to format the PAF Address elements as a list of strings:
|
|
14
16
|
|
|
15
17
|
```python
|
|
@@ -83,6 +85,71 @@ address.as_dict()
|
|
|
83
85
|
}
|
|
84
86
|
```
|
|
85
87
|
|
|
88
|
+
### Premises Attributes
|
|
89
|
+
|
|
90
|
+
The `sub_building_name`, `building_name` and `building_number` supplied in the source PAF Address elements need to be parsed according Programmer's Guide rules in order to correctly identify the premises elements of the address.
|
|
91
|
+
|
|
92
|
+
The Address class includes a parsed premises dictionary that contains key-values that may be used to identify the premises within the thoroughfare and the sub-premises within the premises.
|
|
93
|
+
|
|
94
|
+
The parsing decomposes the premises and sub-premises to its constituent parts:
|
|
95
|
+
|
|
96
|
+
| Key | Notes |
|
|
97
|
+
| ------------------- | ----------- |
|
|
98
|
+
| premises_type | If it is of a known type e.g. BLOCK, BUILDING |
|
|
99
|
+
| premises_number | Building number or leading digits of building name |
|
|
100
|
+
| premises_suffix | Non-numeric characters following leading digits of building name |
|
|
101
|
+
| premises_name | Building name, if it cannot be decomposed |
|
|
102
|
+
| sub_premises_type | If it is of a known type e.g. FLAT, UNIT |
|
|
103
|
+
| sub_premises_number | Leading digits of sub-building name |
|
|
104
|
+
| sub_premises_suffix | Non-numeric characters following leading digits of sub-building name |
|
|
105
|
+
| sub_premises_name | Sub-building name, if it cannot be decomposed |
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
import paf
|
|
109
|
+
self.address = paf.Address({
|
|
110
|
+
'sub_building_name': "FLAT 2B",
|
|
111
|
+
'building_name': "THE TOWER",
|
|
112
|
+
'building_number': "27",
|
|
113
|
+
'thoroughfare_name': "JOHN",
|
|
114
|
+
'thoroughfare_descriptor': "STREET",
|
|
115
|
+
'post_town': "WINCHESTER",
|
|
116
|
+
'postcode': "SO23 9AP"
|
|
117
|
+
})
|
|
118
|
+
address.premises()
|
|
119
|
+
|
|
120
|
+
{
|
|
121
|
+
'premises_number': 27,
|
|
122
|
+
'premises_name': 'THE TOWER',
|
|
123
|
+
'sub_premises_type': 'FLAT',
|
|
124
|
+
'sub_premises_number': 2,
|
|
125
|
+
'sub_premises_suffix': 'B',
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
If there are no `sub_building` or `building` elements supplied the `organisation_name` or `po_box_number` elements will be used populate the premises elements, where available.
|
|
130
|
+
|
|
131
|
+
If there is no `sub_building_name` element and the `dependent_thoroughfare` elements are populated the `building` elements will be used to populate the `sub_premises` elements and the `dependent_thoroughfare` elements the `premises` elements.
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
import paf
|
|
135
|
+
self.address = paf.Address({
|
|
136
|
+
'building_name': "1A",
|
|
137
|
+
'dependent_thoroughfare_name': "SEASTONE",
|
|
138
|
+
'dependent_thoroughfare_descriptor': "COURT",
|
|
139
|
+
'thoroughfare_name': "STATION",
|
|
140
|
+
'thoroughfare_descriptor': "ROAD",
|
|
141
|
+
'post_town': "HOLT",
|
|
142
|
+
'postcode': "NR25 7HG"
|
|
143
|
+
})
|
|
144
|
+
address.premises()
|
|
145
|
+
|
|
146
|
+
{
|
|
147
|
+
'premises_name': 'SEASTONE COURT',
|
|
148
|
+
'sub_premises_number': 1,
|
|
149
|
+
'sub_premises_suffix': 'A'
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
86
153
|
## Development
|
|
87
154
|
|
|
88
155
|
After checking out the repo, run `pytest` to run the tests.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: PyPaf
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: Formats the elements of a Royal Mail Postcode Address File entry
|
|
5
5
|
Author-email: John Bard <johnbard@globalnet.co.uk>
|
|
6
6
|
License: MIT
|
|
@@ -25,6 +25,8 @@ Install it from PyPI:
|
|
|
25
25
|
|
|
26
26
|
## Usage
|
|
27
27
|
|
|
28
|
+
### Formatting
|
|
29
|
+
|
|
28
30
|
May be used to format the PAF Address elements as a list of strings:
|
|
29
31
|
|
|
30
32
|
```python
|
|
@@ -98,6 +100,71 @@ address.as_dict()
|
|
|
98
100
|
}
|
|
99
101
|
```
|
|
100
102
|
|
|
103
|
+
### Premises Attributes
|
|
104
|
+
|
|
105
|
+
The `sub_building_name`, `building_name` and `building_number` supplied in the source PAF Address elements need to be parsed according Programmer's Guide rules in order to correctly identify the premises elements of the address.
|
|
106
|
+
|
|
107
|
+
The Address class includes a parsed premises dictionary that contains key-values that may be used to identify the premises within the thoroughfare and the sub-premises within the premises.
|
|
108
|
+
|
|
109
|
+
The parsing decomposes the premises and sub-premises to its constituent parts:
|
|
110
|
+
|
|
111
|
+
| Key | Notes |
|
|
112
|
+
| ------------------- | ----------- |
|
|
113
|
+
| premises_type | If it is of a known type e.g. BLOCK, BUILDING |
|
|
114
|
+
| premises_number | Building number or leading digits of building name |
|
|
115
|
+
| premises_suffix | Non-numeric characters following leading digits of building name |
|
|
116
|
+
| premises_name | Building name, if it cannot be decomposed |
|
|
117
|
+
| sub_premises_type | If it is of a known type e.g. FLAT, UNIT |
|
|
118
|
+
| sub_premises_number | Leading digits of sub-building name |
|
|
119
|
+
| sub_premises_suffix | Non-numeric characters following leading digits of sub-building name |
|
|
120
|
+
| sub_premises_name | Sub-building name, if it cannot be decomposed |
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
import paf
|
|
124
|
+
self.address = paf.Address({
|
|
125
|
+
'sub_building_name': "FLAT 2B",
|
|
126
|
+
'building_name': "THE TOWER",
|
|
127
|
+
'building_number': "27",
|
|
128
|
+
'thoroughfare_name': "JOHN",
|
|
129
|
+
'thoroughfare_descriptor': "STREET",
|
|
130
|
+
'post_town': "WINCHESTER",
|
|
131
|
+
'postcode': "SO23 9AP"
|
|
132
|
+
})
|
|
133
|
+
address.premises()
|
|
134
|
+
|
|
135
|
+
{
|
|
136
|
+
'premises_number': 27,
|
|
137
|
+
'premises_name': 'THE TOWER',
|
|
138
|
+
'sub_premises_type': 'FLAT',
|
|
139
|
+
'sub_premises_number': 2,
|
|
140
|
+
'sub_premises_suffix': 'B',
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
If there are no `sub_building` or `building` elements supplied the `organisation_name` or `po_box_number` elements will be used populate the premises elements, where available.
|
|
145
|
+
|
|
146
|
+
If there is no `sub_building_name` element and the `dependent_thoroughfare` elements are populated the `building` elements will be used to populate the `sub_premises` elements and the `dependent_thoroughfare` elements the `premises` elements.
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
import paf
|
|
150
|
+
self.address = paf.Address({
|
|
151
|
+
'building_name': "1A",
|
|
152
|
+
'dependent_thoroughfare_name': "SEASTONE",
|
|
153
|
+
'dependent_thoroughfare_descriptor': "COURT",
|
|
154
|
+
'thoroughfare_name': "STATION",
|
|
155
|
+
'thoroughfare_descriptor': "ROAD",
|
|
156
|
+
'post_town': "HOLT",
|
|
157
|
+
'postcode': "NR25 7HG"
|
|
158
|
+
})
|
|
159
|
+
address.premises()
|
|
160
|
+
|
|
161
|
+
{
|
|
162
|
+
'premises_name': 'SEASTONE COURT',
|
|
163
|
+
'sub_premises_number': 1,
|
|
164
|
+
'sub_premises_suffix': 'A'
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
101
168
|
## Development
|
|
102
169
|
|
|
103
170
|
After checking out the repo, run `pytest` to run the tests.
|
|
@@ -9,12 +9,19 @@ src/paf/__init__.py
|
|
|
9
9
|
src/paf/address.py
|
|
10
10
|
src/paf/attribute.py
|
|
11
11
|
src/paf/immutable.py
|
|
12
|
+
src/paf/initiator.py
|
|
12
13
|
src/paf/lineable.py
|
|
13
|
-
src/paf/premises_extender.py
|
|
14
14
|
src/paf/thoroughfare_locality.py
|
|
15
15
|
src/paf/version.py
|
|
16
16
|
src/paf/premises/__init__.py
|
|
17
|
-
src/paf/premises/
|
|
17
|
+
src/paf/premises/attribute.py
|
|
18
|
+
src/paf/premises/building_type.py
|
|
19
|
+
src/paf/premises/dependent_premisable.py
|
|
20
|
+
src/paf/premises/exception.py
|
|
21
|
+
src/paf/premises/extender.py
|
|
22
|
+
src/paf/premises/lineable.py
|
|
23
|
+
src/paf/premises/premisable.py
|
|
24
|
+
src/paf/premises/premises.py
|
|
18
25
|
src/paf/premises/rule000.py
|
|
19
26
|
src/paf/premises/rule001.py
|
|
20
27
|
src/paf/premises/rule010.py
|
|
@@ -22,6 +29,7 @@ src/paf/premises/rule011.py
|
|
|
22
29
|
src/paf/premises/rule101.py
|
|
23
30
|
src/paf/premises/rule110.py
|
|
24
31
|
src/paf/premises/rule111.py
|
|
32
|
+
src/paf/premises/split.py
|
|
25
33
|
tests/test_empty.py
|
|
26
34
|
tests/test_exception_i.py
|
|
27
35
|
tests/test_exception_ii.py
|
|
@@ -30,6 +38,7 @@ tests/test_exception_iv.py
|
|
|
30
38
|
tests/test_immutability.py
|
|
31
39
|
tests/test_mainfile.py
|
|
32
40
|
tests/test_po_box.py
|
|
41
|
+
tests/test_premises.py
|
|
33
42
|
tests/test_rule_1.py
|
|
34
43
|
tests/test_rule_2.py
|
|
35
44
|
tests/test_rule_3.py
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
"""PAF Address"""
|
|
2
2
|
|
|
3
3
|
# Tried using dataclasses.dataclass(frozen=True) decorator for immutablity but did not work"""
|
|
4
|
+
from .initiator import attribute_init
|
|
5
|
+
from .attribute import AttributeMixin
|
|
4
6
|
from .immutable import ImmutableMixin
|
|
5
7
|
from .lineable import LineableMixin
|
|
8
|
+
from .thoroughfare_locality import ThoroughfareLocalityMixin
|
|
9
|
+
from .premises.premises import Premises
|
|
6
10
|
|
|
7
|
-
class Address(ImmutableMixin, LineableMixin):
|
|
11
|
+
class Address(ImmutableMixin, AttributeMixin, ThoroughfareLocalityMixin, LineableMixin):
|
|
8
12
|
"""Main PAF Address class"""
|
|
9
13
|
|
|
10
|
-
|
|
14
|
+
@attribute_init
|
|
15
|
+
def __init__(self, *args):
|
|
11
16
|
"""Initialise Address elements"""
|
|
12
|
-
|
|
13
|
-
object.__setattr__(self, key, '')
|
|
14
|
-
for key, val in args.items():
|
|
15
|
-
if hasattr(self, key):
|
|
16
|
-
object.__setattr__(self, key, val)
|
|
17
|
-
self.extend_premises()
|
|
17
|
+
object.__setattr__(self, 'premises', Premises(*args))
|
|
18
18
|
|
|
19
19
|
def __repr__(self):
|
|
20
20
|
"""Return full representation of an Address"""
|
|
21
|
-
args = {k: getattr(self, k) for k in self.
|
|
21
|
+
args = {k: getattr(self, k) for k in list(self.attrs) if getattr(self, k, None)}
|
|
22
22
|
return self.__class__.__name__ + '(' + str(args) + ')'
|
|
23
23
|
|
|
24
24
|
def __str__(self):
|
|
@@ -30,9 +30,9 @@ class Address(ImmutableMixin, LineableMixin):
|
|
|
30
30
|
|
|
31
31
|
def __iter__(self):
|
|
32
32
|
"""Return Address as iterable"""
|
|
33
|
-
yield from self.lines
|
|
33
|
+
yield from list(self.lines)
|
|
34
34
|
if not self.is_empty('postcode'):
|
|
35
|
-
yield from [getattr(self, 'postcode')]
|
|
35
|
+
yield from list([getattr(self, 'postcode')])
|
|
36
36
|
|
|
37
37
|
def as_str(self):
|
|
38
38
|
"""Return Address as string"""
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Attribute Mixin"""
|
|
2
|
+
|
|
3
|
+
class AttributeMixin():
|
|
4
|
+
"""Address elements and derived properties"""
|
|
5
|
+
|
|
6
|
+
@classmethod
|
|
7
|
+
@property
|
|
8
|
+
def premises_attrs(cls):
|
|
9
|
+
"""Returns Paf premises elements"""
|
|
10
|
+
return (
|
|
11
|
+
'organisation_name', 'department_name',
|
|
12
|
+
'sub_building_name', 'building_name', 'building_number',
|
|
13
|
+
'po_box_number'
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
@classmethod
|
|
17
|
+
@property
|
|
18
|
+
def post_attrs(cls):
|
|
19
|
+
"""Returns Paf post elements"""
|
|
20
|
+
return ('post_town', 'postcode')
|
|
21
|
+
|
|
22
|
+
@classmethod
|
|
23
|
+
@property
|
|
24
|
+
def other_attrs(cls):
|
|
25
|
+
"""Returns Paf other elements"""
|
|
26
|
+
return ('concatenation_indicator', 'udprn')
|
|
27
|
+
|
|
28
|
+
@classmethod
|
|
29
|
+
@property
|
|
30
|
+
def attrs(cls):
|
|
31
|
+
"""Returns all Paf address elements"""
|
|
32
|
+
return(
|
|
33
|
+
cls.premises_attrs
|
|
34
|
+
+ cls.dependent_thoroughfare_attrs
|
|
35
|
+
+ cls.thoroughfare_attrs
|
|
36
|
+
+ cls.locality_attrs
|
|
37
|
+
+ cls.post_attrs
|
|
38
|
+
+ cls.other_attrs
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
def is_empty(self, attr):
|
|
42
|
+
"""Returns if attribute value is empty"""
|
|
43
|
+
return getattr(self, attr, '') == ''
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Initiator Decorator"""
|
|
2
|
+
|
|
3
|
+
import functools
|
|
4
|
+
|
|
5
|
+
def attribute_init(func):
|
|
6
|
+
"""Decorator to initiate an object based on a list of attributes"""
|
|
7
|
+
|
|
8
|
+
@functools.wraps(func)
|
|
9
|
+
def wrapper(self, *args, **kwargs):
|
|
10
|
+
"""Initiate properties from specified dict"""
|
|
11
|
+
for key in list(getattr(self, 'attrs', [])):
|
|
12
|
+
object.__setattr__(self, key, '')
|
|
13
|
+
for key, val in args[0].items():
|
|
14
|
+
if hasattr(self, key):
|
|
15
|
+
object.__setattr__(self, key, str(val))
|
|
16
|
+
return func(self, *args, **kwargs)
|
|
17
|
+
|
|
18
|
+
return wrapper
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Lineable Mixin"""
|
|
2
|
+
|
|
3
|
+
from itertools import chain
|
|
4
|
+
|
|
5
|
+
class LineableMixin():
|
|
6
|
+
"""Converts Paf address elements into list of address lines"""
|
|
7
|
+
|
|
8
|
+
@classmethod
|
|
9
|
+
@property
|
|
10
|
+
def optional_lines_attrs(cls):
|
|
11
|
+
"""Returns optional address line attributes"""
|
|
12
|
+
return ('thoroughfares_and_localities',)
|
|
13
|
+
|
|
14
|
+
@classmethod
|
|
15
|
+
@property
|
|
16
|
+
def lines_attrs(cls):
|
|
17
|
+
"""Returns optional address line attributes and post_town"""
|
|
18
|
+
return cls.optional_lines_attrs + ('post_town',)
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def optional_lines(self):
|
|
22
|
+
"""Returns address lines, excluding post_town and postcode"""
|
|
23
|
+
return self.premises.lines + self._lines(self.optional_lines_attrs)
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def lines(self):
|
|
27
|
+
"""Returns address lines, excluding postcode"""
|
|
28
|
+
return self.premises.lines + self._lines(self.lines_attrs)
|
|
29
|
+
|
|
30
|
+
def _lines(self, attrs):
|
|
31
|
+
"""Returns list of address lines from specified attributes"""
|
|
32
|
+
lines = list(filter(None, [getattr(self, k, None) for k in attrs]))
|
|
33
|
+
return list(chain(*[line if isinstance(line, list) else [line] for line in lines]))
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Attribute Mixin"""
|
|
2
|
+
|
|
3
|
+
class AttributeMixin():
|
|
4
|
+
"""Premises elements and derived properties"""
|
|
5
|
+
|
|
6
|
+
@classmethod
|
|
7
|
+
@property
|
|
8
|
+
def organisation_attrs(cls):
|
|
9
|
+
"""Returns Paf organisation elements"""
|
|
10
|
+
return ('organisation_name', 'department_name')
|
|
11
|
+
|
|
12
|
+
@classmethod
|
|
13
|
+
@property
|
|
14
|
+
def building_attrs(cls):
|
|
15
|
+
"""Returns Paf building elements"""
|
|
16
|
+
return ('sub_building_name', 'building_name', 'building_number')
|
|
17
|
+
|
|
18
|
+
@classmethod
|
|
19
|
+
@property
|
|
20
|
+
def other_attrs(cls):
|
|
21
|
+
"""Returns Paf other elements"""
|
|
22
|
+
return ('po_box_number', 'concatenation_indicator')
|
|
23
|
+
|
|
24
|
+
@classmethod
|
|
25
|
+
@property
|
|
26
|
+
def attrs(cls):
|
|
27
|
+
"""Returns all Paf premises elements"""
|
|
28
|
+
return(
|
|
29
|
+
cls.organisation_attrs
|
|
30
|
+
+ cls.building_attrs
|
|
31
|
+
+ cls.dependent_thoroughfare_attrs
|
|
32
|
+
+ cls.thoroughfare_attrs
|
|
33
|
+
+ cls.locality_attrs
|
|
34
|
+
+ cls.other_attrs
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
def is_empty(self, attr):
|
|
38
|
+
"""Returns if attribute value is empty"""
|
|
39
|
+
return getattr(self, attr, '') == ''
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Building Types"""
|
|
2
|
+
|
|
3
|
+
from .split import SplitMixin
|
|
4
|
+
|
|
5
|
+
class BuildingTypeMixin(SplitMixin):
|
|
6
|
+
"""Determines if a string represents a known building type"""
|
|
7
|
+
|
|
8
|
+
@classmethod
|
|
9
|
+
@property
|
|
10
|
+
def known_building_types(cls):
|
|
11
|
+
"""Returns known building types"""
|
|
12
|
+
return (
|
|
13
|
+
"BACK OF", "BLOCK", "BLOCKS", "BUILDING", "MAISONETTE", "MAISONETTES", "REAR OF",
|
|
14
|
+
"SHOP", "SHOPS", "STALL", "STALLS", "SUITE", "SUITES", "UNIT", "UNITS", "PO BOX"
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
@classmethod
|
|
18
|
+
@property
|
|
19
|
+
def known_sub_building_types(cls):
|
|
20
|
+
"""Returns known sub-building types"""
|
|
21
|
+
return cls.known_building_types + ("FLAT", "FLATS")
|
|
22
|
+
|
|
23
|
+
def is_known_building_type(self, attr='building_name'):
|
|
24
|
+
"""Returns if attribute starts with a known type"""
|
|
25
|
+
return self.but_last_word(attr) in self.known_building_types
|
|
26
|
+
|
|
27
|
+
def is_known_sub_building_type(self, attr='sub_building_name'):
|
|
28
|
+
"""Returns if attribute starts with a known type"""
|
|
29
|
+
return self.but_last_word(attr) in self.known_sub_building_types
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""Dependent Premisable Mixin"""
|
|
2
|
+
|
|
3
|
+
from .premisable import PremisableMixin
|
|
4
|
+
|
|
5
|
+
class DependentPremisableMixin(PremisableMixin):
|
|
6
|
+
"""Returns the values for the premises properties"""
|
|
7
|
+
|
|
8
|
+
@property
|
|
9
|
+
def _premises_number(self):
|
|
10
|
+
"""Returns premises number"""
|
|
11
|
+
if getattr(self, 'dependent_thoroughfare', '') != '':
|
|
12
|
+
return ''
|
|
13
|
+
return super()._premises_number
|
|
14
|
+
|
|
15
|
+
@property
|
|
16
|
+
def _premises_name(self):
|
|
17
|
+
"""Returns premises name"""
|
|
18
|
+
if getattr(self, 'dependent_thoroughfare', '') != '':
|
|
19
|
+
return getattr(self, 'dependent_thoroughfare', '')
|
|
20
|
+
return super()._premises_name
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def _sub_premises_number(self):
|
|
24
|
+
"""Returns sub-premises number"""
|
|
25
|
+
if getattr(self, 'dependent_thoroughfare', '') != '':
|
|
26
|
+
return super()._premises_number
|
|
27
|
+
return super()._sub_premises_number
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def _sub_premises_name(self):
|
|
31
|
+
"""Returns sub-premises name"""
|
|
32
|
+
if getattr(self, 'dependent_thoroughfare', '') != '':
|
|
33
|
+
return super()._premises_name
|
|
34
|
+
return super()._sub_premises_name
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Exceptions"""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from .building_type import BuildingTypeMixin
|
|
5
|
+
from .split import SplitMixin
|
|
6
|
+
|
|
7
|
+
class ExceptionMixin(BuildingTypeMixin, SplitMixin):
|
|
8
|
+
"""Exceptions"""
|
|
9
|
+
|
|
10
|
+
@classmethod
|
|
11
|
+
def __is_exception_i(cls, val):
|
|
12
|
+
"""Returns if first and last characters are numeric"""
|
|
13
|
+
return re.fullmatch(r'^[\d](?:.*[\d])?$', val)
|
|
14
|
+
|
|
15
|
+
@classmethod
|
|
16
|
+
def __is_exception_ii(cls, val):
|
|
17
|
+
"""Returns if first and penultimate characters are numeric, and last is alphabetic"""
|
|
18
|
+
return re.fullmatch(r'^([\d][a-zA-Z]|[\d].*?[\d][a-zA-Z])$', val)
|
|
19
|
+
|
|
20
|
+
@classmethod
|
|
21
|
+
def __is_exception_iii(cls, val):
|
|
22
|
+
"""Returns if single non-whitespace character"""
|
|
23
|
+
return re.fullmatch(r'^[^ \t\r\n\v\f]$', val)
|
|
24
|
+
|
|
25
|
+
@classmethod
|
|
26
|
+
def __is_exception(cls, val):
|
|
27
|
+
"""Returns if value is an exception"""
|
|
28
|
+
return (
|
|
29
|
+
cls.__is_exception_i(val)
|
|
30
|
+
or cls.__is_exception_ii(val)
|
|
31
|
+
or cls.__is_exception_iii(val)
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
def __is_exception_iv(self, attr): # pylint: disable=unused-argument
|
|
35
|
+
"""Returns if value starts with a known building type
|
|
36
|
+
and ends with numeric range or numeric alpha suffix"""
|
|
37
|
+
# Do not include suffix check as does not account for values such as BLOCK B
|
|
38
|
+
return self.is_known_building_type(attr)
|
|
39
|
+
# and re.match(r'^\d', splitstring.last_word(getattr(self, attr, None)))
|
|
40
|
+
|
|
41
|
+
def is_exception(self, attr):
|
|
42
|
+
"""Returns if attribute is an exception"""
|
|
43
|
+
return self.__is_exception(getattr(self, attr, None))
|
|
44
|
+
|
|
45
|
+
def is_split_exception(self, attr):
|
|
46
|
+
"""Returns if attribute should be split"""
|
|
47
|
+
return (
|
|
48
|
+
self.__is_exception(self.last_word(attr))
|
|
49
|
+
and not self.last_word(attr).isdigit()
|
|
50
|
+
and not self.__is_exception_iv(attr)
|
|
51
|
+
)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Premises Extender Mixin"""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from .rule000 import Rule000 # pylint: disable=unused-import
|
|
5
|
+
from .rule001 import Rule001 # pylint: disable=unused-import
|
|
6
|
+
from .rule010 import Rule010 # pylint: disable=unused-import
|
|
7
|
+
from .rule011 import Rule011 # pylint: disable=unused-import
|
|
8
|
+
from .rule101 import Rule101 # pylint: disable=unused-import
|
|
9
|
+
from .rule110 import Rule110 # pylint: disable=unused-import
|
|
10
|
+
from .rule111 import Rule111 # pylint: disable=unused-import
|
|
11
|
+
|
|
12
|
+
class ExtenderMixin():
|
|
13
|
+
"""Dynamic Premises processing"""
|
|
14
|
+
|
|
15
|
+
@property
|
|
16
|
+
def rule(self):
|
|
17
|
+
"""Returns premises rule class"""
|
|
18
|
+
rule = ''.join(['0' if self.is_empty(k) else '1' for k in self.building_attrs])
|
|
19
|
+
return getattr(sys.modules[__name__], 'Rule' + rule)
|
|
20
|
+
|
|
21
|
+
def extend(self):
|
|
22
|
+
"""Dynamically extends instance with appropriate rule"""
|
|
23
|
+
base_cls = self.__class__
|
|
24
|
+
object.__setattr__(self, '__class__', type(base_cls.__name__, (base_cls, self.rule), {}))
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Lineable Mixin"""
|
|
2
|
+
|
|
3
|
+
from itertools import chain
|
|
4
|
+
|
|
5
|
+
class LineableMixin():
|
|
6
|
+
"""Converts Paf address premises elements into list of lines"""
|
|
7
|
+
|
|
8
|
+
@property
|
|
9
|
+
def lines_attrs(self):
|
|
10
|
+
"""Returns premises line attributes"""
|
|
11
|
+
return self.organisation_attrs + ('po_box',) + self.rule_attrs
|
|
12
|
+
|
|
13
|
+
@property
|
|
14
|
+
def lines(self):
|
|
15
|
+
"""Returns premises lines"""
|
|
16
|
+
return self._lines(self.lines_attrs)
|
|
17
|
+
|
|
18
|
+
@property
|
|
19
|
+
def po_box(self):
|
|
20
|
+
"""Returns PO Box"""
|
|
21
|
+
return '' if self.is_empty('po_box_number') else f"PO BOX {getattr(self, 'po_box_number')}"
|
|
22
|
+
|
|
23
|
+
def _lines(self, attrs):
|
|
24
|
+
"""Returns list of premises lines from specified attributes"""
|
|
25
|
+
lines = list(filter(None, [getattr(self, k, None) for k in attrs]))
|
|
26
|
+
return list(chain(*[line if isinstance(line, list) else [line] for line in lines]))
|