pywmdr 0.2.dev2__py3-none-any.whl → 0.2.dev3__py3-none-any.whl

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.
pywmdr/bundle.py CHANGED
@@ -19,6 +19,8 @@
19
19
  #
20
20
  ###############################################################################
21
21
 
22
+ import csv
23
+ import io
22
24
  import logging
23
25
  from pathlib import Path
24
26
  import shutil
@@ -38,6 +40,8 @@ TEMPDIR2 = Path(tempfile.TemporaryDirectory().name)
38
40
 
39
41
  WMDR2_FILES = get_userdir() / 'wmdr2'
40
42
  WMDR2_FILES_TEMP = TEMPDIR2 / 'wmdr2'
43
+ WMDS_DIR = get_userdir() / 'wmds'
44
+ WMDS_DIR_TEMP = TEMPDIR2 / 'wmds'
41
45
 
42
46
 
43
47
  @click.group()
@@ -61,6 +65,21 @@ def sync(ctx, verbosity):
61
65
  with json_schema.open('wb') as fh:
62
66
  fh.write(urlopen_(f'{WMDR2_SCHEMA}').read())
63
67
 
68
+ LOGGER.debug('Caching codelists')
69
+ LOGGER.debug(f'Downloading WMDS codelists to {WMDS_DIR_TEMP}')
70
+ WMDS_DIR_TEMP.mkdir(parents=True, exist_ok=True)
71
+ WMDS_CODELISTS_LIST = 'https://codes.wmo.int/wmdr?_format=csv&_view=with_metadata&status=valid' # noqa
72
+ codelists = urlopen_(WMDS_CODELISTS_LIST).read().decode('utf-8')
73
+ codelists_fh = io.StringIO(codelists.strip())
74
+ reader = csv.reader(codelists_fh)
75
+ for row in reader:
76
+ codelist_url = f'https://codes.wmo.int/wmdr/{row[1]}?_format=csv&_view=with_metadata&status=valid' # noqa
77
+ LOGGER.debug(f'Downloading {codelist_url}')
78
+ codelist_file = WMDS_DIR_TEMP / f'{row[1]}.csv'
79
+ LOGGER.debug(f'Writing codelist {codelist_file}')
80
+ with codelist_file.open('wb') as fh:
81
+ fh.write(urlopen_(codelist_url).read())
82
+
64
83
  LOGGER.debug(f'Removing {USERDIR}')
65
84
  if USERDIR.exists():
66
85
  shutil.rmtree(USERDIR)
pywmdr/codelists.py ADDED
@@ -0,0 +1,122 @@
1
+ ##############################################################################
2
+ #
3
+ # Licensed to the Apache Software Foundation (ASF) under one
4
+ # or more contributor license agreements. See the NOTICE file
5
+ # distributed with this work for additional information
6
+ # regarding copyright ownership. The ASF licenses this file
7
+ # to you under the Apache License, Version 2.0 (the
8
+ # "License"); you may not use this file except in compliance
9
+ # with the License. You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing,
14
+ # software distributed under the License is distributed on an
15
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16
+ # KIND, either express or implied. See the License for the
17
+ # specific language governing permissions and limitations
18
+ # under the License.
19
+ #
20
+ ###############################################################################
21
+
22
+ import csv
23
+ import logging
24
+
25
+ from pywmdr.util import get_userdir
26
+
27
+ LOGGER = logging.getLogger(__name__)
28
+
29
+ CODELISTS_MAP = {
30
+ 'facilityType': {
31
+ 'codelists': ['FacilityType.csv']
32
+ },
33
+ 'wmoRegion': {
34
+ 'codelists': ['WMORegion.csv']
35
+ },
36
+ 'territory': {
37
+ 'codelists': ['TerritoryName.csv']
38
+ },
39
+ 'observedGeometry': {
40
+ 'codelists': ['Geometry.csv']
41
+ },
42
+ 'observedProperty': {
43
+ 'codelists': [
44
+ 'ObservedVariableAtmosphere.csv',
45
+ 'ObservedVariableEarth.csv',
46
+ 'ObservedVariableOcean.csv',
47
+ 'ObservedVariableOuterSpace.csv',
48
+ 'ObservedVariableTerrestrial.csv'
49
+ ]
50
+ },
51
+ 'programAffiliation': {
52
+ 'codelists': ['ProgramAffiliation.csv']
53
+ },
54
+ 'reportingStatus': {
55
+ 'codelists': ['ReportingStatus.csv']
56
+ },
57
+ 'observingMethod': {
58
+ 'codelists': [
59
+ 'ObservingMethodAtmosphere.csv',
60
+ 'ObservingMethodTerrestrial.csv',
61
+ 'ObservingMethodOcean.csv'
62
+ ]
63
+ },
64
+ 'operatingStatus': {
65
+ 'codelists': ['InstrumentOperatingStatus.csv']
66
+ },
67
+ 'sourceOfObservation': {
68
+ 'codelists': ['SourceOfObservation.csv']
69
+ },
70
+ 'referenceSurface': {
71
+ 'codelists': ['ReferenceSurfaceType.csv']
72
+ },
73
+ 'unit': {
74
+ 'codelists': ['unit.csv']
75
+ },
76
+ }
77
+
78
+
79
+ class WMDSCodelists:
80
+ def __init__(self):
81
+ """initializer"""
82
+
83
+ self.codelists = {}
84
+ for key, value in CODELISTS_MAP.items():
85
+ self.codelists[key] = []
86
+ for codelist in value['codelists']:
87
+ filename = get_userdir() / 'wmds' / codelist
88
+ with filename.open() as fh:
89
+ reader = csv.DictReader(fh)
90
+ for row in reader:
91
+ self.codelists[key].append(row['@notation'])
92
+
93
+ def is_valid(self, pname: str, pvalue: dict | None) -> bool:
94
+ """
95
+ Helper function to determine whether a concept is valid
96
+ (WMDS codelist or null)
97
+
98
+ :param pname: name of property/object
99
+ :param value: value of property/object
100
+
101
+ :returns: `bool` of whether a concept is valid
102
+ """
103
+
104
+ if pname not in self.codelists:
105
+ msg = f'{pname} not found in codelists'
106
+ LOGGER.error(msg)
107
+ raise ValueError(msg)
108
+
109
+ if pvalue is None:
110
+ LOGGER.debug('Value is null')
111
+ return True
112
+
113
+ LOGGER.debug(f'Property name: {pname}')
114
+ LOGGER.debug(f'Property value: {pvalue}')
115
+ LOGGER.debug(f'Codelist: {self.codelists[pname]}')
116
+ if pvalue['id'] in self.codelists[pname]:
117
+ return True
118
+
119
+ return False
120
+
121
+ def __repr__(self):
122
+ return '<WMDSCodelists>'
pywmdr/pygeoapi_plugin.py CHANGED
@@ -60,7 +60,7 @@ LOGGER = logging.getLogger(__name__)
60
60
  with (THISDIR / 'resources' / 'ets-report.json').open() as fh:
61
61
  ETS_REPORT_SCHEMA = json.load(fh)
62
62
 
63
- with (THISDIR / 'resources' / '20250504_0-20008-0-NRB.json').open() as fh:
63
+ with (THISDIR / 'resources' / '0-20008-0-THE.json').open() as fh:
64
64
  EXAMPLE_WMDR2 = json.load(fh)
65
65
 
66
66
 
@@ -0,0 +1,176 @@
1
+ {
2
+ "type": "Feature",
3
+ "id": "0-20008-0-THE",
4
+ "geometry": {
5
+ "type": "Point",
6
+ "coordinates": [
7
+ 22.956,
8
+ 40.634,
9
+ 60
10
+ ]
11
+ },
12
+ "time": {
13
+ "interval": [
14
+ "1982-03-13",
15
+ ".."
16
+ ]
17
+ },
18
+ "conformsTo": [
19
+ "http://wigos.wmo.int/spec/wmdr/2/conf/core"
20
+ ],
21
+ "properties": {
22
+ "type": "facility",
23
+ "facilityType": {
24
+ "id": "landFixed"
25
+ },
26
+ "title": "Thessaloniki",
27
+ "created": "2024-12-11T00:00:00Z",
28
+ "updated": "2024-12-11T00:00:00Z",
29
+ "description": "The Brewer spectrophotometers and all radiation instruments are located on the roof of the 4-storey building of the Physics Dept., of the Aristotle University of Thessaloniki. The Campus is located in the city center about 500 m east of the seashore. The highest elevation of about 1100 m is at a distance of about 15 km towards the east. Thessaloniki is the second largest city of Greece with about 1000000 inhabitants in the greater metropolitan area.",
30
+ "contacts": [
31
+ {
32
+ "identifier": "contact:aristotle-university-of-thessaloniki--ed7b7e4a",
33
+ "organization": "Aristotle University of Thessaloniki",
34
+ "roles": [
35
+ "owner"
36
+ ],
37
+ "links": [
38
+ {
39
+ "rel": "about",
40
+ "href": "https://lapweb.physics.auth.gr",
41
+ "type": "text/html"
42
+ }
43
+ ]
44
+ },
45
+ {
46
+ "identifier": "contact:clima@hnms.gr",
47
+ "name": "Mrs M. Michelaraki",
48
+ "organization": "Hellenic National Meteorological Service",
49
+ "roles": [
50
+ "pointOfContact"
51
+ ],
52
+ "links": [
53
+ {
54
+ "rel": "about",
55
+ "href": "https://www.emy.gr",
56
+ "type": "text/html"
57
+ }
58
+ ]
59
+ },
60
+ {
61
+ "identifier": "contact:abais@auth.gr",
62
+ "name": "Alkiviadis Bais",
63
+ "organization": "Aristotle University of Thessaloniki",
64
+ "roles": [
65
+ "principalInvestigator"
66
+ ],
67
+ "links": [
68
+ {
69
+ "rel": "about",
70
+ "href": "https://lapweb.physics.auth.gr",
71
+ "type": "text/html"
72
+ }
73
+ ]
74
+ },
75
+ {
76
+ "identifier": "contact:balis@auth.gr",
77
+ "name": "Dimitris Balis",
78
+ "organization": "Aristotle University of Thessaloniki",
79
+ "roles": [
80
+ "principalInvestigator"
81
+ ],
82
+ "links": [
83
+ {
84
+ "rel": "about",
85
+ "href": "https://lapweb.physics.auth.gr",
86
+ "type": "text/html"
87
+ }
88
+ ]
89
+ }
90
+ ],
91
+ "wmoRegion": {
92
+ "id": "europe"
93
+ },
94
+ "territories": [
95
+ {
96
+ "dates": [
97
+ "2011-02-16",
98
+ ".."
99
+ ],
100
+ "territory": {"id": "GRC"}
101
+ }
102
+ ],
103
+ "observations": [
104
+ {
105
+ "observedProperty": {
106
+ "id": "12164"
107
+ },
108
+ "programAffiliations": [
109
+ {
110
+ "dates": [
111
+ "1982-03-13",
112
+ ".."
113
+ ],
114
+ "programAffiliation": {
115
+ "id": "GAWregional"
116
+ },
117
+ "reportingStatus": {
118
+ "id": "operational"
119
+ }
120
+ },
121
+ {
122
+ "dates": [
123
+ "2008-01-01",
124
+ "2024-12-11"
125
+ ],
126
+ "programAffiliation": {
127
+ "id": "GALION"
128
+ },
129
+ "reportingStatus": {
130
+ "id": "operational"
131
+ }
132
+ },
133
+ {
134
+ "dates": [
135
+ "2003-06-01",
136
+ "2024-12-11"
137
+ ],
138
+ "programAffiliation": {
139
+ "id": "AERONET"
140
+ },
141
+ "reportingStatus": {
142
+ "id": "operational"
143
+ }
144
+ }
145
+ ],
146
+ "configurations": [
147
+ {
148
+ "id": "configuration-456",
149
+ "time": {
150
+ "interval": [
151
+ "1982-03-13",
152
+ ".."
153
+ ]
154
+ },
155
+ "observingMethod": {
156
+ "id": "78"
157
+ },
158
+ "sourceOfObservation": {
159
+ "id": "automaticReading"
160
+ },
161
+ "instrument": {
162
+ "id": "instrument-789"
163
+ }
164
+ }
165
+ ]
166
+ }
167
+ ]
168
+ },
169
+ "links": [
170
+ {
171
+ "href": "https://lapweb.physics.auth.gr/en/infrastructure/#Infrastructure",
172
+ "rel": "about",
173
+ "type": "text/html"
174
+ }
175
+ ]
176
+ }
pywmdr/wmdr2/ets.py CHANGED
@@ -27,6 +27,7 @@ from jsonschema import FormatChecker
27
27
  from jsonschema.validators import Draft202012Validator
28
28
 
29
29
  from pywmdr.bundle import WMDR2_FILES
30
+ from pywmdr.codelists import WMDSCodelists
30
31
  from pywmdr.errors import TestSuiteError
31
32
  from pywmdr.util import get_current_datetime_rfc3339, get_package_version
32
33
 
@@ -42,7 +43,7 @@ def gen_test_id(test_id: str) -> str:
42
43
  :returns: test identifier as URI
43
44
  """
44
45
 
45
- return f'http://wis.wmo.int/spec/wmdr/2/conf/core/{test_id}'
46
+ return f'http://wigos.wmo.int/spec/wmdr/2/conf/core/{test_id}'
46
47
 
47
48
 
48
49
  class WMDR2TestSuite:
@@ -57,6 +58,7 @@ class WMDR2TestSuite:
57
58
 
58
59
  self.version = get_package_version()
59
60
  self.errors = []
61
+ self.codelists = WMDSCodelists()
60
62
  self.record = record
61
63
 
62
64
  def run_tests(self):
@@ -144,6 +146,73 @@ class WMDR2TestSuite:
144
146
 
145
147
  return status
146
148
 
149
+ def test_requirement_codelisted_values(self):
150
+ """
151
+ Test property values against required codelists
152
+ """
153
+
154
+ validation_errors = []
155
+
156
+ status = {
157
+ 'id': gen_test_id('codelisted_values'),
158
+ 'code': 'PASSED'
159
+ }
160
+
161
+ codelisted_values = []
162
+
163
+ LOGGER.debug('Validating against codelisted values')
164
+
165
+ codelisted_values.append(
166
+ ('facilityType', [self.record['properties']['facilityType']]))
167
+
168
+ codelisted_values.append(
169
+ ('wmoRegion', [self.record['properties']['wmoRegion']]))
170
+
171
+ for territory in self.record['properties'].get('territories', []):
172
+ codelisted_values.append(('territory', [territory['territory']]))
173
+
174
+ for observation in self.record['properties']['observations']:
175
+ codelisted_values.append(
176
+ ('observedGeometry', [observation.get('observedGeometry')]))
177
+ codelisted_values.append(
178
+ ('observedProperty', [observation['observedProperty']]))
179
+
180
+ for program_affiliation in observation.get('programAffiliations'):
181
+ codelisted_values.append(
182
+ ('programAffiliation', [program_affiliation['programAffiliation']])) # noqa
183
+ codelisted_values.append(
184
+ ('reportingStatus', [program_affiliation.get('reportingStatus')])) # noqa
185
+
186
+ for configuration in observation['configurations']:
187
+ codelisted_values.append(
188
+ ('observingMethod', [configuration.get('observingMethod')])) # noqa
189
+ codelisted_values.append(
190
+ ('operatingStatus', [configuration.get('operatingStatus')])) # noqa
191
+ codelisted_values.append(
192
+ ('sourceOfObservation', [configuration.get('sourceOfObservation')])) # noqa
193
+
194
+ vertical_distance = configuration.get('verticalDistance')
195
+
196
+ if vertical_distance is not None:
197
+ codelisted_values.append(
198
+ ('unit', [vertical_distance['unit']]))
199
+ codelisted_values.append(
200
+ ('referenceSurface', [vertical_distance['referenceSurface']])) # noqa
201
+
202
+ for codelisted_value in codelisted_values:
203
+ LOGGER.debug(f'Testing {codelisted_value[0]} against {codelisted_value[1]}') # noqa
204
+ for codelisted_value2 in codelisted_value[1]:
205
+ if not self.codelists.is_valid(codelisted_value[0], codelisted_value2): # noqa
206
+ msg = f'Invalid codelist value for {codelisted_value[0]}'
207
+ validation_errors.append(msg)
208
+
209
+ if validation_errors:
210
+ status['code'] = 'FAILED'
211
+ status['message'] = f'{len(validation_errors)} error(s)'
212
+ status['errors'] = validation_errors
213
+
214
+ return status
215
+
147
216
  def raise_for_status(self):
148
217
  """
149
218
  Raise error if one or more failures were found during validation.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pywmdr
3
- Version: 0.2.dev2
3
+ Version: 0.2.dev3
4
4
  Summary: A Python implementation of the test suite for WIGOS Metadata Record
5
5
  Author-email: Tom Kralidis <tomkralidis@gmail.com>
6
6
  Maintainer-email: Tom Kralidis <tomkralidis@gmail.com>
@@ -0,0 +1,18 @@
1
+ pywmdr/__init__.py,sha256=nmM2-nevDwLyTfthzuef-AJ8kMnWxX7Nb_gMXJ3ZL00,1318
2
+ pywmdr/bundle.py,sha256=VgkDwwsftqbpBXKJORwjzl6oqST5LVB-6K0vCKOZRqo,3190
3
+ pywmdr/cli_options.py,sha256=N70OlGeonJvkVNdDoh-X606J1JCUWnKwpa5gduoJ_aM,1533
4
+ pywmdr/codelists.py,sha256=UMYqy4Tk8GLulqMjE3xouZMWCUpnW1qHbuwZ1CiACpA,3694
5
+ pywmdr/errors.py,sha256=Uu2J6vmTjaWuNJaW-mtdZ_KCztc9x4wjaLRVDZP6Lk0,1176
6
+ pywmdr/ets.py,sha256=I1JeheXMCsPtRXjf5ay8I3MZFrrsDtj5k0XT68XCOOY,2076
7
+ pywmdr/pygeoapi_plugin.py,sha256=WJek0RGL18RjnPY4kemrSNSQtQo7CVQOBPM7Xb6kqh0,4440
8
+ pywmdr/util.py,sha256=LE_Ds6OuG7vNQxjFQcogWiPD5CNQyHhqIXeW6TNXJZE,2639
9
+ pywmdr/resources/0-20008-0-THE.json,sha256=luAjCrxbnwq4x0nwqm2vyx8d5rXmkMeGwVCQ2w-gaXg,5873
10
+ pywmdr/resources/ets-report.json,sha256=VwZohaD_EloSlwhYXxnj08Z3ckfbFYudLc2dWwxvlus,2243
11
+ pywmdr/wmdr2/__init__.py,sha256=VK88WG3wpt5RqksGgutMnr02OhUlksWcoAnoeSRRWic,1035
12
+ pywmdr/wmdr2/ets.py,sha256=QzXUVcGROmYcSsO7QbPcsEibz3gmaWK2FJfHbDoUUa0,7817
13
+ pywmdr-0.2.dev3.dist-info/licenses/LICENSE.md,sha256=Q8E0TXZpH75ydPcip7DXr21Y3uwJNgQUFv3ipMXwPkw,839
14
+ pywmdr-0.2.dev3.dist-info/METADATA,sha256=0KrWF4dlxa-nW0PsMwU_gazhJDYyA-FwRZhINUlt2uA,5104
15
+ pywmdr-0.2.dev3.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
16
+ pywmdr-0.2.dev3.dist-info/entry_points.txt,sha256=Ga7GlkDlThWwprQDc5OawyWtV_RT9FXilgXjwKacynU,38
17
+ pywmdr-0.2.dev3.dist-info/top_level.txt,sha256=_Zdw0CS7tVYk-CJZmu9xEqIzOuY98RsI6CUMh8Kh818,7
18
+ pywmdr-0.2.dev3.dist-info/RECORD,,
@@ -1,94 +0,0 @@
1
- {
2
- "id": "urn:wmo:md:ke-meteo:0-20008-0-NRB",
3
- "type": "Feature",
4
- "conformsTo": [
5
- "http://wis.wmo.int/spec/wmdr/2/conf/core"
6
- ],
7
- "geometry": {
8
- "type": "Point",
9
- "coordinates": [
10
- 36.75919,
11
- -1.30169,
12
- 1795.0
13
- ]
14
- },
15
- "properties": {
16
- "created": "1996-01-01T00:00:00Z",
17
- "wmo:dataPolicy": "core",
18
- "contacts": [
19
- {
20
- "name": "World Meteorological Organization WMO and Federal Office for Meteorology and Climatology MeteoSwiss",
21
- "organization": "World Meteorological Organization WMO and Federal Office for Meteorology and Climatology MeteoSwiss",
22
- "emails": [
23
- {
24
- "value": "oscar@wmo.int"
25
- }
26
- ],
27
- "addresses": [
28
- {
29
- "deliveryPoint": [
30
- "7bis, avenue de la Paix"
31
- ],
32
- "city": "Geneva",
33
- "postalCode": "CH-1211",
34
- "country": "Switzerland"
35
- }
36
- ],
37
- "contactInstructions": "email",
38
- "links": [
39
- {
40
- "rel": "canonical",
41
- "type": "text/html",
42
- "href": "https://oscar.wmo.int/surface"
43
- }
44
- ],
45
- "roles": [
46
- "host"
47
- ]
48
- },
49
- {
50
- "name": "Waweru,Amos,Mr",
51
- "organization": "Kenyan Meteorological Department",
52
- "phones": [
53
- {
54
- "value": "+254723521586"
55
- }
56
- ],
57
- "emails": [
58
- {
59
- "value": "waweru_k@yahoo.com"
60
- },
61
- {
62
- "value": "amoskamau1969@gmail.com"
63
- }
64
- ],
65
- "addresses": [
66
- {
67
- "deliveryPoint": [
68
- "Kenya Meteorological Department 30259\nNairobi \nKenya"
69
- ],
70
- "city": "Nairobi",
71
- "administrativeArea": "NB",
72
- "postalCode": "00100",
73
- "country": "Kenya"
74
- }
75
- ],
76
- "contactInstructions": "africa",
77
- "roles": [
78
- "processor",
79
- "producer"
80
- ]
81
- }
82
- ]
83
- },
84
- "links": [
85
- {
86
- "rel": "license",
87
- "href": "https://creativecommons.org/licenses/by/4.0/"
88
- },
89
- {
90
- "rel": "stations",
91
- "href": "http://www.meteo.go.ke/obsv/ozone.html"
92
- }
93
- ]
94
- }
@@ -1,17 +0,0 @@
1
- pywmdr/__init__.py,sha256=nmM2-nevDwLyTfthzuef-AJ8kMnWxX7Nb_gMXJ3ZL00,1318
2
- pywmdr/bundle.py,sha256=bwqO-pVVeF2shVBKMj2lwgKuVeXbmHPEsU4o-jutCfk,2276
3
- pywmdr/cli_options.py,sha256=N70OlGeonJvkVNdDoh-X606J1JCUWnKwpa5gduoJ_aM,1533
4
- pywmdr/errors.py,sha256=Uu2J6vmTjaWuNJaW-mtdZ_KCztc9x4wjaLRVDZP6Lk0,1176
5
- pywmdr/ets.py,sha256=I1JeheXMCsPtRXjf5ay8I3MZFrrsDtj5k0XT68XCOOY,2076
6
- pywmdr/pygeoapi_plugin.py,sha256=Ib8RH3fh62p4ajAOhk1f73etqXkBsnDtYUqVgjzvh_k,4449
7
- pywmdr/util.py,sha256=LE_Ds6OuG7vNQxjFQcogWiPD5CNQyHhqIXeW6TNXJZE,2639
8
- pywmdr/resources/20250504_0-20008-0-NRB.json,sha256=gvSgRDp4U_Oh5Wyu54a3IhcOvaxg-cpjrNNALyOGhR4,2958
9
- pywmdr/resources/ets-report.json,sha256=VwZohaD_EloSlwhYXxnj08Z3ckfbFYudLc2dWwxvlus,2243
10
- pywmdr/wmdr2/__init__.py,sha256=VK88WG3wpt5RqksGgutMnr02OhUlksWcoAnoeSRRWic,1035
11
- pywmdr/wmdr2/ets.py,sha256=QI89slTlQabmcTXjSmPmaZzQ7Bi0A9YwDCvkNQNj7qs,4859
12
- pywmdr-0.2.dev2.dist-info/licenses/LICENSE.md,sha256=Q8E0TXZpH75ydPcip7DXr21Y3uwJNgQUFv3ipMXwPkw,839
13
- pywmdr-0.2.dev2.dist-info/METADATA,sha256=bHCu19864TYcyGh6HiGCoDnY87_fEw5WMOlkBvoZI-4,5104
14
- pywmdr-0.2.dev2.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
15
- pywmdr-0.2.dev2.dist-info/entry_points.txt,sha256=Ga7GlkDlThWwprQDc5OawyWtV_RT9FXilgXjwKacynU,38
16
- pywmdr-0.2.dev2.dist-info/top_level.txt,sha256=_Zdw0CS7tVYk-CJZmu9xEqIzOuY98RsI6CUMh8Kh818,7
17
- pywmdr-0.2.dev2.dist-info/RECORD,,