Skip to content

Commit e4eb3fb

Browse files
committed
refactoring/testing/adding help system
1 parent 1309c00 commit e4eb3fb

45 files changed

Lines changed: 1088 additions & 523 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@
66
/__pycache__/
77
/dist/
88
MANIFEST
9+
cover
910
.coverage
10-
coverage.xml
11+
coverage.xml

geosupport/__init__.py

Lines changed: 1 addition & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -1,115 +1 @@
1-
from functools import partial
2-
import sys
3-
from .work_area_layouts import format_input, parse_output, GeosupportError
4-
5-
FUNCTION_ALT_NAMES = {
6-
'1': [],
7-
'1a': [],
8-
'1b': ['address'],
9-
'1e': [],
10-
'1n': ['street_name_to_street_code', 'get_street_code'],
11-
'2': ['intersection'],
12-
'2w': ['intersections', 'intersection_wide'],
13-
'3': ['street_segment'],
14-
'3c': ['blockface'],
15-
'3s': ['street_stretch'],
16-
'ap': ['address_point'],
17-
'bb': ['browse_back'],
18-
'bf': ['browse_forward'],
19-
'bl': ['bbl', 'tax_lot', 'lot'],
20-
'bn': ['bin', 'building'],
21-
'd': ['get_street_name'],
22-
'dg': [],
23-
'dn': [],
24-
'n*': ['normalize_street_name']
25-
}
26-
27-
FUNCTIONS = {}
28-
29-
for function, alt_names in FUNCTION_ALT_NAMES.items():
30-
FUNCTIONS[function] = function
31-
FUNCTIONS[function.upper()] = function
32-
for name in alt_names:
33-
FUNCTIONS[name] = function
34-
FUNCTIONS[name.upper()] = function
35-
36-
class Geosupport(object):
37-
def __init__(self):
38-
self.py_version = sys.version_info[0]
39-
try:
40-
self.platform = sys.platform
41-
if sys.maxsize > 2 ** 32:
42-
self.py_bit = '64'
43-
else:
44-
self.py_bit = '32'
45-
if self.platform == 'win32':
46-
if self.py_bit == '64':
47-
from ctypes import cdll
48-
self.geolib = cdll.LoadLibrary("NYCGEO.dll")
49-
else:
50-
from ctypes import windll # must use windll for 32-bit Windows binaries
51-
self.geolib = windll.LoadLibrary("NYCGEO.dll")
52-
elif self.platform == 'linux' or self.platform == 'linux2':
53-
import os
54-
from ctypes import cdll
55-
self.geolib = cdll.LoadLibrary(os.path.join(os.environ['LD_LIBRARY_PATH'], "libgeo.so"))
56-
else:
57-
raise Exception('This Operating System is currently not supported.')
58-
except OSError as e:
59-
sys.exit(
60-
'{}\n'
61-
'You are currently using a {}-bit Python interpreter. Is the installed '
62-
'version of Geosupport {}-bit?'.format(e, self.py_bit, self.py_bit))
63-
64-
def _call_geolib(self, flags, wa1, wa2):
65-
"""
66-
Calls the Geosupport libs & encodes/deocodes strings for Python 3.
67-
:param wa1: Work Area 1
68-
:param wa2: Work Area 2
69-
:param func: Function that determines the dictionary to return.
70-
:return: Dictionary of results
71-
"""
72-
73-
# encode
74-
if self.py_version == 3:
75-
wa1 = bytes(str(wa1), 'utf8')
76-
wa2 = bytes(str(wa2), 'utf8')
77-
78-
# Call Geosupport libs
79-
if self.platform == 'win32':
80-
self.geolib.NYCgeo(wa1, wa2) # windows
81-
else:
82-
self.geolib.geo(wa1, wa2) # linux
83-
84-
# decode
85-
if self.py_version == 3:
86-
wa1 = str(wa1, 'utf8')
87-
wa2 = str(wa2, 'utf8')
88-
89-
#print(wa1)
90-
#print(wa2)
91-
92-
return parse_output(flags, wa1, wa2)
93-
94-
def __getattr__(self, name):
95-
if name in FUNCTIONS:
96-
return partial(self.call, function=FUNCTIONS[name])
97-
98-
raise AttributeError("'%s' object has no attribute '%s'" %(
99-
self.__class__.__name__, name
100-
))
101-
102-
def __getitem__(self, name):
103-
return self.__getattr__(name)
104-
105-
def call(self, kwargs_dict={}, **kwargs):
106-
kwargs_dict.update(kwargs)
107-
flags, wa1, wa2 = format_input(kwargs_dict)
108-
result = self._call_geolib(flags, wa1, wa2)
109-
return_code = result['Geosupport Return Code (GRC)']
110-
if not return_code.isdigit() or int(return_code) > 1:
111-
raise GeosupportError(
112-
result['Message'] + ' ' + result['Message 2'],
113-
result
114-
)
115-
return result
1+
from geosupport.geosupport import Geosupport

geosupport/config.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from os import path
2+
3+
FUNCTION_INFO_PATH = path.join(
4+
path.abspath(path.dirname(__file__)),
5+
'function_info'
6+
)
7+
8+
FUNCTION_INFO_CSV = path.join(FUNCTION_INFO_PATH, 'function_info.csv')
9+
FUNCTION_INPUTS_CSV = path.join(FUNCTION_INFO_PATH, 'function_inputs.csv')
10+
WORK_AREA_LAYOUTS_PATH = path.join(FUNCTION_INFO_PATH, 'work_area_layouts')
11+
12+
BOROUGHS = {
13+
'MANHATTAN': 1, 'MN': 1, 'NEW YORK': 1, 'NY': 1,
14+
'BRONX': 2, 'THE BRONX': 2, 'BX': 2,
15+
'BROOKLYN': 3, 'BK': 3, 'BKLYN': 3, 'KINGS': 3,
16+
'QUEENS': 4, 'QN': 4, 'QU': 4,
17+
'STATEN ISLAND': 5, 'SI': 5, 'STATEN IS': 5, 'RICHMOND': 5,
18+
'': '',
19+
}

geosupport/error.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
class GeosupportError(Exception):
2+
def __init__(self, message, result={}):
3+
super(GeosupportError, self).__init__(message)
4+
self.result = result

geosupport/function_info.py

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
from csv import DictReader
2+
import glob
3+
from os import path
4+
5+
from geosupport.config import FUNCTION_INFO_CSV, FUNCTION_INPUTS_CSV, WORK_AREA_LAYOUTS_PATH
6+
7+
MODES = ['regular', 'extended', 'long', 'long+tpad']
8+
AUXILIARY_SEGMENT_LENGTH = 500
9+
10+
class FunctionDict(dict):
11+
12+
def __init__(self):
13+
super(FunctionDict, self).__init__()
14+
self.alt_names = {}
15+
16+
def __getitem__(self, name):
17+
name = str(name).strip().upper()
18+
if self.alt_names and name in self.alt_names:
19+
name = self.alt_names[name]
20+
21+
return super(FunctionDict, self).__getitem__(name)
22+
23+
def __contains__(self, name):
24+
name = str(name).strip().upper()
25+
26+
return (
27+
(name in self.alt_names) or
28+
(super(FunctionDict, self).__contains__(name))
29+
)
30+
31+
def load_function_info():
32+
FUNCTIONS = FunctionDict()
33+
34+
alt_names = {}
35+
36+
with open(FUNCTION_INFO_CSV) as f:
37+
csv = DictReader(f)
38+
for row in csv:
39+
function = row['function']
40+
for k in MODES:
41+
if row[k]:
42+
row[k] = int(row[k])
43+
else:
44+
row[k] = None
45+
46+
if row['alt_names']:
47+
row['alt_names'] = [
48+
n.strip() for n in row['alt_names'].split(',')
49+
]
50+
else:
51+
row['alt_names'] = []
52+
53+
for n in row['alt_names']:
54+
alt_names[n.upper()] = function
55+
56+
row['inputs'] = []
57+
58+
FUNCTIONS[function] = row
59+
60+
FUNCTIONS.alt_names = alt_names
61+
62+
with open(FUNCTION_INPUTS_CSV) as f:
63+
csv = DictReader(f)
64+
65+
for row in csv:
66+
if row['function']:
67+
FUNCTIONS[row['function']]['inputs'].append({
68+
'name': row['field'],
69+
'comment': row['comments']
70+
})
71+
72+
return FUNCTIONS
73+
74+
def list_functions():
75+
s = sorted([
76+
"%s (%s)" % (
77+
function['function'], ', '.join(function['alt_names'])
78+
) for function in FUNCTIONS.values()
79+
])
80+
s = ["List of functions (and alternate names):"] + s
81+
s.append("\nUse Geosupport.help(<function>) to read about specific function.")
82+
return '\n'.join(s)
83+
84+
def function_help(function):
85+
function = FUNCTIONS[function]
86+
87+
s = [
88+
"%s (%s)" % (function['function'], ', '.join(function['alt_names'])),
89+
"="*40,
90+
function['description'],
91+
"",
92+
"Input: %s" % function['input'],
93+
"Output: %s" % function['output'],
94+
"Modes: %s" % ', '.join([
95+
m for m in MODES if function[m] is not None
96+
]),
97+
"",
98+
"Inputs",
99+
"="*40,
100+
]
101+
102+
for i in function['inputs']:
103+
s.append("%s - %s" % (i['name'], i['comment']))
104+
105+
s = "\n".join(s)
106+
107+
return s
108+
109+
FUNCTIONS = load_function_info()
110+
111+
def load_work_area_layouts():
112+
WORK_AREA_LAYOUTS = {}
113+
114+
for csv in glob.glob(path.join(WORK_AREA_LAYOUTS_PATH, '*', '*.csv')):
115+
directory = path.basename(path.dirname(csv))
116+
if directory not in WORK_AREA_LAYOUTS:
117+
WORK_AREA_LAYOUTS[directory] = {}
118+
119+
layout = {}
120+
name = path.basename(csv).split('.')[0]
121+
122+
if '-' in name:
123+
functions, mode = name.split('-')
124+
mode = '-' + mode
125+
else:
126+
functions = name
127+
mode = ''
128+
129+
functions = functions.split('_')
130+
for function in functions:
131+
WORK_AREA_LAYOUTS[directory][function + mode] = layout
132+
133+
'''df = pd.read_csv(
134+
csv, encoding='latin-1', dtype={'from': int, 'to': int, 'formatter': str}
135+
).fillna('')'''
136+
137+
with open(csv) as f:
138+
rows = DictReader(f)
139+
140+
for row in rows:
141+
name = row['name'].strip().strip(':').strip()
142+
143+
parent = row['parent'].strip().strip(':').strip()
144+
if parent and 'i' in layout[parent]:
145+
layout[parent] = {parent: layout[parent]}
146+
147+
alt_names = [
148+
n.strip() for n in row['alt_names'].split(',') if n
149+
]
150+
151+
v = {
152+
'i': (int(row['from']) - 1, int(row['to'])),
153+
#'formatter': get_formatter(row['formatter'])
154+
'formatter': row['formatter']
155+
}
156+
157+
if parent:
158+
layout[parent][name] = v
159+
else:
160+
layout[name] = v
161+
162+
for n in alt_names:
163+
layout[n] = v
164+
layout[n.upper()] = v
165+
layout[n.lower()] = v
166+
167+
return WORK_AREA_LAYOUTS
168+
169+
WORK_AREA_LAYOUTS = load_work_area_layouts()

0 commit comments

Comments
 (0)