Skip to content

Commit 0637b5e

Browse files
committed
Initial stab at converting geocoding data into Python data structure
1 parent ff2bcf2 commit 0637b5e

2 files changed

Lines changed: 146 additions & 1 deletion

File tree

python/buildgeocodingdata.py

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
#!/usr/bin/env python
2+
"""Script to read the libphonenumber geocoding metadata and generate Python code.
3+
4+
Invocation:
5+
buildgeocodingdata.py indir outfile
6+
7+
Processes all of the geocoding data under the given input directory and emit
8+
generated Python code.
9+
"""
10+
11+
# Based on original geocoding data files from libphonenumber:
12+
# resources/geocoding/*/*.txt
13+
# Copyright (C) 2011 Google Inc.
14+
#
15+
# Licensed under the Apache License, Version 2.0 (the "License");
16+
# you may not use this file except in compliance with the License.
17+
# You may obtain a copy of the License at
18+
#
19+
# http://www.apache.org/licenses/LICENSE-2.0
20+
#
21+
# Unless required by applicable law or agreed to in writing, software
22+
# distributed under the License is distributed on an "AS IS" BASIS,
23+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24+
# See the License for the specific language governing permissions and
25+
# limitations under the License.
26+
#
27+
import os
28+
import sys
29+
import glob
30+
import re
31+
import datetime
32+
33+
GEODATA_SUFFIX = ".txt"
34+
BLANK_LINE_RE = re.compile(ur'^\s*$', re.UNICODE)
35+
COMMENT_LINE_RE = re.compile(ur'^\s*#.*$', re.UNICODE)
36+
DATA_LINE_RE = re.compile(ur'^(?P<prefix>\d+)\|(?P<location>.*)$', re.UNICODE)
37+
38+
# Boilerplate header
39+
GEODATA_FILE_PROLOG = '''"""Geocoding data, mapping each prefix to a dict of locale:locationname.
40+
41+
Auto-generated file, do not edit by hand.
42+
"""
43+
'''
44+
45+
# Copyright notice covering the XML metadata; include current year.
46+
COPYRIGHT_NOTICE = """# Copyright (C) 2011-%s Google Inc.
47+
#
48+
# Licensed under the Apache License, Version 2.0 (the "License");
49+
# you may not use this file except in compliance with the License.
50+
# You may obtain a copy of the License at
51+
#
52+
# http://www.apache.org/licenses/LICENSE-2.0
53+
#
54+
# Unless required by applicable law or agreed to in writing, software
55+
# distributed under the License is distributed on an "AS IS" BASIS,
56+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
57+
# See the License for the specific language governing permissions and
58+
# limitations under the License.
59+
""" % datetime.datetime.now().year
60+
61+
62+
def load_geodata_file(geodata, filename, locale, overall_prefix):
63+
"""Load geocoding data from the given file, for the given locale and prefix.
64+
65+
We assume that this file:
66+
- is encoded in UTF-8
67+
- may have comment lines (starting with #) and blank lines
68+
- has data lines of the form '<prefix>|<location_name>'
69+
- contains only data for prefixes that are extensions of the filename.
70+
"""
71+
with open(filename) as infile:
72+
lineno = 0
73+
for line in infile:
74+
uline = line.decode('utf-8')
75+
lineno += 1
76+
dm = DATA_LINE_RE.match(uline)
77+
if dm:
78+
prefix = dm.group('prefix')
79+
location = dm.group('location')
80+
if not prefix.startswith(overall_prefix):
81+
raise Exception("%s:%d: Prefix %s is not within %s" %
82+
(filename, lineno, prefix, overall_prefix))
83+
if prefix not in geodata:
84+
geodata[prefix] = {}
85+
geodata[prefix][locale] = location
86+
elif BLANK_LINE_RE.match(uline):
87+
pass
88+
elif COMMENT_LINE_RE.match(uline):
89+
pass
90+
else:
91+
raise Exception("%s:%d: Unexpected line format: %s" %
92+
(filename, lineno, line))
93+
94+
95+
def load_geodata(indir):
96+
"""Load geocoding data from the given top-level directory.
97+
98+
Geocoding data is assumed to be held in files <indir>/<locale>/<prefix>.txt.
99+
The same prefix may occur in multiple files, giving the location's name in
100+
different locales.
101+
"""
102+
geodata = {} # prefix => dict mapping location to location name
103+
for locale in os.listdir(indir):
104+
if not os.path.isdir(os.path.join(indir, locale)):
105+
continue
106+
for filename in glob.glob(os.path.join(indir, locale, "*%s" % GEODATA_SUFFIX)):
107+
overall_prefix, ext = os.path.splitext(os.path.basename(filename))
108+
load_geodata_file(geodata, filename, locale, overall_prefix)
109+
return geodata
110+
111+
112+
def output_geodata_code(geodata, outfilename):
113+
"""Output the geocoding data in Python form to the given file """
114+
with open(outfilename, "wb") as outfile:
115+
longest_prefix = 0
116+
print >> outfile, GEODATA_FILE_PROLOG
117+
print >> outfile, COPYRIGHT_NOTICE
118+
print >> outfile, "GEOCODE_DATA = {"
119+
for prefix in geodata:
120+
if len(prefix) > longest_prefix:
121+
longest_prefix = len(prefix)
122+
print >> outfile, " '%s': %r," % (prefix, geodata[prefix])
123+
print >> outfile, "}"
124+
print >> outfile, "GEOCODE_LONGEST_PREFIX = %d" % longest_prefix
125+
126+
127+
def _standalone(argv):
128+
"""Parse the given input directory and emit generated code."""
129+
if len(argv) != 2:
130+
print >> sys.stderr, __doc__
131+
sys.exit(1)
132+
geodata = load_geodata(argv[0])
133+
output_geodata_code(geodata, argv[1])
134+
135+
136+
if __name__ == "__main__":
137+
_standalone(sys.argv[1:])

python/makefile

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11

2+
phonenumbers/geodata/__init__.py: buildgeocodingdata.py ../resources/geocoding
3+
python buildgeocodingdata.py ../resources/geocoding $@
4+
5+
tests/testgeodata/__init__.py: buildgeocodingdata.py ../resources/test/geocoding
6+
python buildgeocodingdata.py ../resources/test/geocoding $@
7+
8+
geodata: phonenumbers/geodata/__init__.py tests/testgeodata/__init__.py
9+
210
phonenumbers/data/__init__.py: ../resources/PhoneNumberMetaData.xml buildmetadatafromxml.py
311
python buildmetadatafromxml.py ../resources/PhoneNumberMetaData.xml phonenumbers/data
412

5-
metadata: phonenumbers/data/__init__.py tests/testdata/__init__.py
13+
metadata: phonenumbers/data/__init__.py tests/testdata/__init__.py geodata
614

715
tests/testdata/__init__.py: ../resources/PhoneNumberMetaDataForTesting.xml buildmetadatafromxml.py
816
python buildmetadatafromxml.py ../resources/PhoneNumberMetaDataForTesting.xml tests/testdata

0 commit comments

Comments
 (0)