proptax 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE +674 -0
- data/README.md +161 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/proptax +3 -0
- data/lib/R/filter_csv.R +68 -0
- data/lib/proptax/cli.rb +89 -0
- data/lib/proptax/generators/report/cherry-picked.Rmd +296 -0
- data/lib/proptax/generators/report/default.Rmd +292 -0
- data/lib/proptax/generators/report.rb +31 -0
- data/lib/proptax/version.rb +3 -0
- data/lib/proptax.rb +153 -0
- data/proptax.gemspec +38 -0
- metadata +136 -0
@@ -0,0 +1,292 @@
|
|
1
|
+
---
|
2
|
+
title: 2018 Property Assessment Analysis
|
3
|
+
author: Prepared by reports@taxreformyyc.com
|
4
|
+
geometry: margin=1.5cm
|
5
|
+
---
|
6
|
+
|
7
|
+
```{r loadLibraries, echo=FALSE, message=FALSE}
|
8
|
+
# Load the required libraries
|
9
|
+
library(knitr)
|
10
|
+
library(scales)
|
11
|
+
library(formattable)
|
12
|
+
library(ggplot2)
|
13
|
+
```
|
14
|
+
|
15
|
+
```{r defineConstants, echo=FALSE}
|
16
|
+
# Constants
|
17
|
+
address <- "<%= address %>"
|
18
|
+
myAssessedValue <- <%= assessed_value %>
|
19
|
+
csvFile <- "<%= csv_file %>"
|
20
|
+
|
21
|
+
# Get the house number and street name
|
22
|
+
m <- regexpr("^\\d+", address, perl=TRUE)
|
23
|
+
myHouseNumber <- regmatches(address, m)
|
24
|
+
#myHouseNumber <- gsub("[^\\d]+", "", address, perl=TRUE)
|
25
|
+
myStreetName <- gsub(".*[\\d]", "", address, perl=TRUE)
|
26
|
+
|
27
|
+
# How do this factors impact an assessment?
|
28
|
+
unknownImpact <- c('Taxation.Status', 'Assessment.Class',
|
29
|
+
'Property.Type', 'Property.Use', 'Valuation.Approach',
|
30
|
+
'Market.Adjustment', 'Community', 'Market.Area', 'Sub.Neighbourhood.Code..SNC.',
|
31
|
+
'Sub.Market.Area', 'Influences', 'Land.Use.Designation', 'Building.Count',
|
32
|
+
'Building.Type.Structure', 'Year.of.Construction', 'Quality', 'Basement.Suite',
|
33
|
+
'Walkout.Basement', 'Garage.Type', 'Fireplace.Count',
|
34
|
+
'Constructed.On.Original.Foundation', 'Modified.For.Disabled',
|
35
|
+
'Old.House.On.New.Foundation', 'Basementless', 'Penthouse')
|
36
|
+
|
37
|
+
# These factors are informational and don't affect the assessment
|
38
|
+
noImpact <- c('Roll.Number', 'Location.Address')
|
39
|
+
```
|
40
|
+
|
41
|
+
```{r loadData, echo=FALSE}
|
42
|
+
# Load data
|
43
|
+
data <- read.csv(csvFile, header=TRUE)
|
44
|
+
```
|
45
|
+
|
46
|
+
```{r getMetaData, echo=FALSE}
|
47
|
+
streetAddresses <- data[,noImpact[2]]
|
48
|
+
|
49
|
+
# Remove the street names from the addresses
|
50
|
+
#houseNumbers <- as.numeric(gsub("[^\\d]+", "", streetAddresses, perl=TRUE))
|
51
|
+
m <- regexpr("^\\d+", streetAddresses, perl=TRUE)
|
52
|
+
houseNumbers <- as.numeric(regmatches(streetAddresses, m))
|
53
|
+
```
|
54
|
+
|
55
|
+
\center
|
56
|
+
`r paste(min(houseNumbers), "-", max(houseNumbers), myStreetName, sep=" ")`
|
57
|
+
|
58
|
+
\flushleft
|
59
|
+
|
60
|
+
# Synopsis
|
61
|
+
|
62
|
+
This analysis pertains to the property located at **`r address`**.
|
63
|
+
It documents the treatment of the assessment data provided by the City of
|
64
|
+
Calgary for the pertinent property and those adjacent on the street
|
65
|
+
(i.e., buildings `r paste(min(houseNumbers), "-", max(houseNumbers), sep=" ")`)
|
66
|
+
The data under investigation was obtained from
|
67
|
+
[assessmentsearch.calgary.ca](https://assessmentsearch.calgary.ca) and
|
68
|
+
is presented alongside this document in a consolidated CSV file.
|
69
|
+
|
70
|
+
# Approach Overview
|
71
|
+
|
72
|
+
The data investigated in this analysis consists of properties constrained to
|
73
|
+
one street on one city block. No properties were omitted to ensure the integrity
|
74
|
+
of this report and its conclusion.
|
75
|
+
|
76
|
+
Given that there are many factors contained in the data whose precise impact on
|
77
|
+
assessed values are unknown, commonalities are identified and omitted from
|
78
|
+
consideration, as all such factors should have identical impact on the
|
79
|
+
final assessment.
|
80
|
+
|
81
|
+
Factors that vary are identified and presented for consideration, as
|
82
|
+
transparency and integrity is of the utmost importance. Again, the precise
|
83
|
+
impact of these factors on the final assessment is unknown, as the weights
|
84
|
+
assigned by the City of Calgary are not divulged in the assessment data they
|
85
|
+
provide.
|
86
|
+
|
87
|
+
Having acknowledged the factors that cannot easily be quantified, the focus
|
88
|
+
turns to the properties' lot sizes, total developed area, and assessed values.
|
89
|
+
By visualizing the relationship between these quantifiable factors, the
|
90
|
+
pertinent property's assessed value is contrasted with those of neighbouring
|
91
|
+
properties. The conclusion of this analysis is drawn from the underlying data.
|
92
|
+
|
93
|
+
# Identify common factors
|
94
|
+
|
95
|
+
Many of the factors that impact the assessments are identical. This data can
|
96
|
+
safely be removed from consideration, as the impact on the assessed values
|
97
|
+
should be the same for all the properties under investigation.
|
98
|
+
|
99
|
+
```{r commonFactors, echo=FALSE}
|
100
|
+
# Identify common factors
|
101
|
+
headers <- c()
|
102
|
+
identical <- c()
|
103
|
+
displayHeaders <- c()
|
104
|
+
for (col in colnames(data)) {
|
105
|
+
values = data[,col][!duplicated(data[,col])]
|
106
|
+
if (length(values) == 1) {
|
107
|
+
headers <- append(headers, col)
|
108
|
+
displayHeaders <-append(displayHeaders, gsub("\\.", " ", col, perl=TRUE))
|
109
|
+
identical <- append(identical, toString(values))
|
110
|
+
}
|
111
|
+
}
|
112
|
+
commonFactors <- data.frame(headers, displayHeaders, identical)
|
113
|
+
```
|
114
|
+
|
115
|
+
Here, **`r length(commonFactors$headers)`** common factors can safely be
|
116
|
+
removed from the data set:
|
117
|
+
|
118
|
+
`r kable(data.frame(commonFactors$displayHeaders, commonFactors$identical),
|
119
|
+
col.names=c('Factors', 'Identical Values'), align=c('l', 'r'))`
|
120
|
+
|
121
|
+
```{r removeCommonFactors, echo=FALSE}
|
122
|
+
# Remove common factors
|
123
|
+
data <- data[,!(names(data) %in% commonFactors$headers)]
|
124
|
+
```
|
125
|
+
|
126
|
+
|
127
|
+
# Identify unknown and non-impacting factors
|
128
|
+
|
129
|
+
Of the **`r length(colnames(data))`** remaining columns, some cannot be
|
130
|
+
quantified. Others certainly impact the assessed value of a property, but the
|
131
|
+
assessment data provided by the City of Calgary does not reveal to what extent.
|
132
|
+
|
133
|
+
## Non-impacting factors
|
134
|
+
|
135
|
+
```{r noImpactDisplayHeaders, echo=FALSE}
|
136
|
+
# Remove the dots from the header name
|
137
|
+
noImpactDisplayHeaders <- gsub("\\.", " ", noImpact, perl=TRUE)
|
138
|
+
```
|
139
|
+
|
140
|
+
These factors cannot be quantified and are administrative in purpose:
|
141
|
+
|
142
|
+
`r kable(noImpactDisplayHeaders, col.names=c('Non-Impacting Factors'))`
|
143
|
+
|
144
|
+
```{r removeIrrelevantFactors, echo=FALSE}
|
145
|
+
data <- data[,!(names(data) %in% noImpact)]
|
146
|
+
```
|
147
|
+
|
148
|
+
These are removed and the remaining **`r length(colnames(data))`** columns are
|
149
|
+
carried forward.
|
150
|
+
|
151
|
+
## Unknown factors
|
152
|
+
|
153
|
+
The impact these remaining columns have on assessment values is unknown:
|
154
|
+
|
155
|
+
```{r identifyUnknowns, echo=FALSE}
|
156
|
+
# Identify unknown factors
|
157
|
+
unknownFactors <- names(data[,names(data) %in% unknownImpact])
|
158
|
+
```
|
159
|
+
|
160
|
+
`r kable(gsub("\\.", " ", unknownFactors), col.names=c('Unknown Factors'))`
|
161
|
+
|
162
|
+
The variability within these unknown columns is presented here in the interest
|
163
|
+
of transparency:
|
164
|
+
|
165
|
+
```{r consolidateUnknownFactors, echo=FALSE}
|
166
|
+
consolidatedUnknownFactors <- data[,(names(data) %in% c(unknownFactors))]
|
167
|
+
rownames(consolidatedUnknownFactors) <- houseNumbers
|
168
|
+
```
|
169
|
+
|
170
|
+
`r kable(consolidatedUnknownFactors[order(as.numeric(row.names(consolidatedUnknownFactors))),],
|
171
|
+
align=c(rep('c', length(unknownFactors))),
|
172
|
+
row.names=TRUE,
|
173
|
+
col.names=gsub("\\.", " ", unknownFactors))`
|
174
|
+
|
175
|
+
These undoubtedly have an impact on the valuation, but their precise weighting
|
176
|
+
and significance are not presented in the assessment data provided by the City
|
177
|
+
of Calgary. As such, they are removed from the dataset.
|
178
|
+
|
179
|
+
```{r removeUnknownFactors, echo=FALSE}
|
180
|
+
# Remove unknown factors from data set
|
181
|
+
data <- data[,!(names(data) %in% unknownFactors)]
|
182
|
+
```
|
183
|
+
|
184
|
+
The remaining **`r length(colnames(data))`** columns contain the following data:
|
185
|
+
|
186
|
+
```{r addRowNamesToData, echo=FALSE}
|
187
|
+
# Add row names to data
|
188
|
+
rownames(data) <- houseNumbers
|
189
|
+
```
|
190
|
+
|
191
|
+
`r kable(data[order(as.numeric(row.names(data))),], row.names=TRUE, col.names=gsub("\\.", " ", colnames(data)))`
|
192
|
+
|
193
|
+
# Visualization
|
194
|
+
|
195
|
+
The raw data presented above is summarized in Figure 1. It
|
196
|
+
illustrates the disparity between the assessed property values. The pertinent
|
197
|
+
property is coloured red.
|
198
|
+
|
199
|
+
The blue line running through the graph is _best fit_ for the visualized model.
|
200
|
+
It serves as a predictor, or indicator, as to where the properties in question
|
201
|
+
should be positioned.
|
202
|
+
|
203
|
+
The pertinent property's overassessment is determined by measuring the distance
|
204
|
+
between the red point and the blue line.
|
205
|
+
|
206
|
+
|
207
|
+
```{r adjustValues, echo=FALSE}
|
208
|
+
# Sum each property's lot size and total developed space
|
209
|
+
areaTotals <- rowSums(data[,-1])
|
210
|
+
|
211
|
+
# Isolate all assessed values
|
212
|
+
assessedValues <- data[,1]
|
213
|
+
|
214
|
+
# Plot the best fit regression line
|
215
|
+
reg <- lm(assessedValues~areaTotals)
|
216
|
+
|
217
|
+
# Plot distances between points and the regression line
|
218
|
+
assessedDifferences <- residuals(reg)
|
219
|
+
adjustedValues <- predict(reg)
|
220
|
+
|
221
|
+
# Reconcile adjusted property values
|
222
|
+
adjustedProperties <- data.frame(houseNumbers, adjustedValues, assessedDifferences)
|
223
|
+
```
|
224
|
+
|
225
|
+
```{r generatePlot, fig.cap=paste(myHouseNumber, "Overassessment", " "), fig.width=12, echo=FALSE}
|
226
|
+
plot.title <- paste(min(houseNumbers), "-", max(houseNumbers), myStreetName, sep=" ")
|
227
|
+
plot.subtitle = 'Current Assessed Property Values'
|
228
|
+
ggplot(data, aes(x=areaTotals, y=assessedValues)) +
|
229
|
+
# Plot title
|
230
|
+
ggtitle(bquote(atop(bold(.(plot.title)), atop(italic(.(plot.subtitle)), "")))) +
|
231
|
+
theme(plot.title=element_text(size=24, hjust = 0.5)) +
|
232
|
+
|
233
|
+
# Axis labels
|
234
|
+
ylab("Assessed House Values on your Street") +
|
235
|
+
xlab("Total House and Lot Size (Sq. Feet)") +
|
236
|
+
theme(axis.title.x=element_text(size=18, face="bold", margin=margin(20,0,0,0)),
|
237
|
+
axis.title.y=element_text(size=18, face="bold", margin=margin(0,20,0,0))) +
|
238
|
+
|
239
|
+
# Axis tick labels
|
240
|
+
scale_x_continuous(labels=comma) +
|
241
|
+
<% if opts.ylimit? %>
|
242
|
+
scale_y_continuous(labels=dollar, breaks=pretty_breaks(n=10),
|
243
|
+
limits=c(min(assessedValues)-<%= opts.ylimit %>, max(assessedValues)+<%= opts.ylimit %>)) +
|
244
|
+
<% else %>
|
245
|
+
scale_y_continuous(labels=dollar, breaks=pretty_breaks(n=10)) +
|
246
|
+
<% end %>
|
247
|
+
# Scatter plot points
|
248
|
+
geom_point(shape=ifelse(assessedValues==myAssessedValue, 16, 1),
|
249
|
+
size=ifelse(assessedValues==myAssessedValue, 5, 4),
|
250
|
+
colour=ifelse(assessedValues==myAssessedValue, "red", "blue")) +
|
251
|
+
|
252
|
+
# Point labels
|
253
|
+
geom_text(aes(label=houseNumbers), hjust=0.5, vjust=-2, size=5) +
|
254
|
+
geom_text(aes(label=paste("$", accounting(assessedValues, format="d"), sep="")),
|
255
|
+
hjust=0.5, vjust=-1.2) +
|
256
|
+
|
257
|
+
# Best fit line
|
258
|
+
geom_smooth(method=lm) #+
|
259
|
+
```
|
260
|
+
|
261
|
+
# Conclusion
|
262
|
+
|
263
|
+
The data investigated in this analysis describes the factors considered in
|
264
|
+
assessing the properties located at
|
265
|
+
`r paste(min(houseNumbers), "-", max(houseNumbers), myStreetName, sep=" ")`.
|
266
|
+
It was collected and provided by the City of Calgary. This report set out to
|
267
|
+
quantify the disparity between the pertinent property (i.e., `r address`) and
|
268
|
+
the neighbouring properties on its street.
|
269
|
+
|
270
|
+
Common factors were identified and eliminated from the analysis. Similarly,
|
271
|
+
varying factors were identified, catalogued, and removed from consideration. The
|
272
|
+
City of Calgary's property reports do not describe how these characteristic
|
273
|
+
features are quantified and weighted in determining a property's assessed
|
274
|
+
value. As such, they could not be included.
|
275
|
+
|
276
|
+
The conclusions that follow are drawn from comparing lot sizes, total developed
|
277
|
+
area, and assessed values. The underlying data and the overall approach have
|
278
|
+
been presented in full.
|
279
|
+
|
280
|
+
## Overvaluation: `r currency(adjustedProperties[houseNumbers==myHouseNumber,]$assessedDifferences)`
|
281
|
+
|
282
|
+
This investigation compared the assessed values with lot sizes and developed
|
283
|
+
square footage. It has revealed that that the pertinent property is overvalued
|
284
|
+
by
|
285
|
+
**`r currency(adjustedProperties[houseNumbers==myHouseNumber,]$assessedDifferences)`**.
|
286
|
+
|
287
|
+
## Corrected Assessed Value: `r currency(adjustedProperties[houseNumbers==myHouseNumber,]$adjustedValues)`
|
288
|
+
|
289
|
+
In order to bring the pertinent property's assessed value in line with those of
|
290
|
+
its immediate neighbours, it must be reassessed at
|
291
|
+
**`r currency(adjustedProperties[houseNumbers==myHouseNumber,]$adjustedValues)`**.
|
292
|
+
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'thor/group'
|
2
|
+
require 'csv'
|
3
|
+
module Proptax
|
4
|
+
module Generators
|
5
|
+
class Report < Thor::Group
|
6
|
+
include Thor::Actions
|
7
|
+
attr_accessor :address, :assessed_value, :y_axis_limits
|
8
|
+
|
9
|
+
argument :csv_file, :type => :string
|
10
|
+
argument :opts
|
11
|
+
|
12
|
+
def create_report_dir
|
13
|
+
empty_directory('reports')
|
14
|
+
FileUtils.cp(csv_file, "reports")
|
15
|
+
end
|
16
|
+
|
17
|
+
def copy_report_template
|
18
|
+
CSV.foreach(csv_file, headers: true) do |row|
|
19
|
+
self.address = row['Location Address']
|
20
|
+
self.assessed_value = row['Current Assessed Value']
|
21
|
+
file_name = address.gsub(/\s/, '_')
|
22
|
+
template("#{opts.template}.Rmd", "reports/#{file_name}.Rmd")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.source_root
|
27
|
+
File.dirname(__FILE__) + "/report"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/proptax.rb
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
require "proptax/version"
|
2
|
+
|
3
|
+
module Proptax
|
4
|
+
class Consolidator
|
5
|
+
|
6
|
+
require 'csv'
|
7
|
+
require 'pathname'
|
8
|
+
|
9
|
+
#
|
10
|
+
# Headers for publicly available assessment data for 2018.
|
11
|
+
#
|
12
|
+
Headers = ['Current Assessed Value', 'Roll Number', 'Location Address', 'Taxation Status',
|
13
|
+
'Assessment Class', 'Property Type', 'Property Use', 'Valuation Approach',
|
14
|
+
'Market Adjustment', 'Community', 'Market Area', 'Sub Neighbourhood Code (SNC)',
|
15
|
+
'Sub Market Area', 'Influences', 'Land Use Designation', 'Assessable Land Area',
|
16
|
+
'Building Count', 'Building Type/Structure', 'Year of Construction',
|
17
|
+
'Quality', 'Total Living Area Above Grade', 'Living Area Below Grade', 'Basement Suite',
|
18
|
+
'Walkout Basement', 'Garage Type', 'Garage Area', 'Fireplace Count',
|
19
|
+
'Constructed On Original Foundation', 'Modified For Disabled', 'Old House On New Foundation',
|
20
|
+
'Basementless', 'Penthouse']
|
21
|
+
|
22
|
+
#
|
23
|
+
# Keys that can be assigned multiple values
|
24
|
+
#
|
25
|
+
MultiFieldHeaders = ['Influences']
|
26
|
+
|
27
|
+
#
|
28
|
+
# Section headers for publicly available assessment data for 2018.
|
29
|
+
#
|
30
|
+
Sections = ['Assessment Details', 'Assessment Approach', 'Location Details', 'Land Details', 'Building Details']
|
31
|
+
|
32
|
+
#
|
33
|
+
# Convert a directory containing 2018 PDF assessment reports to text and
|
34
|
+
# write the relevant CSV information to stdout
|
35
|
+
#
|
36
|
+
# @param string - path to directory containing PDFs
|
37
|
+
#
|
38
|
+
# @return nil
|
39
|
+
#
|
40
|
+
def self.process(dir_name)
|
41
|
+
# `strip!': can't modify frozen String (RuntimeError)
|
42
|
+
#dir_name.strip!
|
43
|
+
dir_name = dir_name.strip
|
44
|
+
if dir_name.empty?
|
45
|
+
puts "No directory specified"
|
46
|
+
return
|
47
|
+
end
|
48
|
+
|
49
|
+
begin
|
50
|
+
path = Pathname.new(dir_name).realpath
|
51
|
+
puts Headers.to_csv if Dir.exists?(path)
|
52
|
+
Dir.glob("#{path}/*.{pdf,PDF}") do |pdf|
|
53
|
+
data = `pdftotext "#{pdf}" -`
|
54
|
+
puts parse(data).to_csv
|
55
|
+
end
|
56
|
+
rescue
|
57
|
+
puts "Directory #{dir_name} does not exist"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
#
|
62
|
+
# Take text-converted 2018 assessment reports and extract the relevant data
|
63
|
+
# into an array.
|
64
|
+
#
|
65
|
+
# @param string - text converted 2018 assessment report
|
66
|
+
#
|
67
|
+
# @return array
|
68
|
+
#
|
69
|
+
def self.parse(text)
|
70
|
+
csv_hash = {}
|
71
|
+
current_header = nil
|
72
|
+
text.split(/\n/).each do |line|
|
73
|
+
# Replace non-breaking spaces with space and strip
|
74
|
+
line = line.gsub(/\u00a0/, ' ').strip
|
75
|
+
|
76
|
+
# Remove soft hyphens
|
77
|
+
line.gsub!(/\u00AD/, '')
|
78
|
+
## This is untested
|
79
|
+
line.gsub!(/â\\200\\224/, '-')
|
80
|
+
|
81
|
+
# Remove trailing colon
|
82
|
+
line.gsub!(/:+$/, "");
|
83
|
+
|
84
|
+
next if line.empty?
|
85
|
+
|
86
|
+
if current_header
|
87
|
+
if Sections.include? line
|
88
|
+
current_header = nil
|
89
|
+
next
|
90
|
+
end
|
91
|
+
|
92
|
+
if MultiFieldHeaders.include? current_header
|
93
|
+
if csv_hash[current_header].nil?
|
94
|
+
csv_hash[current_header] = clean(line, current_header)
|
95
|
+
else
|
96
|
+
csv_hash[current_header] += "/#{clean(line, current_header)}"
|
97
|
+
end
|
98
|
+
else
|
99
|
+
csv_hash[current_header] = clean(line, current_header)
|
100
|
+
current_header = nil
|
101
|
+
end
|
102
|
+
elsif Headers.include? line
|
103
|
+
current_header = line
|
104
|
+
#
|
105
|
+
# Need a rasterized report (a la, Windows 7)
|
106
|
+
#
|
107
|
+
# For tesseract-extracted text
|
108
|
+
# else
|
109
|
+
# Headers.each do |header|
|
110
|
+
# current_header = line[Regexp.new("^#{Regexp.escape(header)}")]
|
111
|
+
# break if current_header
|
112
|
+
# end
|
113
|
+
# if current_header
|
114
|
+
# line = line.gsub(Regexp.new("^#{Regexp.escape(current_header)}"), '').strip
|
115
|
+
# csv_hash[current_header] = clean(line, current_header)
|
116
|
+
# current_header = nil
|
117
|
+
# end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
Headers.map { |header| csv_hash[header] || '0' }
|
121
|
+
end
|
122
|
+
|
123
|
+
#
|
124
|
+
# Return square footage only when given spacial data
|
125
|
+
#
|
126
|
+
# @param string - the text data to be cleaned
|
127
|
+
# @param string - the header associated with the text data
|
128
|
+
#
|
129
|
+
# @return string
|
130
|
+
#
|
131
|
+
def self.clean(text, header)
|
132
|
+
case header
|
133
|
+
when 'Current Assessed Value'
|
134
|
+
text.gsub!(/,/, '')
|
135
|
+
when 'Assessable Land Area',
|
136
|
+
'Total Living Area Above Grade',
|
137
|
+
'Living Area Below Grade',
|
138
|
+
'Garage Area'
|
139
|
+
text = text.split(' ')[0].gsub(/,/, '')
|
140
|
+
when 'Basement Suite',
|
141
|
+
'Walkout Basement',
|
142
|
+
'Constructed On Original Foundation',
|
143
|
+
'Modified For Disabled',
|
144
|
+
'Old House On New Foundation',
|
145
|
+
'Basementless',
|
146
|
+
'Penthouse',
|
147
|
+
'Market Adjustment'
|
148
|
+
text = text == 'Yes' ? 'T' : 'F'
|
149
|
+
end
|
150
|
+
text
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
data/proptax.gemspec
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "proptax/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "proptax"
|
8
|
+
spec.version = Proptax::VERSION
|
9
|
+
spec.authors = ["Daniel Bidulock"]
|
10
|
+
spec.email = ["daniel@capitolhill.ca"]
|
11
|
+
|
12
|
+
spec.summary = %q{Automatically process and visualize residential property data collected by the City of Calgary in 2018.}
|
13
|
+
spec.description = %q{This software produces reports from property data collected and provided by the City of Calgary. The versions used in previous years have won three victories before Calgary's Assessment Review Board, the quasi-judicial body that rules on property tax appeals.}
|
14
|
+
spec.homepage = "https://github.com/TaxReformYYC/report-generator-2018"
|
15
|
+
|
16
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
17
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
18
|
+
# if spec.respond_to?(:metadata)
|
19
|
+
# spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
|
20
|
+
# else
|
21
|
+
# raise "RubyGems 2.0 or newer is required to protect against " \
|
22
|
+
# "public gem pushes."
|
23
|
+
# end
|
24
|
+
|
25
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
26
|
+
f.match(%r{^(test|spec|features)/})
|
27
|
+
end
|
28
|
+
spec.bindir = "exe"
|
29
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
30
|
+
spec.require_paths = ["lib"]
|
31
|
+
|
32
|
+
spec.add_development_dependency "bundler", "~> 1.15"
|
33
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
34
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
35
|
+
spec.add_development_dependency "aruba", "~> 0.14.3"
|
36
|
+
|
37
|
+
spec.add_dependency "thor"
|
38
|
+
end
|
metadata
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: proptax
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Daniel Bidulock
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-01-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.15'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.15'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: aruba
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.14.3
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.14.3
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: thor
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: This software produces reports from property data collected and provided
|
84
|
+
by the City of Calgary. The versions used in previous years have won three victories
|
85
|
+
before Calgary's Assessment Review Board, the quasi-judicial body that rules on
|
86
|
+
property tax appeals.
|
87
|
+
email:
|
88
|
+
- daniel@capitolhill.ca
|
89
|
+
executables:
|
90
|
+
- proptax
|
91
|
+
extensions: []
|
92
|
+
extra_rdoc_files: []
|
93
|
+
files:
|
94
|
+
- ".gitignore"
|
95
|
+
- ".rspec"
|
96
|
+
- ".travis.yml"
|
97
|
+
- Gemfile
|
98
|
+
- LICENSE
|
99
|
+
- README.md
|
100
|
+
- Rakefile
|
101
|
+
- bin/console
|
102
|
+
- bin/setup
|
103
|
+
- exe/proptax
|
104
|
+
- lib/R/filter_csv.R
|
105
|
+
- lib/proptax.rb
|
106
|
+
- lib/proptax/cli.rb
|
107
|
+
- lib/proptax/generators/report.rb
|
108
|
+
- lib/proptax/generators/report/cherry-picked.Rmd
|
109
|
+
- lib/proptax/generators/report/default.Rmd
|
110
|
+
- lib/proptax/version.rb
|
111
|
+
- proptax.gemspec
|
112
|
+
homepage: https://github.com/TaxReformYYC/report-generator-2018
|
113
|
+
licenses: []
|
114
|
+
metadata: {}
|
115
|
+
post_install_message:
|
116
|
+
rdoc_options: []
|
117
|
+
require_paths:
|
118
|
+
- lib
|
119
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '0'
|
124
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
125
|
+
requirements:
|
126
|
+
- - ">="
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: '0'
|
129
|
+
requirements: []
|
130
|
+
rubyforge_project:
|
131
|
+
rubygems_version: 2.6.8
|
132
|
+
signing_key:
|
133
|
+
specification_version: 4
|
134
|
+
summary: Automatically process and visualize residential property data collected by
|
135
|
+
the City of Calgary in 2018.
|
136
|
+
test_files: []
|