Skip to content

Commit ef1308a

Browse files
authored
Add data and meter uploads via spreadsheet (#49)
* refactor for ag grid * temp - register ag gird in main.ts * renove unused class * datasets grid * rename * route rename to datasets * dataset grid, dummy actions * icons * basic upload to mapping * ptl data upload modal fxnal * import data to mapping * help component * mapping table dev * debug * mapping table fxns and styles * validate data mapping * save mapping step * uploaded data, step4 issues * link to ptl step4 * subprogress fxnal * whole xls upload fxnal * styles * autocomplete not subset * steps not editable * select all count * rounded btns * styles * data quality results modal * fetch and apply profile * save profi;e * profiles fxnal * hook up delete taxlots from inv list * upload props and taxlots related * show both taxlot and property results * several bug fixes, features, styles * prettier * lint * shared modal header, reorg, base for meter upload * meter preview * step2 grid * progress loop default vals * meter upload from data page * meter upload fxnal * lint lint * wording, success notifications, remove mapping pagination * prevent overlapping uploads * lint * remove bedes help * default to fileHeader * reuse inv file for meters
1 parent e874eaa commit ef1308a

File tree

101 files changed

+4043
-340
lines changed

Some content is hidden

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

101 files changed

+4043
-340
lines changed

cspell.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
],
2121
"useGitignore": true,
2222
"words": [
23+
"BEDES",
2324
"CEJST",
2425
"eeej",
2526
"EPSG",
@@ -32,6 +33,7 @@
3233
"SRID",
3334
"Syncr",
3435
"ubids",
36+
"unlinkable",
3537
"unpair",
3638
"Unpair",
3739
"unpairing",

src/@seed/api/column_mapping_profile/column_mapping_profile.service.ts

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@ import type { HttpErrorResponse } from '@angular/common/http'
22
import { HttpClient } from '@angular/common/http'
33
import { inject, Injectable } from '@angular/core'
44
import type { Observable } from 'rxjs'
5-
import { catchError, map, ReplaySubject } from 'rxjs'
5+
import { catchError, map, ReplaySubject, tap } from 'rxjs'
66
import { ErrorService } from '@seed/services'
7+
import { SnackBarService } from 'app/core/snack-bar/snack-bar.service'
78
import { UserService } from '../user'
89
import type {
910
ColumnMapping,
1011
ColumnMappingProfile,
1112
ColumnMappingProfileDeleteResponse,
1213
ColumnMappingProfilesRequest,
14+
ColumnMappingProfileType,
1315
ColumnMappingProfileUpdateResponse,
1416
ColumnMappingSuggestionResponse,
1517
} from './column_mapping_profile.types'
@@ -20,6 +22,7 @@ export class ColumnMappingProfileService {
2022
private _userService = inject(UserService)
2123
private _profiles = new ReplaySubject<ColumnMappingProfile[]>(1)
2224
private _errorService = inject(ErrorService)
25+
private _snackBar = inject(SnackBarService)
2326

2427
profiles$ = this._profiles.asObservable()
2528

@@ -30,9 +33,13 @@ export class ColumnMappingProfileService {
3033
})
3134
}
3235

33-
getProfiles(org_id: number): Observable<ColumnMappingProfile[]> {
34-
const url = `/api/v3/column_mapping_profiles/filter/?organization_id=${org_id}`
35-
return this._httpClient.post<ColumnMappingProfilesRequest>(url, {}).pipe(
36+
getProfiles(orgId: number, columnMappingProfileTypes: ColumnMappingProfileType[] = []): Observable<ColumnMappingProfile[]> {
37+
const url = `/api/v3/column_mapping_profiles/filter/?organization_id=${orgId}`
38+
const data: Record<string, unknown> = {}
39+
if (columnMappingProfileTypes.length) {
40+
data.profile_type = columnMappingProfileTypes
41+
}
42+
return this._httpClient.post<ColumnMappingProfilesRequest>(url, data).pipe(
3643
map((response) => {
3744
this._profiles.next(response.data)
3845
return response.data
@@ -44,8 +51,8 @@ export class ColumnMappingProfileService {
4451
)
4552
}
4653

47-
updateMappings(org_id: number, profile_id: number, mappings: ColumnMapping[]): Observable<ColumnMappingProfile> {
48-
const url = `/api/v3/column_mapping_profiles/${profile_id}/?organization_id=${org_id}`
54+
updateMappings(orgId: number, profile_id: number, mappings: ColumnMapping[]): Observable<ColumnMappingProfile> {
55+
const url = `/api/v3/column_mapping_profiles/${profile_id}/?organization_id=${orgId}`
4956
return this._httpClient.put<ColumnMappingProfileUpdateResponse>(url, { mappings }).pipe(
5057
map((response) => {
5158
return response.data
@@ -56,20 +63,21 @@ export class ColumnMappingProfileService {
5663
)
5764
}
5865

59-
update(org_id: number, profile: ColumnMappingProfile): Observable<ColumnMappingProfile> {
60-
const url = `/api/v3/column_mapping_profiles/${profile.id}/?organization_id=${org_id}`
61-
return this._httpClient.put<ColumnMappingProfileUpdateResponse>(url, { name: profile.name }).pipe(
66+
update(orgId: number, profile: ColumnMappingProfile): Observable<ColumnMappingProfile> {
67+
const url = `/api/v3/column_mapping_profiles/${profile.id}/?organization_id=${orgId}`
68+
return this._httpClient.put<ColumnMappingProfileUpdateResponse>(url, profile).pipe(
6269
map((response) => {
6370
return response.data
6471
}),
72+
tap(() => { this._snackBar.success('Profile updated successfully') }),
6573
catchError((error: HttpErrorResponse) => {
6674
return this._errorService.handleError(error, 'Error updating profile')
6775
}),
6876
)
6977
}
7078

71-
delete(org_id: number, profile_id: number): Observable<ColumnMappingProfileDeleteResponse> {
72-
const url = `/api/v3/column_mapping_profiles/${profile_id}/?organization_id=${org_id}`
79+
delete(orgId: number, profile_id: number): Observable<ColumnMappingProfileDeleteResponse> {
80+
const url = `/api/v3/column_mapping_profiles/${profile_id}/?organization_id=${orgId}`
7381
return this._httpClient.delete<ColumnMappingProfileDeleteResponse>(url).pipe(
7482
map((response) => {
7583
return response
@@ -80,17 +88,18 @@ export class ColumnMappingProfileService {
8088
)
8189
}
8290

83-
create(org_id: number, profile: ColumnMappingProfile): Observable<ColumnMappingProfileUpdateResponse> {
84-
const url = `/api/v3/column_mapping_profiles/?organization_id=${org_id}`
91+
create(orgId: number, profile: ColumnMappingProfile): Observable<ColumnMappingProfileUpdateResponse> {
92+
const url = `/api/v3/column_mapping_profiles/?organization_id=${orgId}`
8593
return this._httpClient.post<ColumnMappingProfileUpdateResponse>(url, { ...profile }).pipe(
94+
tap(() => { this._snackBar.success('Profile created successfully') }),
8695
catchError((error: HttpErrorResponse) => {
8796
return this._errorService.handleError(error, 'Error creating profile')
8897
}),
8998
)
9099
}
91100

92-
export(org_id: number, profile_id: number) {
93-
const url = `/api/v3/column_mapping_profiles/${profile_id}/csv/?organization_id=${org_id}`
101+
export(orgId: number, profile_id: number) {
102+
const url = `/api/v3/column_mapping_profiles/${profile_id}/csv/?organization_id=${orgId}`
94103
return this._httpClient.get(url, { responseType: 'text' }).pipe(
95104
map((response) => {
96105
return new Blob([response], { type: 'text/csv;charset: utf-8' })
@@ -101,8 +110,8 @@ export class ColumnMappingProfileService {
101110
)
102111
}
103112

104-
suggestions(org_id: number, headers: string[]) {
105-
const url = `/api/v3/column_mapping_profiles/suggestions/?organization_id=${org_id}`
113+
suggestions(orgId: number, headers: string[]) {
114+
const url = `/api/v3/column_mapping_profiles/suggestions/?organization_id=${orgId}`
106115
return this._httpClient.post<ColumnMappingSuggestionResponse>(url, { headers }).pipe(
107116
map((response) => {
108117
return response.data

src/@seed/api/column_mapping_profile/column_mapping_profile.types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,5 @@ export type ColumnMappingSuggestionResponse = {
3333
status: string;
3434
data: Record<string, ['PropertyState' | 'TaxLotState', string, number]>;
3535
}
36+
37+
export type ColumnMappingProfileType = 'Normal' | 'BuildingSync Default' | 'BuildingSync Custom'

src/@seed/api/cycle/cycle.service.ts

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ import { HttpClient } from '@angular/common/http'
33
import { inject, Injectable } from '@angular/core'
44
import type { Observable } from 'rxjs'
55
import { BehaviorSubject, catchError, map, take, tap } from 'rxjs'
6-
import { OrganizationService } from '@seed/api/organization'
76
import { ErrorService } from '@seed/services'
87
import { SnackBarService } from 'app/core/snack-bar/snack-bar.service'
8+
import { UserService } from '../user'
99
import type { Cycle, CycleResponse, CyclesResponse } from './cycle.types'
1010

1111
@Injectable({ providedIn: 'root' })
1212
export class CycleService {
1313
private _httpClient = inject(HttpClient)
14-
private _organizationService = inject(OrganizationService)
14+
private _userService = inject(UserService)
1515
private _snackBar = inject(SnackBarService)
1616
private _errorService = inject(ErrorService)
1717
private _cycles = new BehaviorSubject<Cycle[]>([])
@@ -20,21 +20,20 @@ export class CycleService {
2020
cycles$ = this._cycles.asObservable()
2121

2222
constructor() {
23-
this._organizationService.currentOrganization$
23+
this._userService.currentOrganizationId$
2424
.pipe(
25-
tap(({ org_id }) => {
26-
this.get(org_id)
25+
tap((orgId) => {
26+
this.getCycles(orgId)
2727
}),
2828
)
2929
.subscribe()
3030
}
3131

32-
get(orgId: number) {
32+
getCycles(orgId: number) {
3333
const url = `/api/v3/cycles/?organization_id=${orgId}`
3434
this._httpClient
3535
.get<CyclesResponse>(url)
3636
.pipe(
37-
take(1),
3837
map(({ cycles }) => cycles),
3938
tap((cycles) => {
4039
this._cycles.next(cycles)
@@ -46,12 +45,25 @@ export class CycleService {
4645
.subscribe()
4746
}
4847

48+
getCycle(orgId: number, cycleId: number): Observable<Cycle> {
49+
const url = `/api/v3/cycles/${cycleId}?organization_id=${orgId}`
50+
return this._httpClient
51+
.get<CycleResponse>(url)
52+
.pipe(
53+
take(1),
54+
map(({ cycles }) => cycles),
55+
catchError((error: HttpErrorResponse) => {
56+
return this._errorService.handleError(error, 'Error fetching cycles')
57+
}),
58+
)
59+
}
60+
4961
post({ data, orgId }): Observable<CycleResponse | null> {
5062
const url = `/api/v3/cycles/?organization_id=${orgId}`
5163
return this._httpClient.post<CycleResponse>(url, data).pipe(
5264
tap((response) => {
5365
this._snackBar.success(`Created Cycle ${response.cycles.name}`)
54-
this.get(orgId as number)
66+
this.getCycles(orgId as number)
5567
}),
5668
catchError((error: HttpErrorResponse) => {
5769
return this._errorService.handleError(error, 'Error creating cycle')
@@ -64,7 +76,7 @@ export class CycleService {
6476
return this._httpClient.put<CycleResponse>(url, data).pipe(
6577
tap((response) => {
6678
this._snackBar.success(`Updated Cycle ${response.cycles.name}`)
67-
this.get(orgId as number)
79+
this.getCycles(orgId as number)
6880
}),
6981
catchError((error: HttpErrorResponse) => {
7082
return this._errorService.handleError(error, 'Error updating cycle')
@@ -76,7 +88,7 @@ export class CycleService {
7688
const url = `/api/v3/cycles/${id}/?organization_id=${orgId}`
7789
return this._httpClient.delete(url).pipe(
7890
tap(() => {
79-
this.get(orgId)
91+
this.getCycles(orgId)
8092
}),
8193
catchError((error: HttpErrorResponse) => {
8294
return this._errorService.handleError(error, 'Error deleting cycle')

src/@seed/api/data-quality/data-quality.service.ts

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { HttpClient, type HttpErrorResponse } from '@angular/common/http'
22
import { inject, Injectable } from '@angular/core'
3-
import { catchError, type Observable, ReplaySubject, switchMap, tap } from 'rxjs'
3+
import { catchError, map, type Observable, ReplaySubject, switchMap, tap } from 'rxjs'
44
import { OrganizationService } from '@seed/api/organization'
55
import { ErrorService } from '@seed/services'
66
import { SnackBarService } from 'app/core/snack-bar/snack-bar.service'
7-
import type { Rule } from './data-quality.types'
7+
import type { DQCProgressResponse } from '../progress'
8+
import type { DataQualityResults, DataQualityResultsResponse, Rule } from './data-quality.types'
89

910
@Injectable({ providedIn: 'root' })
1011
export class DataQualityService {
@@ -79,4 +80,38 @@ export class DataQualityService {
7980
}),
8081
)
8182
}
83+
84+
startDataQualityCheckForImportFile(orgId: number, importFileId: number): Observable<DQCProgressResponse> {
85+
const url = `/api/v3/import_files/${importFileId}/start_data_quality_checks/?organization_id=${orgId}`
86+
return this._httpClient.post<DQCProgressResponse>(url, {})
87+
.pipe(
88+
catchError((error: HttpErrorResponse) => {
89+
return this._errorService.handleError(error, 'Error starting data quality checks for import file')
90+
}),
91+
)
92+
}
93+
94+
startDataQualityCheckForOrg(orgId: number, property_view_ids: number[], taxlot_view_ids: number[], goal_id: number): Observable<DQCProgressResponse> {
95+
const url = `/api/v3/data_quality_checks/${orgId}/start/`
96+
const data = {
97+
property_view_ids,
98+
taxlot_view_ids,
99+
goal_id,
100+
}
101+
return this._httpClient.post<DQCProgressResponse>(url, data).pipe(
102+
catchError((error: HttpErrorResponse) => {
103+
return this._errorService.handleError(error, 'Error fetching data quality results for organization')
104+
}),
105+
)
106+
}
107+
108+
getDataQualityResults(orgId: number, runId: number): Observable<DataQualityResults[]> {
109+
const url = `/api/v3/data_quality_checks/results/?organization_id=${orgId}&run_id=${runId}`
110+
return this._httpClient.get<DataQualityResultsResponse>(url).pipe(
111+
map(({ data }) => data),
112+
catchError((error: HttpErrorResponse) => {
113+
return this._errorService.handleError(error, 'Error fetching data quality results')
114+
}),
115+
)
116+
}
82117
}

src/@seed/api/data-quality/data-quality.types.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,24 @@ export type UnitNames =
6060
| 'MJ/m²/year'
6161
| 'kWh/m²/year'
6262
| 'kBtu/m²/year'
63+
64+
export type DataQualityResultsResponse = {
65+
data: DataQualityResults[];
66+
}
67+
68+
export type DataQualityResults = {
69+
[key: string]: unknown;
70+
data_quality_results: DataQualityResult[];
71+
}
72+
73+
export type DataQualityResult = {
74+
condition: string;
75+
detailed_message: string;
76+
field: string;
77+
formatted_field: string;
78+
label: string;
79+
message: string;
80+
severity: string;
81+
table_name: string;
82+
value: unknown;
83+
}

0 commit comments

Comments
 (0)