-
Notifications
You must be signed in to change notification settings - Fork 0
/
OpeningHoursBot.py
executable file
·168 lines (144 loc) · 5.55 KB
/
OpeningHoursBot.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#!/usr/bin/env python
# encoding: utf-8
# Helper script to fix common mistakes in opening_hours like values in OSM
# automatically.
# For discussion see:
# http://permalink.gmane.org/gmane.comp.gis.openstreetmap.tagging/16810
# modules {{{
import logging
import sys
import re
import xml.etree.ElementTree as ET
sys.path.append('pyopening_hours/')
from pyopening_hours import OpeningHours, ParseException
# }}}
class OpeningHoursBot:
def fix_xml_object(self, osm_xml_root):
"""Fix the given OSM XML object using the defended rule set."""
osm_xml = osm_xml_root.getroot()
logging.info(
u"Parsing OSM file version '{}' generated by '{}' …".format(
osm_xml.attrib['version'],
osm_xml.attrib['generator']
)
)
for node in osm_xml_root.findall('node'):
self._for_object(node)
for way in osm_xml_root.findall('way'):
self._for_object(way)
def _fix_ruleset_1_time_error(self, wrong_val):
""" '0930-0630' -> '09:30-06:30'
Use with caution. Does conflict with goal b) (as defined here:
http://permalink.gmane.org/gmane.comp.gis.openstreetmap.tagging/16810)
Should not be enabled because the time could be specified in 12 hours
format or in 24 hours format (which is the recommended format:
http://wiki.openstreetmap.org/wiki/Key:opening_hours:specification).
"""
regex = re.compile(r'\A(?P<day_list>(?:(?:Mo|Tu|We|Th|Fr|Sa|Su)-?){2}\s*|)(?P<start_hour>[0-1][0-9]|2[0-4])(?P<start_min>[:1-5][0-9]|0[0-9])\s*(?P<sep>-)\s*(?P<end_hour>[0-1][0-9]|2[0-4])(?P<end_min>[:1-5][0-9]|0[0-9])\Z')
re_object = re.search(regex, wrong_val)
if not re_object:
return wrong_val
return '{}{}:{}{}{}:{}'.format(
re_object.group('day_list'),
re_object.group('start_hour'), re_object.group('start_min'),
re_object.group('sep'),
re_object.group('end_hour'), re_object.group('end_min')
)
def _fix_ruleset_2(self, wrong_val):
"""
Changes: 'nach vereinbarung' -> '"nach Vereinbarung"'
(most often used value for this).
"""
regex = re.compile(r'\A(?P<start_word>(?:Termin|nur) |)nach vereinbarung\Z', flags=re.IGNORECASE)
re_object = re.search(regex, wrong_val)
if not re_object:
return wrong_val
return '"{}nach Vereinbarung"'.format(
re_object.group('start_word') if re_object.group('start_word') else ''
)
fixing_functions = [
# _fix_ruleset_1_time_error,
_fix_ruleset_2,
]
# helper functions {{{
def _is_opening_hours_ok(self, value):
"""Returns True if there are no warnings and False if OpeningHours raises
an exception. It will return None in all other cases."""
error = False
try:
oh = OpeningHours(value)
except ParseException:
error = True
if not error and not oh.getWarnings():
if value != oh.prettifyValue():
logging.error(
"OpeningHoursBot did generate an opening_hours value ('{}') which does not match the prettified value.".format(value)
)
return True
elif error:
return False
else:
return None
# }}}
# do the work {{{
def _fix_opening_hours(self, tag):
"""Fix value of the tag with functions in fixing_functions list.
:param tag: XML tag node.
:returns: Boolean which is True if fixing was successful.
"""
value = tag.attrib['v']
if self._is_opening_hours_ok(value) is False:
correct_value = value
for fix_oh_val_func in self.fixing_functions:
correct_value = fix_oh_val_func(self, value)
if value != correct_value:
break
if value != correct_value:
if self._is_opening_hours_ok(correct_value):
logging.debug(
u"Fixed value '{}' -> '{}'".format(
value,
correct_value
)
)
tag.set('v', correct_value)
return True
else:
logging.critical(
"OpeningHoursBot did generate a wrong opening_hours value ('{}' -> '{}')".format(
value,
correct_value
)
)
return False
def _for_object(self, node):
"""Called for each object (node, way)."""
fixing_successful = None
for tag in node.findall('tag'):
if tag.attrib['k'] == 'opening_hours':
fixing_successful = self._fix_opening_hours(tag)
if fixing_successful:
node.set('action', 'modify')
# }}}
def main(osm_file):
logging.basicConfig(
format='%(levelname)s: %(message)s',
level=logging.DEBUG,
# level=logging.INFO,
)
if osm_file[-4:] == '.osm':
osm_file_basename = osm_file[:-4]
osm_xml_root = ET.parse(osm_file)
oh_bot = OpeningHoursBot()
oh_bot.fix_xml_object(osm_xml_root)
osm_xml_root.write('{}-fixed.osm'.format(osm_file_basename))
if __name__ == '__main__':
if len(sys.argv) > 1:
osm_file = sys.argv[1]
else:
logging.error(
'Not enough parameters.'
+ ' 1. File path OSM file.'
)
sys.exit(1)
main(osm_file)