From ce30a81fe2b71782717a027c495b6831619e20f3 Mon Sep 17 00:00:00 2001 From: Owen Dreikosen Date: Thu, 10 Oct 2019 14:20:44 -0400 Subject: [PATCH 1/9] First Attempt at python excel add in generating --- scripts/excel.py | 437 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 437 insertions(+) create mode 100644 scripts/excel.py diff --git a/scripts/excel.py b/scripts/excel.py new file mode 100644 index 0000000..8722883 --- /dev/null +++ b/scripts/excel.py @@ -0,0 +1,437 @@ +import itertools +import re +scripts=[] +s="" + + + + + + +#Template for VBA +template="QUERY_NAME(FILTER_PARAMS, GEO_PARAMS, API_TOKEN As String) As Variant\nQUERY_NAME = FUNCTION" +helper_function_template=''' + numCols = dataResults("data")(1).Count + columnNames = Array("households", "year") + numObservations = dataResults("data").Count + + ReDim dataArray(numObservations - 1, numCols - 1) + + idxRow = 0 + For Each Value In dataResults("data") + For idxCol = 0 To numCols - 1 + dataArray(idxRow, idxCol) = Value(columnNames(idxCol)) + Next idxCol + idxRow = idxRow + 1 + Next Value + + queryHouseholdsIncomeAge = dataArray +End Function\n\n''' +class households: + def __init__(self): + self.s="" + self.function_name="queryHouseholdsIncomeAge" + self.function_string='''Public Function queryHouseholdsIncomeAge(YEAR_LOW As Integer, YEAR_HIGH As Integer, INCOME_LOW As Integer, INCOME_HIGH As Integer, AGE_LOW As Integer, AGE_HIGH As Integer, geoname As String, geoFilter As Dictionary, API_TOKEN As String) As Variant + Dim dataResults As Object + + Set dataResults = submitAPIQuery( _ + query:=apiQueryParameters( _ + table:="incomeforecast_" & geoname & "_annual_income_group_age", _ + dataFields:=Array(renameVariable(original:="households", renamed:="households"), "year"), _ + dataFilters:=Array( _ + geoFilter, _ + betweenFilter("year", Array(YEAR_LOW, YEAR_HIGH)), _ + betweenFilter("income_g", Array(INCOME_LOW, INCOME_HIGH)), _ + betweenFilter("age_g", Array(AGE_LOW, AGE_HIGH))), _ + aggregations:=Array(sumAggregation("house1holds")), _ + groupby:=Array("year"), _ + order:=Array("year")), _ + API_TOKEN:=API_TOKEN) +''' + helper_function_template + + def params(self,s): + if ("AGE_LOW" not in s): + s=s+"\t\tAGE_LOW:=1, _ \n\t\tAGE_HIGH:=18, _\n" + if ("INCOME_LOW" not in s): + s=s+"\t\tINCOME_LOW:=1, _\n\t\tINCOME_HIGH:=18, _\n" + self.s=s + +class median_household_income: + def __init__(self): + self.s="" + self.function_name="queryMedianIncomeAge" + self.function_string='''Public Function queryMedianIncomeAge(YEAR_LOW As Integer, YEAR_HIGH As Integer, AGE_LOW As Integer, AGE_HIGH As Integer, geoname As String, geoFilter As Dictionary, API_TOKEN As String) As Variant + Dim dataResults As Object + + Set dataResults = submitAPIQuery( _ + query:=medianQueryParameters( _ + table:="incomeforecast_" & geoname & "_annual_income_group_age", _ + dataFields:=Array(renameVariable(original:="median_value", renamed:="median_hhi"), "year"), _ + medianVariableName:="income_g", _ + dataFilters:=Array( _ + geoFilter, _ + betweenFilter("year", Array(YEAR_LOW, YEAR_HIGH)), _ + betweenFilter("age_g", Array(AGE_LOW, AGE_HIGH))), _ + aggregations:=Array(sumAggregation("households")), _ + groupby:=Array("year"), _ + order:=Array("year")), _ + API_TOKEN:=API_TOKEN) + ''' + helper_function_template + + def params(self,s): + self.s=s + + + + +#List of all supported function types +stratodem_functions={ + 'HOUSEHOLDS':households(), + 'MEDIAN_HOUSEHOLD_INCOME':median_household_income() +} + +#List all supported data types for filters +data=['WITH_INCOME_XXX_','WITH_AGE_XXX_'] +#List all supported filters +filters={ + 'BETWEEN':'XXX_LOW as Integer, XXX_HIGH as Intger' +} + +params={ + 'BETWEEN':'\t\tXXX_LOW:=XXX_LOW, _\n\t\tXXX_HIGH:=XXX_HIGH, _\n' +} + +#List all supported geo levels +upper_geo={'METRO':'METRO_CODE As Long', + #'STATE': 'GEOID2', + 'COUNTY': 'COUNTY_CODE As Long', + #'US':'US', + #"MICRO":'micro', + #'ZIP':"zip", + #'TRACT': 'GEOID11', + 'MILE_RADIUS':"LATITUDE As Double, LONGITUDE As Double, MILES As Double" + #'DRIVE_TIME':'drive_time' +} +lower_geo={ + 'METRO':"\t\tgeoname:=\"metro\", _\n\t\tgeoFilter:=equalToFilter(\"cbsa\",METRO_CODE), _\n", + #'STATE':'LOL', + 'COUNTY': "\t\tgeoname:=\"county\", _\n\t\tgeoFilter:=equalToFilter(\"geoid5\",COUNTY_CODE), _\n", + #"US":"Still gotta do", + #"MICRO":"WILL FINISH", + #"ZIP":"I PROMISE", + #"TRACT":"working on replicating the excel example first", + "MILE_RADIUS":"\t\tgeoname:=\"tract\", _\n\t\tgeoFilter:=mileRadiusFilter(LATITUDE:=LATITUDE,LONGITUDE:=LONGITUDE, MILES:=MILES), _\n" + #"DRIVE_TIME": "Okay last one" +} +#Generate all possible combinations of data +filters_and_data=[] +for x in filters.keys(): + for y in data: + filters_and_data.append(y.replace('XXX',x)) + + +data_combos=[] +queries=[] +for i in range(1,len(data)+1): + data_combos.append(list(itertools.combinations(filters_and_data,i))) +for x in data_combos: + for y in x: + stuff=''.join(y) + for i in upper_geo.keys(): + queries.append("STRATODEM_HOUSEHOLDS_"+stuff+"FOR_"+i) +print(queries) + +def data_filterParser(s): + data_filters=[] + data_params=[] + x=re.search("(.+?)(?:_WITH)",s) + test=x.group(1) + s=s[len(x.group(1))+1:] + while(len(s)>1): + x=re.search("(?:WITH_)(.+?(?:_).+?)(?:_)",s) + if (x is None): + break + substring=x.group(1) + stuff=substring.split('_') + data_filters.append(filters[stuff[1]].replace("XXX",stuff[0])) + data_params.append(params[stuff[1]].replace("XXX",stuff[0])) + s=s[len(x.group(0)):] + return [''.join(data_params),','.join(data_filters)] + +def geoParser(s): + x=re.search("(?:FOR_).+",s) + stuff=x.group(0) + stuff=stuff[4:] + return [lower_geo[stuff],upper_geo[stuff]] + +def functionParser(s): + x=re.search("(?:STRATODEM_)(.*?)(?:_)",s) + stuff=x.group(1) + return stratodem_functions[stuff] + +def stringParser(s): + query="Public Function "+s + data_filter=data_filterParser(s) + query=query+"(YEAR_LOW As Integer, YEAR_HIGH as Integer, " + data_filter[1] + function_info=functionParser(s) + geo=geoParser(s) + query=query+", "+geo[1]+", API_TOKEN As String) As Variant\n" + query=query+"\t"+s+"="+function_info.function_name+"( _\n\t\tYEAR_LOW:=YEAR_LOW, _ \n\t\tYEAR_HIGH:=YEAR_HIGH, _\n"+data_filter[0] + function_info.params(query) + query=function_info.s + query=query+geo[0] + "\t\tAPI_TOKEN:=API_TOKEN)\nEnd Function\n\n" + return query + +LongString="" + +for x in queries: + LongString=LongString+stringParser(x) +for x in stratodem_functions.keys(): + LongString=LongString+stratodem_functions[x].function_string +LongString=LongString+'''Private Function submitAPIQuery(query As Dictionary, API_TOKEN As String) As Object + 'Query the StratoDem Analytics API + + Dim httpReq As New WinHttp.WinHttpRequest + Dim apiToken As String + Dim apiQuery As Dictionary: Set apiQuery = New Dictionary + Dim apiQueryString As String + Dim apiResponse As String + Dim apiResponseObject As Object + + apiQuery.Add "token", API_TOKEN + apiQuery.Add "query", query + apiQueryString = JsonConverter.ConvertToJson(apiQuery) + + 'POST the API query to the StratoDem Analytics API + With httpReq + .Open "POST", "https://api.stratodem.com/api", False + .SetRequestHeader "Content-type", "application/json" + .SetRequestHeader "Accept", "application/json" + .SetTimeouts 60000, 60000, 60000, 60000 + .Send (apiQueryString) + + 'Return the API results + Set apiResponseObject = JsonConverter.ParseJson(.ResponseText) + If apiResponseObject("success") Then + Set submitAPIQuery = apiResponseObject + Else + MsgBox "API Query Failed: " + apiResponseObject("message") + " " + apiQueryString + Err.Raise 5, "API Query Failed", apiResponseObject("message") + End If + End With +End Function + +' ----- ///// FILTERS ///// ------ ' +Private Function apiFilter(filterVariable As String, filterType As String, filterValue As Variant) As Dictionary + ' Helper method to create an API filter + Dim filterDict As Dictionary: Set filterDict = New Dictionary + + filterDict.Add "filter_variable", filterVariable + filterDict.Add "filter_type", filterType + filterDict.Add "filter_value", filterValue + + Set apiFilter = filterDict +End Function + +Private Function equalToFilter(filterVariable As String, filterValue As Variant) As Dictionary + ' Create "equal to" filter + Set equalToFilter = apiFilter(filterVariable, "eq", filterValue) +End Function + +Private Function notEqualToFilter(filterVariable As String, filterValue As Variant) As Dictionary + ' Create "not equal to" filter + Set notEqualToFilter = apiFilter(filterVariable, "ne", filterValue) +End Function + +Private Function inFilter(filterVariable As String, filterValue As Variant) As Dictionary + ' Create "in" filter + Set inFilter = apiFilter(filterVariable, "in", filterValue) +End Function + +Private Function notInFilter(filterVariable As String, filterValue As Variant) As Dictionary + ' Create "not in" filter + Set notInFilter = apiFilter(filterVariable, "nin", filterValue) +End Function + +Private Function greaterThanFilter(filterVariable As String, filterValue As Variant) As Dictionary + ' Create "greater than" filter + Set greaterThanFilter = apiFilter(filterVariable, "gt", filterValue) +End Function + +Private Function greaterThanOrEqualToFilter(filterVariable As String, filterValue As Variant) As Dictionary + ' Create "greater than or equal to" filter + Set greaterThanOrEqualToFilter = apiFilter(filterVariable, "ge", filterValue) +End Function + +Private Function lessThanFilter(filterVariable As String, filterValue As Variant) As Dictionary + ' Create "less than" filter + Set lessThanFilter = apiFilter(filterVariable, "lt", filterValue) +End Function + +Private Function lessThanOrEqualToFilter(filterVariable As String, filterValue As Variant) As Dictionary + ' Create "less than or equal to" filter + Set lessThanOrEqualToFilter = apiFilter(filterVariable, "le", filterValue) +End Function + +Private Function betweenFilter(filterVariable As String, filterValue As Variant) As Dictionary + ' Create "between" filter (a <= x <= b) + Set betweenFilter = apiFilter(filterVariable, "between", filterValue) +End Function + +Private Function drivetimeFilter(LATITUDE As Double, LONGITUDE As Double, minutes As Integer) + ' Create drivetime filter (within minutes of latitude-longitude pair) + Dim dtValue As Dictionary: Set dtValue = New Dictionary + + dtValue.Add "latitude", LATITUDE + dtValue.Add "longitude", LONGITUDE + dtValue.Add "minutes", minutes + + Set drivetimeFilter = apiFilter("", "drivetime", dtValue) +End Function + +Private Function mileRadiusFilter(LATITUDE As Double, LONGITUDE As Double, MILES As Double) + ' Create mile radius filter (within miles of latitude-longitude pair) + Dim mrValue As Dictionary: Set mrValue = New Dictionary + + mrValue.Add "latitude", LATITUDE + mrValue.Add "longitude", LONGITUDE + mrValue.Add "miles", MILES + + Set mileRadiusFilter = apiFilter("", "mile_radius", mrValue) +End Function + +' ----- ///// AGGREGATIONS ///// ------ ' +Function apiAggregation(aggregationFunc As String, variableName As String) As Dictionary + ' Helper method to create an API aggregation + Dim agg As Dictionary: Set agg = New Dictionary + + agg.Add "aggregation_func", aggregationFunc + agg.Add "variable_name", variableName + + Set apiAggregation = agg +End Function + +Function sumAggregation(variableName As String) As Dictionary + ' Create a "sum" aggregation + Set sumAggregation = apiAggregation("sum", variableName) +End Function + +Function meanAggregation(variableName As String) As Dictionary + ' Create a "mean" aggregation + Set meanAggregation = apiAggregation("mean", variableName) +End Function + +' ----- ///// QUERY STRUCTURES ///// ------ ' +Function apiQueryParameters(table As String, dataFields As Variant, dataFilters As Variant, _ + Optional aggregations As Variant, Optional groupby As Variant, _ + Optional order As Variant, Optional queryType As String = "COUNT", _ + Optional join As Variant, Optional joinOn As Variant, _ + Optional LATITUDE As Double, Optional LONGITUDE As Double, _ + Optional medianVariableName As String, Optional meanVariableName As String) As Dictionary + ' Structure apiQueryParameters for submitting to the StratoDem Analytics API + Dim queryParams As Dictionary: Set queryParams = New Dictionary + + queryParams.Add "table", table + queryParams.Add "query_type", queryType + queryParams.Add "data_fields", dataFields + queryParams.Add "data_filters", dataFilters + + If IsMissing(aggregations) Then + queryParams.Add "aggregations", New Collection + Else + queryParams.Add "aggregations", aggregations + End If + If IsMissing(groupby) Then + queryParams.Add "groupby", New Collection + Else + queryParams.Add "groupby", groupby + End If + If IsMissing(order) Then + queryParams.Add "order", New Collection + Else + queryParams.Add "order", order + End If + If Not IsMissing(join) Then + queryParams.Add "join", join + End If + If Not IsMissing(joinOn) Then + queryParams.Add "on", joinOn + End If + If Not IsMissing(LATITUDE) Then + queryParams.Add "latitude", LATITUDE + End If + If Not IsMissing(LONGITUDE) Then + queryParams.Add "longitude", LONGITUDE + End If + + queryParams.Add "median_variable_name", medianVariableName + queryParams.Add "mean_variable_name", meanVariableName + + Set apiQueryParameters = queryParams +End Function + +Function medianQueryParameters(table As String, dataFields As Variant, dataFilters As Variant, _ + medianVariableName As String, _ + Optional aggregations As Variant, Optional groupby As Variant, _ + Optional order As Variant, Optional join As Variant, Optional joinOn As Variant) As Dictionary + ' Structure a median query for submitting to the StratoDem Analytics API + Set medianQueryParameters = apiQueryParameters(table, _ + dataFields, _ + dataFilters, _ + aggregations:=aggregations, _ + groupby:=groupby, _ + order:=order, _ + queryType:="MEDIAN", _ + join:=join, _ + joinOn:=joinOn, _ + medianVariableName:=medianVariableName) +End Function + +Function meanQueryParameters(table As String, dataFields As Variant, dataFilters As Variant, _ + meanVariableName As String, _ + Optional aggregations As Variant, Optional groupby As Variant, _ + Optional order As Variant, Optional join As Variant, Optional joinOn As Variant) As Dictionary + ' Structure a mean query for submitting to the StratoDem Analytics API + Set meanQueryParameters = apiQueryParameters(table, _ + dataFields, _ + dataFilters, _ + aggregations:=aggregations, _ + groupby:=groupby, _ + order:=order, _ + queryType:="MEAN", _ + join:=join, _ + joinOn:=joinOn, _ + meanVariableName:=meanVariableName) +End Function + +Function geocoderQueryParameters(table As String, LATITUDE As Double, LONGITUDE As Double, dataFields As Variant) As Dictionary + ' Structure a geocoder query for submitting to the StratoDem Analytics API + Set geocoderQueryParameters = apiQueryParameters(table, _ + dataFields:=dataFields, _ + LATITUDE:=LATITUDE, _ + LONGITUDE:=LONGITUDE, _ + dataFilters:=Array(), _ + queryType:="GEOCODER") +End Function + +Function renameVariable(original As String, renamed As String) As Dictionary + ' Rename a variable from original to renamed in the database query + Dim renameDict As Dictionary: Set renameDict = New Dictionary + + renameDict.Add original, renamed + + Set renameVariable = renameDict +End Function + +Function joinOnStructure(left As Variant, right As Variant) As Dictionary + ' Create the correct joining structure for queries + Dim joinOnDict As Dictionary: Set joinOnDict = New Dictionary + + joinOnDict.Add "left", left + joinOnDict.Add "right", right + Set joinOnStructure = joinOnDict +End Function''' +f=open("test.xlsm","w+") +f.write(LongString) +f.close() + From 37523ac4574d98658af69aef49b27825ca87c581 Mon Sep 17 00:00:00 2001 From: Owen Dreikosen Date: Thu, 10 Oct 2019 16:06:53 -0400 Subject: [PATCH 2/9] POC StratoExcel Add In --- StratoDem_Excel_Add_In.xlam | Bin 0 -> 10886 bytes scripts/excel.py | 55 +++++++++++++++++++++++++++++++----- 2 files changed, 48 insertions(+), 7 deletions(-) create mode 100644 StratoDem_Excel_Add_In.xlam diff --git a/StratoDem_Excel_Add_In.xlam b/StratoDem_Excel_Add_In.xlam new file mode 100644 index 0000000000000000000000000000000000000000..180371de5084e04b1d6a2c231b88dfafdf7715f1 GIT binary patch literal 10886 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 Date: Thu, 10 Oct 2019 18:06:50 -0400 Subject: [PATCH 3/9] Commented and Cleaned Code --- StratoDem_Excel_Add_In.xlam | Bin 10886 -> 121093 bytes scripts/excel.py | 195 ++++++++++++++++++++---------------- 2 files changed, 109 insertions(+), 86 deletions(-) diff --git a/StratoDem_Excel_Add_In.xlam b/StratoDem_Excel_Add_In.xlam index 180371de5084e04b1d6a2c231b88dfafdf7715f1..7d712fc9184505502427ee58cd7d4b3677ab2661 100644 GIT binary patch delta 117314 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 diff --git a/scripts/excel.py b/scripts/excel.py index 7426fbc..96227f3 100644 --- a/scripts/excel.py +++ b/scripts/excel.py @@ -3,13 +3,65 @@ scripts=[] s="" +''' +Naming Conventions - +query = 1 specific type of query for the API -> STRATODEM_HOUSEHOLDS_WITH_INCOME_BETWEEN_FOR_METRO + +function = multiple different variations for different +input variables and filters of queries -> queryHouseholdsIncomeAge + + many queries -> 1 function + +''' + +#Values for variables to default to give broad range +default_values={ + 'AGE':'\t\tAGE_LOW:=1, _ \n\t\tAGE_HIGH:=18, _\n', + 'INCOME':'\t\tINCOME_LOW:=1, _ \n\t\tINCOME_HIGH:=18, _\n' +} + + +#List of all supported filters and their templates +filters={ + 'BETWEEN':'XXX_LOW as Integer, XXX_HIGH as Integer, ' +} + +#Templates for all supported filters for the paramaters for functions +params={ + 'BETWEEN':'\t\tXXX_LOW:=XXX_LOW, _\n\t\tXXX_HIGH:=XXX_HIGH, _\n' +} + +#List of all supported geo locations +upper_geo={'METRO':'METRO_CODE As Long', + #'STATE': 'GEOID2', + 'COUNTY': 'COUNTY_CODE As Long', + #'US':'US', + #"MICRO":'micro', + #'ZIP':"zip", + #'TRACT': 'GEOID11', + 'MILE_RADIUS':"LATITUDE As Double, LONGITUDE As Double, MILES As Double" + #'DRIVE_TIME':'drive_time' +} +#Geo templates for function parameters +lower_geo={ + 'METRO':"\t\tgeoname:=\"metro\", _\n\t\tgeoFilter:=equalToFilter(\"cbsa\",METRO_CODE), _\n", + #'STATE':'LOL', + 'COUNTY': "\t\tgeoname:=\"county\", _\n\t\tgeoFilter:=equalToFilter(\"geoid5\",COUNTY_CODE), _\n", + #"US":"Still gotta do", + #"MICRO":"WILL FINISH", + #"ZIP":"I PROMISE", + #"TRACT":"working on replicating the excel example first", + "MILE_RADIUS":"\t\tgeoname:=\"tract\", _\n\t\tgeoFilter:=mileRadiusFilter(LATITUDE:=LATITUDE,LONGITUDE:=LONGITUDE, MILES:=MILES), _\n" + #"DRIVE_TIME": "Okay last one" +} -#Template for VBA +#Template for VBA QUERY template="QUERY_NAME(FILTER_PARAMS, GEO_PARAMS, API_TOKEN As String) As Variant\nQUERY_NAME = FUNCTION" + +#Template for end of VBA function helper_function_template=''' numCols = dataResults("data")(1).Count columnNames = Array("households", "year") @@ -27,8 +79,36 @@ XXX = dataArray End Function\n\n''' -class households: + +class stratofunction: + #Creates all possible combinations of queries with given input variables + def __init__(self,user_variables,function_title): + self.user_variables=user_variables + self.queries=[] + filters_and_data=[] + combos=[] + for _filter in filters.keys(): + for variable in self.user_variables: + filters_and_data.append(f"WITH_{variable}_{_filter}_") + for i in range(1,len(self.user_variables)+1): + combos.append(list(itertools.combinations(filters_and_data,i))) + for combo in combos: + for variable in combo: + query=''.join(variable) + for geo in upper_geo.keys(): + self.queries.append(f"STRATODEM_{function_title}_{query}FOR_{geo}") + for geo in upper_geo.keys(): + self.queries.append(f"STRATODEM_{function_title}_FOR_{geo}") + + def params(self,query): + for variable in self.user_variables: + if variable not in query: + query=query+default_values[variable] + self.s=query + +class households(stratofunction): def __init__(self): + super().__init__(['AGE','INCOME'],'HOUSEHOLDS') self.s="" self.function_name="queryHouseholdsIncomeAge" self.function_string='''Public Function queryHouseholdsIncomeAge(YEAR_LOW As Integer, YEAR_HIGH As Integer, INCOME_LOW As Integer, INCOME_HIGH As Integer, AGE_LOW As Integer, AGE_HIGH As Integer, geoname As String, geoFilter As Dictionary, API_TOKEN As String) As Variant @@ -47,18 +127,13 @@ def __init__(self): groupby:=Array("year"), _ order:=Array("year")), _ API_TOKEN:=API_TOKEN) -'''+helper_function_template +''' + helper_function_template self.function_string=self.function_string.replace("XXX",self.function_name) - def params(self,s): - if ("AGE_LOW" not in s): - s=s+"\t\tAGE_LOW:=1, _ \n\t\tAGE_HIGH:=18, _\n" - if ("INCOME_LOW" not in s): - s=s+"\t\tINCOME_LOW:=1, _\n\t\tINCOME_HIGH:=18, _\n" - self.s=s - -class median_household_income: + +class median_household_income(stratofunction): def __init__(self): + super().__init__(['AGE',],"MEDIAN_HOUSEHOLD_INCOME") self.s="" self.function_name="queryMedianIncomeAge" self.function_string='''Public Function queryMedianIncomeAge(YEAR_LOW As Integer, YEAR_HIGH As Integer, AGE_LOW As Integer, AGE_HIGH As Integer, geoname As String, geoFilter As Dictionary, API_TOKEN As String) As Variant @@ -79,9 +154,6 @@ def __init__(self): API_TOKEN:=API_TOKEN) ''' + helper_function_template self.function_string=self.function_string.replace("XXX",self.function_name) - - def params(self,s): - self.s=s @@ -92,84 +164,35 @@ def params(self,s): 'MEDIAN_HOUSEHOLD_INCOME':median_household_income() } -#List all supported data types for filters -data=['WITH_INCOME_XXX_','WITH_AGE_XXX_'] -#List all supported filters -filters={ - 'BETWEEN':'XXX_LOW as Integer, XXX_HIGH as Integer' -} - -params={ - 'BETWEEN':'\t\tXXX_LOW:=XXX_LOW, _\n\t\tXXX_HIGH:=XXX_HIGH, _\n' -} - -#List all supported geo levels -upper_geo={'METRO':'METRO_CODE As Long', - #'STATE': 'GEOID2', - 'COUNTY': 'COUNTY_CODE As Long', - #'US':'US', - #"MICRO":'micro', - #'ZIP':"zip", - #'TRACT': 'GEOID11', - 'MILE_RADIUS':"LATITUDE As Double, LONGITUDE As Double, MILES As Double" - #'DRIVE_TIME':'drive_time' -} -lower_geo={ - 'METRO':"\t\tgeoname:=\"metro\", _\n\t\tgeoFilter:=equalToFilter(\"cbsa\",METRO_CODE), _\n", - #'STATE':'LOL', - 'COUNTY': "\t\tgeoname:=\"county\", _\n\t\tgeoFilter:=equalToFilter(\"geoid5\",COUNTY_CODE), _\n", - #"US":"Still gotta do", - #"MICRO":"WILL FINISH", - #"ZIP":"I PROMISE", - #"TRACT":"working on replicating the excel example first", - "MILE_RADIUS":"\t\tgeoname:=\"tract\", _\n\t\tgeoFilter:=mileRadiusFilter(LATITUDE:=LATITUDE,LONGITUDE:=LONGITUDE, MILES:=MILES), _\n" - #"DRIVE_TIME": "Okay last one" -} -#Generate all possible combinations of data -filters_and_data=[] -for x in filters.keys(): - for y in data: - filters_and_data.append(y.replace('XXX',x)) - - -data_combos=[] -queries=[] -for i in range(1,len(data)+1): - data_combos.append(list(itertools.combinations(filters_and_data,i))) -for x in data_combos: - for y in x: - stuff=''.join(y) - for i in upper_geo.keys(): - queries.append("STRATODEM_HOUSEHOLDS_"+stuff+"FOR_"+i) -print(queries) def data_filterParser(s): data_filters=[] data_params=[] x=re.search("(.+?)(?:_WITH)",s) - test=x.group(1) + if (x is None): + return['',''] s=s[len(x.group(1))+1:] while(len(s)>1): x=re.search("(?:WITH_)(.+?(?:_).+?)(?:_)",s) if (x is None): break substring=x.group(1) - stuff=substring.split('_') - data_filters.append(filters[stuff[1]].replace("XXX",stuff[0])) - data_params.append(params[stuff[1]].replace("XXX",stuff[0])) + filter_param=substring.split('_') + data_filters.append(filters[filter_param[1]].replace("XXX",filter_param[0])) + data_params.append(params[filter_param[1]].replace("XXX",filter_param[0])) s=s[len(x.group(0)):] - return [''.join(data_params),','.join(data_filters)] + return [''.join(data_params),''.join(data_filters)] def geoParser(s): x=re.search("(?:FOR_).+",s) - stuff=x.group(0) - stuff=stuff[4:] - return [lower_geo[stuff],upper_geo[stuff]] + geo=x.group(0) + geo=geo[4:] + return [lower_geo[geo],upper_geo[geo]] def functionParser(s): - x=re.search("(?:STRATODEM_)(.*?)(?:_)",s) - stuff=x.group(1) - return stratodem_functions[stuff] + x=re.search("(?:STRATODEM_)(.*?)(?:_WITH|_FOR)",s) + function=x.group(1) + return stratodem_functions[function] def stringParser(s): query="Public Function "+s @@ -177,20 +200,20 @@ def stringParser(s): query=query+"(YEAR_LOW As Integer, YEAR_HIGH as Integer, " + data_filter[1] function_info=functionParser(s) geo=geoParser(s) - query=query+", "+geo[1]+", API_TOKEN As String) As Variant\n" + query=query+geo[1]+", API_TOKEN As String) As Variant\n" query=query+"\t"+s+"="+function_info.function_name+"( _\n\t\tYEAR_LOW:=YEAR_LOW, _ \n\t\tYEAR_HIGH:=YEAR_HIGH, _\n"+data_filter[0] function_info.params(query) query=function_info.s - query=query+geo[0] + "\t\tAPI_TOKEN:=API_TOKEN)\nEnd Function\n\n" + query=query +geo[0] + "\t\tAPI_TOKEN:=API_TOKEN)\nEnd Function\n\n" return query -LongString="" +VBA_Script="" -for x in queries: - LongString=LongString+stringParser(x) -for x in stratodem_functions.keys(): - LongString=LongString+stratodem_functions[x].function_string -LongString=LongString+'''Private Function submitAPIQuery(query As Dictionary, API_TOKEN As String) As Object +for x in stratodem_functions.values(): + for query in x.queries: + VBA_Script=VBA_Script+stringParser(query) + VBA_Script=VBA_Script+x.function_string +VBA_Script=VBA_Script+'''Private Function submitAPIQuery(query As Dictionary, API_TOKEN As String) As Object 'Query the StratoDem Analytics API Dim httpReq As New WinHttp.WinHttpRequest @@ -472,7 +495,7 @@ def stringParser(s): End Function ''' -f=open("test.txt","w+") -f.write(LongString) +f=open("Strato_Excel_Add_In.txt","w+") +f.write(VBA_Script) f.close() From 98d594298fe42dd29c8075aa07eef0a56b31306c Mon Sep 17 00:00:00 2001 From: michael Date: Thu, 10 Oct 2019 18:22:12 -0400 Subject: [PATCH 4/9] Code style --> SDStyle --- scripts/excel.py | 344 +++++++++++++++++++++++++++++++---------------- 1 file changed, 228 insertions(+), 116 deletions(-) diff --git a/scripts/excel.py b/scripts/excel.py index 52e716a..07bebe8 100644 --- a/scripts/excel.py +++ b/scripts/excel.py @@ -1,9 +1,11 @@ -import itertools -import re -scripts = [] -s = "" +""" +StratoDem Analytics : excel +Principal Author(s) : Owen Dreikosen +Secondary Author(s) : +Description : VBA package generation for Excel User-Defined Functions + +Notes : -''' Naming Conventions query = 1 specific type of query for the API @@ -12,59 +14,65 @@ function = multiple different variations for different input variables and filters of queries -> queryHouseholdsIncomeAge - many queries -> 1 function +many queries -> 1 function -''' +October 10, 2019 +""" + +import itertools +import re +from types import MappingProxyType +from typing import Tuple -# Values for variables to default to give broad range -default_values = { +# Map the metric to the default filter with all possible values of the metric +# (MappingProxyType so this is immutable) +map_metric_to_default_filter_values = MappingProxyType({ 'AGE': '\t\tAGE_LOW:=1, _ \n\t\tAGE_HIGH:=18, _\n', 'INCOME': '\t\tINCOME_LOW:=1, _ \n\t\tINCOME_HIGH:=18, _\n' -} +}) - -# List of all supported filters and their templates -filters = { +# Map filter type to the filter dim definition in VBA function stub +# (MappingProxyType so this is immutable) +map_filter_type_to_filter_dim = MappingProxyType({ 'BETWEEN': 'XXX_LOW as Integer, XXX_HIGH as Integer, ' -} +}) -# Templates for all supported filters for the paramaters for functions -params = { +# Templates for all supported filters for the parameters for functions +# (MappingProxyType so this is immutable) +map_filter_type_to_filter_template = MappingProxyType({ 'BETWEEN': '\t\tXXX_LOW:=XXX_LOW, _\n\t\tXXX_HIGH:=XXX_HIGH, _\n' -} +}) -# List of all supported geo locations -upper_geo = { +# Map geographic level to dim definition in VBA function stub +# (MappingProxyType so this is immutable) +outer_geo_function_dim = MappingProxyType({ 'METRO': 'METRO_CODE As Long', - #'STATE': 'GEOID2', + # 'STATE': 'GEOID2', 'COUNTY': 'COUNTY_CODE As Long', - #'US':'US', - #"MICRO":'micro', - #'ZIP':"zip", - #'TRACT': 'GEOID11', + # 'US':'US', + # "MICRO":'micro', + # 'ZIP':"zip", + # 'TRACT': 'GEOID11', 'MILE_RADIUS': "LATITUDE As Double, LONGITUDE As Double, MILES As Double" - #'DRIVE_TIME':'drive_time' - } - + # 'DRIVE_TIME':'drive_time' +}) -# Geo templates for function parameters -lower_geo = { +# Map geographic level to geographic filter used in function +# (MappingProxyType so this is immutable) +inner_geo_filters = MappingProxyType({ 'METRO': '''\t\tgeoname:=\"metro\", _ \t\tgeoFilter:=equalToFilter(\"cbsa\",METRO_CODE), _\n''', - #'STATE':'LOL', + # 'STATE':'LOL', 'COUNTY': '''\t\tgeoname:=\"county\", _ \t\tgeoFilter:=equalToFilter(\"geoid5\",COUNTY_CODE), _\n''', - #"US":"Still gotta do", - #"MICRO":"WILL FINISH", - #"ZIP":"I PROMISE", - #"TRACT":"working on replicating the excel example first", + # "US":"Still gotta do", + # "MICRO":"WILL FINISH", + # "ZIP":"I PROMISE", + # "TRACT":"working on replicating the excel example first", "MILE_RADIUS": '''\t\tgeoname:=\"tract\", _ \t\tgeoFilter:=mileRadiusFilter(LATITUDE:=LATITUDE,LONGITUDE:=LONGITUDE, MILES:=MILES), _\n''' - #"DRIVE_TIME": "Okay last one" -} -# Template for VBA QUERY -template = '''QUERY_NAME(FILTER_PARAMS, GEO_PARAMS, API_TOKEN As String) As Variant - QUERY_NAME = FUNCTION''' + # "DRIVE_TIME": "Okay last one" +}) # Template for end of VBA function helper_function_template = ''' @@ -86,14 +94,20 @@ End Function\n\n''' -class stratofunction: +class StratoFunction: + user_variables = None + function_title = None + # Creates all possible combinations of queries with given input variables - def __init__(self, user_variables, function_title): - self.user_variables = user_variables + def __init__(self): self.queries = [] + self.vba_function_definition_str = None + filters_and_data = [] combos = [] - for _filter in filters.keys(): + + # TODO what is all this doing?? :D + for _filter in map_filter_type_to_filter_dim.keys(): for variable in self.user_variables: filters_and_data.append(f"WITH_{variable}_{_filter}_") for i in range(1, len(self.user_variables) + 1): @@ -101,24 +115,26 @@ def __init__(self, user_variables, function_title): for combo in combos: for variable in combo: query = ''.join(variable) - for geo in upper_geo.keys(): - self.queries.append(f"STRATODEM_{function_title}_{query}FOR_{geo}") - for geo in upper_geo.keys(): - self.queries.append(f"STRATODEM_{function_title}_FOR_{geo}") + for geo in outer_geo_function_dim.keys(): + self.queries.append(f"STRATODEM_{self.function_title}_{query}FOR_{geo}") + for geo in outer_geo_function_dim.keys(): + self.queries.append(f"STRATODEM_{self.function_title}_FOR_{geo}") - def params(self, query): + def params(self, vba_function_definition_str): for variable in self.user_variables: - if variable not in query: - query = query+default_values[variable] - self.s = query + if variable not in vba_function_definition_str: + vba_function_definition_str += map_metric_to_default_filter_values[variable] + self.vba_function_definition_str = vba_function_definition_str + +class HouseholdsByIncomeStratoFunction(StratoFunction): + user_variables = ['AGE', 'INCOME'] + function_title = 'HOUSEHOLDS' -class households(stratofunction): def __init__(self): - super().__init__(['AGE', 'INCOME'], 'HOUSEHOLDS') - self.s = "" + super().__init__() self.function_name = "queryHouseholdsIncomeAge" - self.function_string ='''Public Function queryHouseholdsIncomeAge(YEAR_LOW As Integer, YEAR_HIGH As Integer, INCOME_LOW As Integer, INCOME_HIGH As Integer, AGE_LOW As Integer, AGE_HIGH As Integer, geoname As String, geoFilter As Dictionary, API_TOKEN As String) As Variant + self.function_string = '''Public Function queryHouseholdsIncomeAge(YEAR_LOW As Integer, YEAR_HIGH As Integer, INCOME_LOW As Integer, INCOME_HIGH As Integer, AGE_LOW As Integer, AGE_HIGH As Integer, geoname As String, geoFilter As Dictionary, API_TOKEN As String) As Variant Dim dataResults As Object Set dataResults = submitAPIQuery( _ @@ -139,10 +155,12 @@ def __init__(self): self.function_string = function_string -class median_household_income(stratofunction): +class MedianHouseholdIncomeStratoFunction(StratoFunction): + user_variables = ['AGE'] + function_title = 'MEDIAN_HOUSEHOLD_INCOME' + def __init__(self): - super().__init__(['AGE'], "MEDIAN_HOUSEHOLD_INCOME") - self.s = "" + super().__init__() self.function_name = "queryMedianIncomeAge" self.function_string = '''Public Function queryMedianIncomeAge(YEAR_LOW As Integer, YEAR_HIGH As Integer, AGE_LOW As Integer, AGE_HIGH As Integer, geoname As String, geoFilter As Dictionary, API_TOKEN As String) As Variant Dim dataResults As Object @@ -165,68 +183,148 @@ def __init__(self): self.function_string = function_string -# List of all supported function types -stratodem_functions = { - 'HOUSEHOLDS': households(), - 'MEDIAN_HOUSEHOLD_INCOME': median_household_income() -} +# Map the function topic (e.g., 'HOUSEHOLDS') to the StratoFunction instance +map_function_topic_to_stratofunction = MappingProxyType({ + 'HOUSEHOLDS': HouseholdsByIncomeStratoFunction(), + 'MEDIAN_HOUSEHOLD_INCOME': MedianHouseholdIncomeStratoFunction() +}) + + +def parse_data_filters(function_name: str) -> Tuple[str, str]: + """ + Parse the function name and create the data filters used in the query (and function definition) + Parameters + ---------- + function_name: str + Name of the VBA function, e.g. "STRATODEM_HOUSEHOLDS_WITH_AGE_BETWEEN_FOR_METRO" -def data_filterParser(s): + Returns + ------- + str, str + Data filters, Data dim in function definition + """ data_filters = [] data_params = [] - x = re.search("(.+?)(?:_WITH)", s) - if (x is None): - return['', ''] - s = s[len(x.group(1))+1:] - while(len(s) > 1): - x = re.search("(?:WITH_)(.+?(?:_).+?)(?:_)", s) - if (x is None): + re_matches = re.search("(.+?)(?:_WITH)", function_name) + + if re_matches is None: + return '', '' + + function_name = function_name[len(re_matches.group(1)) + 1:] + while len(function_name) > 1: + re_matches = re.search("(?:WITH_)(.+?(?:_).+?)(?:_)", function_name) + if re_matches is None: break - substring = x.group(1) + substring = re_matches.group(1) filter_param = substring.split('_') - data_filters.append(filters[filter_param[1]].replace("XXX", filter_param[0])) - data_params.append(params[filter_param[1]].replace("XXX", filter_param[0])) - s = s[len(x.group(0)):] - return [''.join(data_params), ''.join(data_filters)] + data_filters.append( + map_filter_type_to_filter_dim[filter_param[1]].replace("XXX", filter_param[0])) + data_params.append( + map_filter_type_to_filter_template[filter_param[1]].replace("XXX", filter_param[0])) + function_name = function_name[len(re_matches.group(0)):] + + return ''.join(data_params), ''.join(data_filters) + + +def parse_geolevel(function_name: str) -> Tuple[str, str]: + """ + + Parameters + ---------- + function_name: str + Name of the VBA function, e.g. "STRATODEM_HOUSEHOLDS_WITH_AGE_BETWEEN_FOR_METRO" + + Returns + ------- + str, str + Geographic filters, Geographic dim in function definition + """ + re_matches = re.search("(?:FOR_).+", function_name) + geo = re_matches.group(0) + geo = geo[4:] + geo_filters = inner_geo_filters[geo] + geo_dim = outer_geo_function_dim[geo] -def geoParser(s): - x = re.search("(?:FOR_).+", s) - geo = x.group(0) - geo = geo[4:] - return [lower_geo[geo], upper_geo[geo]] - - -def functionParser(s): - x = re.search("(?:STRATODEM_)(.*?)(?:_WITH|_FOR)",s) - function = x.group(1) - return stratodem_functions[function] - - -def stringParser(s): - query = "Public Function "+s - data_filter = data_filterParser(s) - query = query + f"(YEAR_LOW As Integer, YEAR_HIGH as Integer, {data_filter[1]}" - function_info = functionParser(s) - geo = geoParser(s) - query = query+geo[1]+", API_TOKEN As String) As Variant\n" - query = query+f'''\t{s}={function_info.function_name}( _ - \t\tYEAR_LOW:=YEAR_LOW, _ \n\t\tYEAR_HIGH:=YEAR_HIGH, _ - {data_filter[0]}''' - function_info.params(query) - query = function_info.s - query = query + geo[0] + "\t\tAPI_TOKEN:=API_TOKEN)\nEnd Function\n\n" - return query - - -VBA_Script = "" - -for x in stratodem_functions.values(): - for query in x.queries: - VBA_Script = VBA_Script+stringParser(query) - VBA_Script = VBA_Script + x.function_string -VBA_Script = VBA_Script+'''Private Function submitAPIQuery(query As Dictionary, API_TOKEN As String) As Object + return geo_filters, geo_dim + + +def parse_function_name_to_function_info(function_name: str) -> StratoFunction: + """ + Pull the StratoFunction definition associated with the given function name + + Parameters + ---------- + function_name: str + Name of the VBA function, e.g. "STRATODEM_HOUSEHOLDS_WITH_AGE_BETWEEN_FOR_METRO" + + Returns + ------- + StratoFunction + """ + re_matches = re.search("(?:STRATODEM_)(.*?)(?:_WITH|_FOR)", function_name) + function_topic = re_matches.group(1) + + return map_function_topic_to_stratofunction[function_topic] + + +def generate_vba_function_definition(function_name: str) -> str: + """ + Generate the VBA function definition for a given function name + + Parameters + ---------- + function_name: str + Name of the VBA function, e.g. "STRATODEM_HOUSEHOLDS_WITH_AGE_BETWEEN_FOR_METRO" + + Returns + ------- + str + VBA function definition + + Examples + -------- + ``` + generate_vba_function_definition('STRATODEM_HOUSEHOLDS_WITH_AGE_BETWEEN_FOR_METRO') + ``` + > Returns + > Public Function STRATODEM_HOUSEHOLDS_WITH_AGE_BETWEEN_FOR_METRO(YEAR_LOW As Integer, YEAR_HIGH As Integer, AGE_LOW as Integer, AGE_HIGH as Integer, METRO_CODE As Long, API_TOKEN As String) As Variant + > STRATODEM_HOUSEHOLDS_WITH_AGE_BETWEEN_FOR_METRO=queryHouseholdsIncomeAge( _ + > YEAR_LOW:=YEAR_LOW, _ + > YEAR_HIGH:=YEAR_HIGH, _ + > AGE_LOW:=AGE_LOW, _ + > AGE_HIGH:=AGE_HIGH, _ + > INCOME_LOW:=1, _ + > INCOME_HIGH:=18, _ + > geoname:="metro", _ + > geoFilter:=equalToFilter("cbsa",METRO_CODE), _ + > API_TOKEN:=API_TOKEN) + End Function + + """ + data_filter = parse_data_filters(function_name) + function_info = parse_function_name_to_function_info(function_name) + geo = parse_geolevel(function_name) + + vba_function_definition = f""" +Public Function {function_name}(YEAR_LOW As Integer, YEAR_HIGH As Integer, {data_filter[1]}{geo[1]}, API_TOKEN As String) As Variant + {function_name}={function_info.function_name}( _ + YEAR_LOW:=YEAR_LOW, _ + YEAR_HIGH:=YEAR_HIGH, _ + {data_filter[0]}""" + + # TODO not a fan of the mutation here + function_info.params(vba_function_definition) + + vba_function_definition = function_info.vba_function_definition_str + + vba_function_definition += geo[0] + "\t\tAPI_TOKEN:=API_TOKEN)\nEnd Function\n\n" + + return vba_function_definition + + +STANDARD_LIBRARY_VBA_CODE = """Private Function submitAPIQuery(query As Dictionary, API_TOKEN As String) As Object 'Query the StratoDem Analytics API Dim httpReq As New WinHttp.WinHttpRequest @@ -506,8 +604,22 @@ def stringParser(s): Err.Raise 5, "geolevelToGeoname", "Failed to map GEOLEVEL " & GEOLEVEL End If End Function +""" + +if __name__ == '__main__': + vba_script = """ +' StratoDem Analytics Excel Add-in for User-Defined Functions +' (c) StratoDem Analytics, 2019- +' Questions? Email team@stratodem.com +""" + + for function_ in map_function_topic_to_stratofunction.values(): + for query in function_.queries: + vba_script += generate_vba_function_definition(query) + + vba_script += function_.function_string + + vba_script += STANDARD_LIBRARY_VBA_CODE -''' -f = open("Strato_Excel_Add_In.txt", "w+") -f.write(VBA_Script) -f.close() + with open('Strato_Excel_Add_In.txt', 'w') as f: + f.write(vba_script) From a8c2a6ae36effaed2503cdc08914b02eb487afdb Mon Sep 17 00:00:00 2001 From: michael Date: Thu, 10 Oct 2019 18:22:53 -0400 Subject: [PATCH 5/9] add description to parse_geolevel function --- scripts/excel.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/excel.py b/scripts/excel.py index 07bebe8..f355551 100644 --- a/scripts/excel.py +++ b/scripts/excel.py @@ -229,6 +229,7 @@ def parse_data_filters(function_name: str) -> Tuple[str, str]: def parse_geolevel(function_name: str) -> Tuple[str, str]: """ + Parse the function name to determine the geographic filters and geographic variable definitions Parameters ---------- From 17c0193bc8cf9973da095ead4bd564bf6813a1ea Mon Sep 17 00:00:00 2001 From: michael Date: Thu, 10 Oct 2019 18:26:09 -0400 Subject: [PATCH 6/9] use abstract base class to avoid need for super().__init__ call --- scripts/excel.py | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/scripts/excel.py b/scripts/excel.py index f355551..45f4313 100644 --- a/scripts/excel.py +++ b/scripts/excel.py @@ -19,6 +19,7 @@ October 10, 2019 """ +import abc import itertools import re from types import MappingProxyType @@ -94,7 +95,7 @@ End Function\n\n''' -class StratoFunction: +class StratoFunction(abc.ABC): user_variables = None function_title = None @@ -126,15 +127,20 @@ def params(self, vba_function_definition_str): vba_function_definition_str += map_metric_to_default_filter_values[variable] self.vba_function_definition_str = vba_function_definition_str + @property + @abc.abstractmethod + def function_string(self): + pass + class HouseholdsByIncomeStratoFunction(StratoFunction): user_variables = ['AGE', 'INCOME'] function_title = 'HOUSEHOLDS' + function_name = 'queryHouseholdsIncomeAge' - def __init__(self): - super().__init__() - self.function_name = "queryHouseholdsIncomeAge" - self.function_string = '''Public Function queryHouseholdsIncomeAge(YEAR_LOW As Integer, YEAR_HIGH As Integer, INCOME_LOW As Integer, INCOME_HIGH As Integer, AGE_LOW As Integer, AGE_HIGH As Integer, geoname As String, geoFilter As Dictionary, API_TOKEN As String) As Variant + @property + def function_string(self) -> str: + function_string = '''Public Function queryHouseholdsIncomeAge(YEAR_LOW As Integer, YEAR_HIGH As Integer, INCOME_LOW As Integer, INCOME_HIGH As Integer, AGE_LOW As Integer, AGE_HIGH As Integer, geoname As String, geoFilter As Dictionary, API_TOKEN As String) As Variant Dim dataResults As Object Set dataResults = submitAPIQuery( _ @@ -151,18 +157,19 @@ def __init__(self): order:=Array("year")), _ API_TOKEN:=API_TOKEN) ''' + helper_function_template - function_string = self.function_string.replace("XXX", self.function_name) - self.function_string = function_string + function_string = function_string.replace("XXX", self.function_name) + + return function_string class MedianHouseholdIncomeStratoFunction(StratoFunction): user_variables = ['AGE'] function_title = 'MEDIAN_HOUSEHOLD_INCOME' + function_name = 'queryMedianIncomeAge' - def __init__(self): - super().__init__() - self.function_name = "queryMedianIncomeAge" - self.function_string = '''Public Function queryMedianIncomeAge(YEAR_LOW As Integer, YEAR_HIGH As Integer, AGE_LOW As Integer, AGE_HIGH As Integer, geoname As String, geoFilter As Dictionary, API_TOKEN As String) As Variant + @property + def function_string(self) -> str: + function_string = '''Public Function queryMedianIncomeAge(YEAR_LOW As Integer, YEAR_HIGH As Integer, AGE_LOW As Integer, AGE_HIGH As Integer, geoname As String, geoFilter As Dictionary, API_TOKEN As String) As Variant Dim dataResults As Object Set dataResults = submitAPIQuery( _ @@ -179,8 +186,9 @@ def __init__(self): order:=Array("year")), _ API_TOKEN:=API_TOKEN) ''' + helper_function_template - function_string = self.function_string.replace("XXX", self.function_name) - self.function_string = function_string + function_string = function_string.replace("XXX", self.function_name) + + return function_string # Map the function topic (e.g., 'HOUSEHOLDS') to the StratoFunction instance From 06bdb1fd8d9140cd3966503ba2187a797f367b98 Mon Sep 17 00:00:00 2001 From: Owen Dreikosen Date: Thu, 10 Oct 2019 19:04:42 -0400 Subject: [PATCH 7/9] PIP8 Linting --- scripts/excel.py | 256 +++++++++++++++++++++++++---------------------- 1 file changed, 134 insertions(+), 122 deletions(-) diff --git a/scripts/excel.py b/scripts/excel.py index 96227f3..52e716a 100644 --- a/scripts/excel.py +++ b/scripts/excel.py @@ -1,74 +1,79 @@ import itertools import re -scripts=[] -s="" +scripts = [] +s = "" ''' Naming Conventions -query = 1 specific type of query for the API -> STRATODEM_HOUSEHOLDS_WITH_INCOME_BETWEEN_FOR_METRO +query = 1 specific type of query for the API +i.e STRATODEM_HOUSEHOLDS_WITH_INCOME_BETWEEN_FOR_METRO -function = multiple different variations for different +function = multiple different variations for different input variables and filters of queries -> queryHouseholdsIncomeAge many queries -> 1 function ''' -#Values for variables to default to give broad range -default_values={ - 'AGE':'\t\tAGE_LOW:=1, _ \n\t\tAGE_HIGH:=18, _\n', - 'INCOME':'\t\tINCOME_LOW:=1, _ \n\t\tINCOME_HIGH:=18, _\n' +# Values for variables to default to give broad range +default_values = { + 'AGE': '\t\tAGE_LOW:=1, _ \n\t\tAGE_HIGH:=18, _\n', + 'INCOME': '\t\tINCOME_LOW:=1, _ \n\t\tINCOME_HIGH:=18, _\n' } -#List of all supported filters and their templates -filters={ - 'BETWEEN':'XXX_LOW as Integer, XXX_HIGH as Integer, ' +# List of all supported filters and their templates +filters = { + 'BETWEEN': 'XXX_LOW as Integer, XXX_HIGH as Integer, ' } -#Templates for all supported filters for the paramaters for functions -params={ - 'BETWEEN':'\t\tXXX_LOW:=XXX_LOW, _\n\t\tXXX_HIGH:=XXX_HIGH, _\n' -} - -#List of all supported geo locations -upper_geo={'METRO':'METRO_CODE As Long', - #'STATE': 'GEOID2', - 'COUNTY': 'COUNTY_CODE As Long', - #'US':'US', - #"MICRO":'micro', - #'ZIP':"zip", - #'TRACT': 'GEOID11', - 'MILE_RADIUS':"LATITUDE As Double, LONGITUDE As Double, MILES As Double" - #'DRIVE_TIME':'drive_time' +# Templates for all supported filters for the paramaters for functions +params = { + 'BETWEEN': '\t\tXXX_LOW:=XXX_LOW, _\n\t\tXXX_HIGH:=XXX_HIGH, _\n' } - -#Geo templates for function parameters -lower_geo={ - 'METRO':"\t\tgeoname:=\"metro\", _\n\t\tgeoFilter:=equalToFilter(\"cbsa\",METRO_CODE), _\n", +# List of all supported geo locations +upper_geo = { + 'METRO': 'METRO_CODE As Long', + #'STATE': 'GEOID2', + 'COUNTY': 'COUNTY_CODE As Long', + #'US':'US', + #"MICRO":'micro', + #'ZIP':"zip", + #'TRACT': 'GEOID11', + 'MILE_RADIUS': "LATITUDE As Double, LONGITUDE As Double, MILES As Double" + #'DRIVE_TIME':'drive_time' + } + + +# Geo templates for function parameters +lower_geo = { + 'METRO': '''\t\tgeoname:=\"metro\", _ + \t\tgeoFilter:=equalToFilter(\"cbsa\",METRO_CODE), _\n''', #'STATE':'LOL', - 'COUNTY': "\t\tgeoname:=\"county\", _\n\t\tgeoFilter:=equalToFilter(\"geoid5\",COUNTY_CODE), _\n", + 'COUNTY': '''\t\tgeoname:=\"county\", _ + \t\tgeoFilter:=equalToFilter(\"geoid5\",COUNTY_CODE), _\n''', #"US":"Still gotta do", #"MICRO":"WILL FINISH", #"ZIP":"I PROMISE", #"TRACT":"working on replicating the excel example first", - "MILE_RADIUS":"\t\tgeoname:=\"tract\", _\n\t\tgeoFilter:=mileRadiusFilter(LATITUDE:=LATITUDE,LONGITUDE:=LONGITUDE, MILES:=MILES), _\n" + "MILE_RADIUS": '''\t\tgeoname:=\"tract\", _ + \t\tgeoFilter:=mileRadiusFilter(LATITUDE:=LATITUDE,LONGITUDE:=LONGITUDE, MILES:=MILES), _\n''' #"DRIVE_TIME": "Okay last one" } +# Template for VBA QUERY +template = '''QUERY_NAME(FILTER_PARAMS, GEO_PARAMS, API_TOKEN As String) As Variant + QUERY_NAME = FUNCTION''' -#Template for VBA QUERY -template="QUERY_NAME(FILTER_PARAMS, GEO_PARAMS, API_TOKEN As String) As Variant\nQUERY_NAME = FUNCTION" - -#Template for end of VBA function -helper_function_template=''' +# Template for end of VBA function +helper_function_template = ''' numCols = dataResults("data")(1).Count columnNames = Array("households", "year") numObservations = dataResults("data").Count - + ReDim dataArray(numObservations - 1, numCols - 1) - + idxRow = 0 For Each Value In dataResults("data") For idxCol = 0 To numCols - 1 @@ -76,44 +81,46 @@ Next idxCol idxRow = idxRow + 1 Next Value - + XXX = dataArray End Function\n\n''' + class stratofunction: - #Creates all possible combinations of queries with given input variables - def __init__(self,user_variables,function_title): - self.user_variables=user_variables - self.queries=[] - filters_and_data=[] - combos=[] + # Creates all possible combinations of queries with given input variables + def __init__(self, user_variables, function_title): + self.user_variables = user_variables + self.queries = [] + filters_and_data = [] + combos = [] for _filter in filters.keys(): for variable in self.user_variables: filters_and_data.append(f"WITH_{variable}_{_filter}_") - for i in range(1,len(self.user_variables)+1): - combos.append(list(itertools.combinations(filters_and_data,i))) + for i in range(1, len(self.user_variables) + 1): + combos.append(list(itertools.combinations(filters_and_data, i))) for combo in combos: for variable in combo: - query=''.join(variable) + query = ''.join(variable) for geo in upper_geo.keys(): self.queries.append(f"STRATODEM_{function_title}_{query}FOR_{geo}") for geo in upper_geo.keys(): self.queries.append(f"STRATODEM_{function_title}_FOR_{geo}") - - def params(self,query): + + def params(self, query): for variable in self.user_variables: if variable not in query: - query=query+default_values[variable] - self.s=query + query = query+default_values[variable] + self.s = query + class households(stratofunction): def __init__(self): - super().__init__(['AGE','INCOME'],'HOUSEHOLDS') - self.s="" - self.function_name="queryHouseholdsIncomeAge" - self.function_string='''Public Function queryHouseholdsIncomeAge(YEAR_LOW As Integer, YEAR_HIGH As Integer, INCOME_LOW As Integer, INCOME_HIGH As Integer, AGE_LOW As Integer, AGE_HIGH As Integer, geoname As String, geoFilter As Dictionary, API_TOKEN As String) As Variant + super().__init__(['AGE', 'INCOME'], 'HOUSEHOLDS') + self.s = "" + self.function_name = "queryHouseholdsIncomeAge" + self.function_string ='''Public Function queryHouseholdsIncomeAge(YEAR_LOW As Integer, YEAR_HIGH As Integer, INCOME_LOW As Integer, INCOME_HIGH As Integer, AGE_LOW As Integer, AGE_HIGH As Integer, geoname As String, geoFilter As Dictionary, API_TOKEN As String) As Variant Dim dataResults As Object - + Set dataResults = submitAPIQuery( _ query:=apiQueryParameters( _ table:="incomeforecast_" & geoname & "_annual_income_group_age", _ @@ -128,17 +135,18 @@ def __init__(self): order:=Array("year")), _ API_TOKEN:=API_TOKEN) ''' + helper_function_template - self.function_string=self.function_string.replace("XXX",self.function_name) + function_string = self.function_string.replace("XXX", self.function_name) + self.function_string = function_string class median_household_income(stratofunction): def __init__(self): - super().__init__(['AGE',],"MEDIAN_HOUSEHOLD_INCOME") - self.s="" - self.function_name="queryMedianIncomeAge" - self.function_string='''Public Function queryMedianIncomeAge(YEAR_LOW As Integer, YEAR_HIGH As Integer, AGE_LOW As Integer, AGE_HIGH As Integer, geoname As String, geoFilter As Dictionary, API_TOKEN As String) As Variant + super().__init__(['AGE'], "MEDIAN_HOUSEHOLD_INCOME") + self.s = "" + self.function_name = "queryMedianIncomeAge" + self.function_string = '''Public Function queryMedianIncomeAge(YEAR_LOW As Integer, YEAR_HIGH As Integer, AGE_LOW As Integer, AGE_HIGH As Integer, geoname As String, geoFilter As Dictionary, API_TOKEN As String) As Variant Dim dataResults As Object - + Set dataResults = submitAPIQuery( _ query:=medianQueryParameters( _ table:="incomeforecast_" & geoname & "_annual_income_group_age", _ @@ -153,76 +161,81 @@ def __init__(self): order:=Array("year")), _ API_TOKEN:=API_TOKEN) ''' + helper_function_template - self.function_string=self.function_string.replace("XXX",self.function_name) - + function_string = self.function_string.replace("XXX", self.function_name) + self.function_string = function_string - -#List of all supported function types -stratodem_functions={ - 'HOUSEHOLDS':households(), - 'MEDIAN_HOUSEHOLD_INCOME':median_household_income() +# List of all supported function types +stratodem_functions = { + 'HOUSEHOLDS': households(), + 'MEDIAN_HOUSEHOLD_INCOME': median_household_income() } def data_filterParser(s): - data_filters=[] - data_params=[] - x=re.search("(.+?)(?:_WITH)",s) + data_filters = [] + data_params = [] + x = re.search("(.+?)(?:_WITH)", s) if (x is None): - return['',''] - s=s[len(x.group(1))+1:] - while(len(s)>1): - x=re.search("(?:WITH_)(.+?(?:_).+?)(?:_)",s) + return['', ''] + s = s[len(x.group(1))+1:] + while(len(s) > 1): + x = re.search("(?:WITH_)(.+?(?:_).+?)(?:_)", s) if (x is None): break - substring=x.group(1) - filter_param=substring.split('_') - data_filters.append(filters[filter_param[1]].replace("XXX",filter_param[0])) - data_params.append(params[filter_param[1]].replace("XXX",filter_param[0])) - s=s[len(x.group(0)):] - return [''.join(data_params),''.join(data_filters)] + substring = x.group(1) + filter_param = substring.split('_') + data_filters.append(filters[filter_param[1]].replace("XXX", filter_param[0])) + data_params.append(params[filter_param[1]].replace("XXX", filter_param[0])) + s = s[len(x.group(0)):] + return [''.join(data_params), ''.join(data_filters)] + def geoParser(s): - x=re.search("(?:FOR_).+",s) - geo=x.group(0) - geo=geo[4:] - return [lower_geo[geo],upper_geo[geo]] + x = re.search("(?:FOR_).+", s) + geo = x.group(0) + geo = geo[4:] + return [lower_geo[geo], upper_geo[geo]] + def functionParser(s): - x=re.search("(?:STRATODEM_)(.*?)(?:_WITH|_FOR)",s) - function=x.group(1) + x = re.search("(?:STRATODEM_)(.*?)(?:_WITH|_FOR)",s) + function = x.group(1) return stratodem_functions[function] - + + def stringParser(s): - query="Public Function "+s - data_filter=data_filterParser(s) - query=query+"(YEAR_LOW As Integer, YEAR_HIGH as Integer, " + data_filter[1] - function_info=functionParser(s) - geo=geoParser(s) - query=query+geo[1]+", API_TOKEN As String) As Variant\n" - query=query+"\t"+s+"="+function_info.function_name+"( _\n\t\tYEAR_LOW:=YEAR_LOW, _ \n\t\tYEAR_HIGH:=YEAR_HIGH, _\n"+data_filter[0] + query = "Public Function "+s + data_filter = data_filterParser(s) + query = query + f"(YEAR_LOW As Integer, YEAR_HIGH as Integer, {data_filter[1]}" + function_info = functionParser(s) + geo = geoParser(s) + query = query+geo[1]+", API_TOKEN As String) As Variant\n" + query = query+f'''\t{s}={function_info.function_name}( _ + \t\tYEAR_LOW:=YEAR_LOW, _ \n\t\tYEAR_HIGH:=YEAR_HIGH, _ + {data_filter[0]}''' function_info.params(query) - query=function_info.s - query=query +geo[0] + "\t\tAPI_TOKEN:=API_TOKEN)\nEnd Function\n\n" + query = function_info.s + query = query + geo[0] + "\t\tAPI_TOKEN:=API_TOKEN)\nEnd Function\n\n" return query -VBA_Script="" + +VBA_Script = "" for x in stratodem_functions.values(): for query in x.queries: - VBA_Script=VBA_Script+stringParser(query) - VBA_Script=VBA_Script+x.function_string -VBA_Script=VBA_Script+'''Private Function submitAPIQuery(query As Dictionary, API_TOKEN As String) As Object + VBA_Script = VBA_Script+stringParser(query) + VBA_Script = VBA_Script + x.function_string +VBA_Script = VBA_Script+'''Private Function submitAPIQuery(query As Dictionary, API_TOKEN As String) As Object 'Query the StratoDem Analytics API - + Dim httpReq As New WinHttp.WinHttpRequest Dim apiToken As String Dim apiQuery As Dictionary: Set apiQuery = New Dictionary Dim apiQueryString As String Dim apiResponse As String Dim apiResponseObject As Object - + apiQuery.Add "token", API_TOKEN apiQuery.Add "query", query apiQueryString = JsonConverter.ConvertToJson(apiQuery) @@ -250,11 +263,11 @@ def stringParser(s): Private Function apiFilter(filterVariable As String, filterType As String, filterValue As Variant) As Dictionary ' Helper method to create an API filter Dim filterDict As Dictionary: Set filterDict = New Dictionary - + filterDict.Add "filter_variable", filterVariable filterDict.Add "filter_type", filterType filterDict.Add "filter_value", filterValue - + Set apiFilter = filterDict End Function @@ -306,22 +319,22 @@ def stringParser(s): Private Function drivetimeFilter(LATITUDE As Double, LONGITUDE As Double, minutes As Integer) ' Create drivetime filter (within minutes of latitude-longitude pair) Dim dtValue As Dictionary: Set dtValue = New Dictionary - + dtValue.Add "latitude", LATITUDE dtValue.Add "longitude", LONGITUDE dtValue.Add "minutes", minutes - + Set drivetimeFilter = apiFilter("", "drivetime", dtValue) End Function Private Function mileRadiusFilter(LATITUDE As Double, LONGITUDE As Double, MILES As Double) ' Create mile radius filter (within miles of latitude-longitude pair) Dim mrValue As Dictionary: Set mrValue = New Dictionary - + mrValue.Add "latitude", LATITUDE mrValue.Add "longitude", LONGITUDE mrValue.Add "miles", MILES - + Set mileRadiusFilter = apiFilter("", "mile_radius", mrValue) End Function @@ -329,10 +342,10 @@ def stringParser(s): Function apiAggregation(aggregationFunc As String, variableName As String) As Dictionary ' Helper method to create an API aggregation Dim agg As Dictionary: Set agg = New Dictionary - + agg.Add "aggregation_func", aggregationFunc agg.Add "variable_name", variableName - + Set apiAggregation = agg End Function @@ -355,7 +368,7 @@ def stringParser(s): Optional medianVariableName As String, Optional meanVariableName As String) As Dictionary ' Structure apiQueryParameters for submitting to the StratoDem Analytics API Dim queryParams As Dictionary: Set queryParams = New Dictionary - + queryParams.Add "table", table queryParams.Add "query_type", queryType queryParams.Add "data_fields", dataFields @@ -388,10 +401,10 @@ def stringParser(s): If Not IsMissing(LONGITUDE) Then queryParams.Add "longitude", LONGITUDE End If - + queryParams.Add "median_variable_name", medianVariableName queryParams.Add "mean_variable_name", meanVariableName - + Set apiQueryParameters = queryParams End Function @@ -442,16 +455,16 @@ def stringParser(s): Function renameVariable(original As String, renamed As String) As Dictionary ' Rename a variable from original to renamed in the database query Dim renameDict As Dictionary: Set renameDict = New Dictionary - + renameDict.Add original, renamed - + Set renameVariable = renameDict End Function Function joinOnStructure(left As Variant, right As Variant) As Dictionary ' Create the correct joining structure for queries Dim joinOnDict As Dictionary: Set joinOnDict = New Dictionary - + joinOnDict.Add "left", left joinOnDict.Add "right", right Set joinOnStructure = joinOnDict @@ -495,7 +508,6 @@ def stringParser(s): End Function ''' -f=open("Strato_Excel_Add_In.txt","w+") +f = open("Strato_Excel_Add_In.txt", "w+") f.write(VBA_Script) f.close() - From 9d9a76224dae240fe350bcbce30b4893867ce565 Mon Sep 17 00:00:00 2001 From: Owen Dreikosen Date: Wed, 16 Oct 2019 03:35:00 -0400 Subject: [PATCH 8/9] Added functionality for more geofilters --- scripts/excel.py | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/scripts/excel.py b/scripts/excel.py index 52e716a..4fa0b5a 100644 --- a/scripts/excel.py +++ b/scripts/excel.py @@ -36,14 +36,14 @@ # List of all supported geo locations upper_geo = { 'METRO': 'METRO_CODE As Long', - #'STATE': 'GEOID2', + 'STATE': 'STATE_CODE As Long', 'COUNTY': 'COUNTY_CODE As Long', - #'US':'US', - #"MICRO":'micro', - #'ZIP':"zip", - #'TRACT': 'GEOID11', - 'MILE_RADIUS': "LATITUDE As Double, LONGITUDE As Double, MILES As Double" - #'DRIVE_TIME':'drive_time' + 'US': '', + "MICRO": 'MICRO_CODE As Long', + "ZIP": 'ZIP_CODE as Long', + 'TRACT': 'TRACT_CODE As Long', + 'MILE_RADIUS': "LATITUDE As Double, LONGITUDE As Double, MILES As Double", + 'DRIVE_TIME': 'LATITUDE As Double, LONGITUDE As Double, minutes as Intger' } @@ -51,16 +51,21 @@ lower_geo = { 'METRO': '''\t\tgeoname:=\"metro\", _ \t\tgeoFilter:=equalToFilter(\"cbsa\",METRO_CODE), _\n''', - #'STATE':'LOL', + 'STATE': '''\t\tgeonmae:=\"state\", _ + \t\tgeoFilter:=equalToFilter(\"geoid2\",STATE_CODE), _\n ''', 'COUNTY': '''\t\tgeoname:=\"county\", _ \t\tgeoFilter:=equalToFilter(\"geoid5\",COUNTY_CODE), _\n''', - #"US":"Still gotta do", - #"MICRO":"WILL FINISH", - #"ZIP":"I PROMISE", - #"TRACT":"working on replicating the excel example first", + "US": "", + "MICRO": '''\t\tgeoname:=\"micro\", _ + \t\tgeoFilter:=equalToFilter(\"cbsa\",MICRO_CODE), _ \n ''', + "ZIP": '''\t\tgeoname:=\"zip\" , _ + \t\tgeoFilter:=equalToFilter(\"zip\",ZIP_CODE), _ \n ''', + "TRACT": '''\t\tgeoname:=\"tract\", _ + \t\tgeoFilter:=equalToFilter(\"geoid11\", TRACT_CODE) , _ \n ''', "MILE_RADIUS": '''\t\tgeoname:=\"tract\", _ - \t\tgeoFilter:=mileRadiusFilter(LATITUDE:=LATITUDE,LONGITUDE:=LONGITUDE, MILES:=MILES), _\n''' - #"DRIVE_TIME": "Okay last one" + \t\tgeoFilter:=mileRadiusFilter(LATITUDE:=LATITUDE,LONGITUDE:=LONGITUDE, MILES:=MILES), _\n''', + "DRIVE_TIME": '''\t\tgeoname:=\"tract\", _ + \t\tgeoFilter:=drivetimeFilter(LATITUDE:=LATITUDE,LONGITUDE:=LONGITUDE, minutes:=minutes), _\n ''' } # Template for VBA QUERY template = '''QUERY_NAME(FILTER_PARAMS, GEO_PARAMS, API_TOKEN As String) As Variant From 2d94ff5ca01ac467a8ad4bef707a8466fe6a216c Mon Sep 17 00:00:00 2001 From: Michael Clawar Date: Wed, 16 Oct 2019 13:17:44 -0400 Subject: [PATCH 9/9] fix typos and support multiple variable name responses --- scripts/excel.py | 59 ++++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/scripts/excel.py b/scripts/excel.py index f0b3e31..5e337a2 100644 --- a/scripts/excel.py +++ b/scripts/excel.py @@ -55,8 +55,7 @@ "ZIP": 'ZIP_CODE as Long', 'TRACT': 'TRACT_CODE As Long', 'MILE_RADIUS': "LATITUDE As Double, LONGITUDE As Double, MILES As Double", - 'DRIVE_TIME': 'LATITUDE As Double, LONGITUDE As Double, minutes as Intger' - } + 'DRIVE_TIME': 'LATITUDE As Double, LONGITUDE As Double, minutes as Integer', }) # Map geographic level to geographic filter used in function @@ -78,31 +77,32 @@ "MILE_RADIUS": '''\t\tgeoname:=\"tract\", _ \t\tgeoFilter:=mileRadiusFilter(LATITUDE:=LATITUDE,LONGITUDE:=LONGITUDE, MILES:=MILES), _\n''', "DRIVE_TIME": '''\t\tgeoname:=\"tract\", _ - \t\tgeoFilter:=drivetimeFilter(LATITUDE:=LATITUDE,LONGITUDE:=LONGITUDE, minutes:=minutes), _\n ''' -} + \t\tgeoFilter:=drivetimeFilter(LATITUDE:=LATITUDE,LONGITUDE:=LONGITUDE, minutes:=minutes), _\n''' +}) # Template for VBA QUERY template = '''QUERY_NAME(FILTER_PARAMS, GEO_PARAMS, API_TOKEN As String) As Variant QUERY_NAME = FUNCTION''' -# Template for end of VBA function -helper_function_template = ''' - numCols = dataResults("data")(1).Count - columnNames = Array("households", "year") - numObservations = dataResults("data").Count - - ReDim dataArray(numObservations - 1, numCols - 1) - - idxRow = 0 - For Each Value In dataResults("data") - For idxCol = 0 To numCols - 1 - dataArray(idxRow, idxCol) = Value(columnNames(idxCol)) - Next idxCol - idxRow = idxRow + 1 - Next Value - - XXX = dataArray -End Function\n\n''' +def build_helper_function_template(target_variable_name: str) -> str: + # Template for end of VBA function + return f''' + numCols = dataResults("data")(1).Count + columnNames = Array("{target_variable_name}", "year") + numObservations = dataResults("data").Count + + ReDim dataArray(numObservations - 1, numCols - 1) + + idxRow = 0 + For Each Value In dataResults("data") + For idxCol = 0 To numCols - 1 + dataArray(idxRow, idxCol) = Value(columnNames(idxCol)) + Next idxCol + idxRow = idxRow + 1 + Next Value + + XXX = dataArray + End Function\n\n''' class StratoFunction(abc.ABC): @@ -166,7 +166,7 @@ def function_string(self) -> str: groupby:=Array("year"), _ order:=Array("year")), _ API_TOKEN:=API_TOKEN) -''' + helper_function_template +''' + build_helper_function_template('households') function_string = function_string.replace("XXX", self.function_name) return function_string @@ -185,17 +185,17 @@ def function_string(self) -> str: Set dataResults = submitAPIQuery( _ query:=medianQueryParameters( _ table:="incomeforecast_" & geoname & "_annual_income_group_age", _ - dataFields:=Array(renameVariable(original:="median_value", renamed:="median_hhi"), "year"), _ + dataFields:=Array("median_value", "year"), _ medianVariableName:="income_g", _ dataFilters:=Array( _ geoFilter, _ betweenFilter("year", Array(YEAR_LOW, YEAR_HIGH)), _ betweenFilter("age_g", Array(AGE_LOW, AGE_HIGH))), _ - aggregations:=Array(sumAggregation("households")), _ + aggregations:=Array(), _ groupby:=Array("year"), _ order:=Array("year")), _ API_TOKEN:=API_TOKEN) - ''' + helper_function_template + ''' + build_helper_function_template('median_value') function_string = function_string.replace("XXX", self.function_name) return function_string @@ -326,8 +326,13 @@ def generate_vba_function_definition(function_name: str) -> str: function_info = parse_function_name_to_function_info(function_name) geo = parse_geolevel(function_name) + if geo[1]: + geo_stub = geo[1] + ',' + else: + geo_stub = '' + vba_function_definition = f""" -Public Function {function_name}(YEAR_LOW As Integer, YEAR_HIGH As Integer, {data_filter[1]}{geo[1]}, API_TOKEN As String) As Variant +Public Function {function_name}(YEAR_LOW As Integer, YEAR_HIGH As Integer, {data_filter[1]}{geo_stub} API_TOKEN As String) As Variant {function_name}={function_info.function_name}( _ YEAR_LOW:=YEAR_LOW, _ YEAR_HIGH:=YEAR_HIGH, _