Skip to content

Commit 696e1eb

Browse files
author
Nick Grippin
authored
Merge pull request cfpb#1140 from schbetsy/reports-metadata
Reports Metadata
2 parents 0b4d02a + 703af60 commit 696e1eb

File tree

6 files changed

+115
-52
lines changed

6 files changed

+115
-52
lines changed

model/shared/src/main/scala/hmda/model/publication/reports/ReportTypeEnum.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ object ReportTypeEnum extends Enum[ReportTypeEnum] {
88

99
val values = findValues
1010

11+
val byName: Map[String, ReportTypeEnum] = Map(
12+
"disclosure" -> Disclosure,
13+
"aggregate" -> Aggregate,
14+
"national aggregate" -> NationalAggregate
15+
)
16+
1117
case object Disclosure extends ReportTypeEnum
1218
case object Aggregate extends ReportTypeEnum
1319
case object NationalAggregate extends ReportTypeEnum
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ReportId,Type,ReportNumber,Dispositions,Description
2+
D51,Disclosure,5-1,Received;Originated;ApprovedButNotAccepted;Denied;Withdrawn;Closed,"Disposition of applications for FHA, FSA/RHS, and VA home-purchase loans, 1- to 4-family and manufactured home dwellings, by income, race and ethnicity of applicant"

publication/src/main/scala/hmda/publication/reports/disclosure/D51.scala

Lines changed: 21 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -8,59 +8,27 @@ import hmda.model.publication.reports.ApplicantIncomeEnum._
88
import hmda.publication.reports._
99
import hmda.model.publication.reports._
1010
import hmda.publication.reports.util.DateUtil._
11-
import hmda.publication.reports.util.DispositionType._
1211
import hmda.publication.reports.util.ReportUtil._
12+
import hmda.publication.reports.util.ReportsMetaDataLookup
1313
import hmda.query.model.filing.LoanApplicationRegisterQuery
1414

1515
import scala.concurrent.Future
1616

1717
case class D51(
1818
respondentId: String,
1919
institutionName: String,
20-
table: String,
21-
description: String,
2220
year: Int,
2321
reportDate: String,
2422
msa: MSAReport,
2523
applicantIncomes: List[ApplicantIncome],
26-
total: List[Disposition]
24+
total: List[Disposition],
25+
table: String = D51.metaData.reportTable,
26+
description: String = D51.metaData.description
2727
) extends DisclosureReport
2828

2929
object D51 {
30-
def apply(
31-
respondentId: String,
32-
institutionName: String,
33-
year: Int,
34-
reportDate: String,
35-
msa: MSAReport,
36-
applicantIncomes: List[ApplicantIncome],
37-
total: List[Disposition]
38-
): D51 = {
39-
40-
val description = "Disposition of applications for FHA, FSA/RHS, and VA home-purchase loans, 1- to 4-family and manufactured home dwellings, by income, race and ethnicity of applicant"
41-
42-
D51(
43-
respondentId,
44-
institutionName,
45-
"5-1",
46-
description,
47-
year,
48-
reportDate,
49-
msa,
50-
applicantIncomes,
51-
total
52-
)
53-
}
54-
55-
val dispositions: List[DispositionType] =
56-
List(
57-
ReceivedDisp,
58-
OriginatedDisp,
59-
ApprovedButNotAcceptedDisp,
60-
DeniedDisp,
61-
WithdrawnDisp,
62-
ClosedDisp
63-
)
30+
val metaData = ReportsMetaDataLookup.values("D51")
31+
val dispositions = metaData.dispositions
6432

6533
// Table filters:
6634
// Loan Type 2,3,4
@@ -69,12 +37,12 @@ object D51 {
6937
def generate[ec: EC, mat: MAT, as: AS](
7038
larSource: Source[LoanApplicationRegisterQuery, NotUsed],
7139
fipsCode: Int,
72-
respId: String,
40+
respondentId: String,
7341
institutionNameF: Future[String]
7442
): Future[D51] = {
7543

7644
val lars = larSource
77-
.filter(lar => lar.respondentId == respId)
45+
.filter(lar => lar.respondentId == respondentId)
7846
.filter(lar => lar.msa != "NA")
7947
.filter(lar => lar.msa.toInt == fipsCode)
8048
.filter { lar =>
@@ -91,7 +59,7 @@ object D51 {
9159
val larsByIncome = larsByIncomeInterval(larsWithIncome, incomeIntervals)
9260
val borrowerCharacteristicsByIncomeF = borrowerCharacteristicsByIncomeInterval(larsByIncome, dispositions)
9361

94-
val dateF = calculateYear(larSource)
62+
val yearF = calculateYear(larSource)
9563
val totalF = calculateDispositions(lars, dispositions)
9664

9765
for {
@@ -102,7 +70,7 @@ object D51 {
10270
lars120BorrowerCharacteristics <- borrowerCharacteristicsByIncomeF(GreaterThan120PercentOfMSAMedian)
10371

10472
institutionName <- institutionNameF
105-
date <- dateF
73+
year <- yearF
10674
total <- totalF
10775
} yield {
10876
val income50 = ApplicantIncome(
@@ -126,19 +94,21 @@ object D51 {
12694
lars120BorrowerCharacteristics
12795
)
12896

97+
val applicantIncomes = List(
98+
income50,
99+
income50To79,
100+
income80To99,
101+
income100To120,
102+
income120
103+
)
104+
129105
D51(
130-
respId,
106+
respondentId,
131107
institutionName,
132-
date,
108+
year,
133109
formatDate(Calendar.getInstance().toInstant),
134110
msa,
135-
List(
136-
income50,
137-
income50To79,
138-
income80To99,
139-
income100To120,
140-
income120
141-
),
111+
applicantIncomes,
142112
total
143113
)
144114
}

publication/src/main/scala/hmda/publication/reports/util/DispositionType.scala

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,20 @@ import scala.util.Try
1313

1414
object DispositionType {
1515

16+
val byName: Map[String, DispositionType] = {
17+
Map(
18+
"received" -> ReceivedDisp,
19+
"originated" -> OriginatedDisp,
20+
"approvedbutnotaccepted" -> ApprovedButNotAcceptedDisp,
21+
"denied" -> DeniedDisp,
22+
"withdrawn" -> WithdrawnDisp,
23+
"closed" -> ClosedDisp,
24+
"purchased" -> PurchasedDisp,
25+
"preapprovaldenied" -> PreapprovalDeniedDisp,
26+
"preapprovalapproved" -> PreapprovalApprovedDisp
27+
)
28+
}
29+
1630
sealed trait DispositionType extends SourceUtils {
1731
def filter(lar: LoanApplicationRegisterQuery): Boolean
1832

@@ -67,7 +81,7 @@ object DispositionType {
6781
override def filter(lar: LoanApplicationRegisterQuery): Boolean = lar.actionTakenType == 7
6882
override def actionTaken: ActionTakenTypeEnum = PreapprovalDenied
6983
}
70-
object PreapprovalAcceptedDisp extends DispositionType {
84+
object PreapprovalApprovedDisp extends DispositionType {
7185
override def filter(lar: LoanApplicationRegisterQuery): Boolean = lar.actionTakenType == 8
7286
override def actionTaken: ActionTakenTypeEnum = PreapprovalApprovedButNotAccepted
7387
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package hmda.publication.reports.util
2+
3+
import com.github.tototoshi.csv.CSVParser.parse
4+
import hmda.model.ResourceUtils
5+
import hmda.model.publication.reports.ReportTypeEnum
6+
import hmda.publication.reports.util.DispositionType.DispositionType
7+
8+
object ReportsMetaDataLookup extends ResourceUtils {
9+
10+
private val lines = resourceLines("/reports-metadata.txt")
11+
12+
val values: Map[String, ReportMetaData] = lines.drop(1).map { line =>
13+
ReportMetaData.fromString(line)
14+
}.toMap
15+
16+
}
17+
18+
case class ReportMetaData(
19+
reportType: ReportTypeEnum,
20+
reportTable: String,
21+
dispositions: List[DispositionType],
22+
description: String
23+
)
24+
25+
case object ReportMetaData {
26+
27+
def fromString(line: String): (String, ReportMetaData) = {
28+
val values = parse(line, '\\', ',', '"').getOrElse(List())
29+
val reportId = values.head
30+
val reportType = ReportTypeEnum.byName(values(1).toLowerCase)
31+
val reportNumber = values(2)
32+
val description = values(4)
33+
val dispositions =
34+
values(3).split(";").filter(_.nonEmpty).map { d =>
35+
DispositionType.byName(d.toLowerCase)
36+
}.toList
37+
38+
val data = ReportMetaData(
39+
reportType,
40+
reportNumber,
41+
dispositions,
42+
description
43+
)
44+
45+
(reportId, data)
46+
}
47+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package hmda.publication.reports.util
2+
3+
import hmda.model.publication.reports.ReportTypeEnum.Disclosure
4+
import hmda.publication.reports.util.DispositionType._
5+
import org.scalatest.{ MustMatchers, WordSpec }
6+
7+
class ReportsMetaDataLookupSpec extends WordSpec with MustMatchers {
8+
9+
"Provide A&D report metadata" in {
10+
val d51 = ReportsMetaDataLookup.values("D51")
11+
d51 mustBe a[ReportMetaData]
12+
d51.reportType mustBe Disclosure
13+
d51.reportTable mustBe "5-1"
14+
d51.dispositions mustBe List(
15+
ReceivedDisp,
16+
OriginatedDisp,
17+
ApprovedButNotAcceptedDisp,
18+
DeniedDisp,
19+
WithdrawnDisp,
20+
ClosedDisp
21+
)
22+
}
23+
24+
}

0 commit comments

Comments
 (0)