Skip to content

Commit e0e5b2b

Browse files
committed
geocode in progress
1 parent b753126 commit e0e5b2b

File tree

9 files changed

+228
-2
lines changed

9 files changed

+228
-2
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import type { HttpErrorResponse } from '@angular/common/http'
2+
import { HttpClient } from '@angular/common/http'
3+
import { inject, Injectable } from '@angular/core'
4+
import { ErrorService } from '@seed/services'
5+
import type { InventoryType } from 'app/modules/inventory'
6+
import type { Observable } from 'rxjs'
7+
import { catchError } from 'rxjs'
8+
import type { ConfidenceSummary, GeocodingColumns } from './geocode.types'
9+
10+
@Injectable({ providedIn: 'root' })
11+
export class GeocodeService {
12+
private _httpClient = inject(HttpClient)
13+
private _errorService = inject(ErrorService)
14+
15+
geocode(orgId: number, viewIds: number[], type: InventoryType): Observable<unknown> {
16+
const url = `/api/v3/geocode/geocode_by_ids/&organization_id=${orgId}`
17+
const data = {
18+
property_view_ids: type === 'taxlots' ? [] : viewIds,
19+
taxlot_view_ids: type === 'taxlots' ? viewIds : [],
20+
}
21+
return this._httpClient.post(url, data)
22+
.pipe(
23+
catchError((error: HttpErrorResponse) => {
24+
return this._errorService.handleError(error, 'Geocode Error')
25+
}),
26+
)
27+
}
28+
29+
confidenceSummary(orgId: number, viewIds: number[], type: InventoryType): Observable<ConfidenceSummary> {
30+
const url = `/api/v3/geocode/confidence_summary/?organization_id=${orgId}`
31+
const data = {
32+
property_view_ids: type === 'taxlots' ? [] : viewIds,
33+
taxlot_view_ids: type === 'taxlots' ? viewIds : [],
34+
}
35+
return this._httpClient.post<ConfidenceSummary>(url, data)
36+
.pipe(
37+
catchError((error: HttpErrorResponse) => {
38+
return this._errorService.handleError(error, 'Geocode Confidence Summary Error')
39+
}),
40+
)
41+
}
42+
43+
checkApiKey(orgId: number): Observable<boolean> {
44+
const url = `/api/v3/organizations/${orgId}/geocode_api_key_exists/`
45+
return this._httpClient.get<boolean>(url)
46+
.pipe(
47+
catchError((error: HttpErrorResponse) => {
48+
return this._errorService.handleError(error, 'Geocode API Key Check Error')
49+
}),
50+
)
51+
}
52+
53+
geocodingEnabled(orgId: number): Observable<boolean> {
54+
const url = `/api/v3/organizations/${orgId}/geocoding_enabled/`
55+
return this._httpClient.get<boolean>(url)
56+
.pipe(
57+
catchError((error: HttpErrorResponse) => {
58+
return this._errorService.handleError(error, 'Geocoding Enabled Check Error')
59+
}),
60+
)
61+
}
62+
63+
geocodingColumns(orgId: number): Observable<GeocodingColumns> {
64+
const url = `/api/v3/organizations/${orgId}/geocoding_columns/`
65+
return this._httpClient.get<GeocodingColumns>(url)
66+
.pipe(
67+
catchError((error: HttpErrorResponse) => {
68+
return this._errorService.handleError(error, 'Geocoding Columns Error')
69+
}),
70+
)
71+
}
72+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
export type ConfidenceSummary = {
2+
properties: InventoryConfidenceSummary;
3+
taxlots: InventoryConfidenceSummary;
4+
}
5+
6+
export type InventoryConfidenceSummary = {
7+
census_geocoder: number;
8+
high_confidence: number;
9+
low_confidence: number;
10+
manual: number;
11+
missing_address_components: number;
12+
not_geocoded: number;
13+
}
14+
15+
export type GeocodingColumns = {
16+
PropertyState: string[]; // column_name
17+
TaxLotState: string[];
18+
}

src/@seed/api/geocode/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './geocode.service'
2+
export * from './geocode.types'

src/@seed/api/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export * from './cycle'
77
export * from './data-quality'
88
export * from './dataset'
99
export * from './derived-column'
10+
export * from './geocode'
1011
export * from './groups'
1112
export * from './inventory'
1213
export * from './label'
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<seed-modal-header
2+
title="Geocode Inventory"
3+
titleIcon="fa-solid:map"
4+
[close]="close.bind(this)"
5+
>
6+
</seed-modal-header>
7+
8+
<!-- CONTENT -->
9+
<div *transloco="let t">
10+
@if (!hasApiKey) {
11+
<seed-alert type="error" appearance="outline" showIcon="false" class="m-1">
12+
<div class="my-1">
13+
{{ t('NO_MAPQUEST_API_KEY_FOR_ORG') }}
14+
</div>
15+
<div class="my-1 text-red-secondary">
16+
{{ t('DIRECTIONS_FOR_UPDATING_MQ_API_KEY') }}
17+
</div>
18+
</seed-alert>
19+
}
20+
21+
@if (!geocodingEnabled) {
22+
<seed-alert type="error" appearance="outline" showIcon="false" class="m-1">
23+
{{ t('Geocoding has been disabled for this organization.') }}
24+
</seed-alert>
25+
}
26+
27+
<!-- @if (suggestVerify) {
28+
<div translate>{{ t('SUGGEST_TO_VERIFY') }}</div>
29+
} -->
30+
31+
<!-- @if (notGeocoded) {
32+
33+
} -->
34+
35+
36+
</div>
37+
38+
<div class="mt-4 flex justify-end gap-2">
39+
<button [disabled]="!valid" (click)="onSubmit()" mat-raised-button color="primary">Geocode</button>
40+
</div>
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import type { OnDestroy, OnInit} from '@angular/core'
2+
import { Component, inject } from '@angular/core'
3+
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'
4+
import { GeocodeService } from '@seed/api'
5+
import type { ConfidenceSummary, GeocodingColumns } from '@seed/api/geocode/geocode.types'
6+
import { AlertComponent, ModalHeaderComponent } from '@seed/components'
7+
import { SharedImports } from '@seed/directives'
8+
import { MaterialImports } from '@seed/materials'
9+
import type { InventoryType } from 'app/modules/inventory/inventory.types'
10+
import { forkJoin, Subject, tap } from 'rxjs'
11+
12+
@Component({
13+
selector: 'seed-geocode-modal',
14+
templateUrl: './geocode-modal.component.html',
15+
imports: [AlertComponent, MaterialImports, ModalHeaderComponent, SharedImports],
16+
})
17+
export class GeocodeModalComponent implements OnInit, OnDestroy {
18+
private _dialogRef = inject(MatDialogRef<GeocodeModalComponent>)
19+
private _geocodeService = inject(GeocodeService)
20+
private _unsubscribeAll$ = new Subject<void>()
21+
22+
confidenceSummary: ConfidenceSummary
23+
geocodingEnabled = true
24+
hasApiKey = true
25+
hasEnoughGeoCols = true
26+
hasGeoColumns = true
27+
suggestVerify = true
28+
notGeocoded = false
29+
30+
geocodeState: 'verify' | 'geocode' | 'result' | 'fail' = 'verify'
31+
32+
data = inject(MAT_DIALOG_DATA) as {
33+
orgId: number;
34+
viewIds: number[];
35+
type: InventoryType;
36+
}
37+
38+
get valid() {
39+
return this.hasApiKey && this.geocodingEnabled && this.hasGeoColumns
40+
}
41+
42+
ngOnInit(): void {
43+
this.getGeocodeConfig()
44+
}
45+
46+
getGeocodeConfig() {
47+
forkJoin([
48+
this._geocodeService.checkApiKey(this.data.orgId),
49+
this._geocodeService.geocodingEnabled(this.data.orgId),
50+
this._geocodeService.geocodingColumns(this.data.orgId),
51+
this._geocodeService.confidenceSummary(this.data.orgId, this.data.viewIds, this.data.type),
52+
]).pipe(
53+
tap(([hasApiKey, geocodingEnabled, geoColumns, confidenceSummary]) => {
54+
this.hasApiKey = hasApiKey
55+
this.geocodingEnabled = geocodingEnabled
56+
this.processGeoColumns(geoColumns)
57+
this.processConfidenceSummary(confidenceSummary)
58+
this.suggestVerify = hasApiKey && this.hasGeoColumns && this.geocodeState === 'verify'
59+
}),
60+
).subscribe()
61+
}
62+
63+
processGeoColumns({ PropertyState, TaxLotState }: GeocodingColumns) {
64+
this.hasGeoColumns = this.data.type === 'taxlots' ? TaxLotState.length > 0 : PropertyState.length > 0
65+
}
66+
67+
processConfidenceSummary(confidenceSummary: ConfidenceSummary) {
68+
this.confidenceSummary = confidenceSummary
69+
// Process the confidence summary as needed
70+
}
71+
72+
close(success = false) {
73+
this._dialogRef.close(success)
74+
}
75+
76+
onSubmit() {
77+
console.log('submit')
78+
}
79+
80+
ngOnDestroy(): void {
81+
this._unsubscribeAll$.next()
82+
this._unsubscribeAll$.complete()
83+
}
84+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
export * from './geocode-modal.component'
12
export * from './refresh-metadata-modal.component'
23
export * from './update-derived-data.component'

src/app/modules/inventory-list/list/grid/actions.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@
8686
(action)="tempAction()"
8787
label="FEMP CTS Export"
8888
></seed-menu-item>
89-
<seed-menu-item class="text-secondary line-through" [disabled]="!hasSelection" (action)="tempAction()" label="Geocode"></seed-menu-item>
89+
<seed-menu-item [disabled]="!hasSelection" (action)="openGeocodeModal()" label="Geocode"></seed-menu-item>
9090
<seed-menu-item
9191
[disabled]="!hasSelection"
9292
(action)="openRefreshMetadataModal()"

src/app/modules/inventory-list/list/grid/actions.component.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { ModalComponent } from 'app/modules/column-list-profile/modal/modal.comp
1111
import { DQCStartModalComponent } from 'app/modules/data-quality'
1212
import { AliChangeModalComponent, AnalysisRunModalComponent, ExportModalComponent, GroupsModalComponent, LabelsModalComponent } from 'app/modules/inventory/actions'
1313
import type { InventoryType, Profile } from '../../../inventory/inventory.types'
14-
import { RefreshMetadataModalComponent, UpdateDerivedDataComponent } from '../actions'
14+
import { GeocodeModalComponent, RefreshMetadataModalComponent, UpdateDerivedDataComponent } from '../actions'
1515

1616
@Component({
1717
selector: 'seed-inventory-grid-actions',
@@ -186,6 +186,14 @@ export class ActionsComponent implements OnDestroy, OnChanges, OnInit {
186186
this.afterClosed(dialogRef)
187187
}
188188

189+
openGeocodeModal() {
190+
const dialogRef = this._dialog.open(GeocodeModalComponent, {
191+
width: '40rem',
192+
data: this.baseData(),
193+
})
194+
this.afterClosed(dialogRef)
195+
}
196+
189197
afterClosed(dialogRef: MatDialogRef<unknown>) {
190198
dialogRef.afterClosed().pipe(
191199
filter(Boolean),

0 commit comments

Comments
 (0)