Merge pull request 'Moved stuff around, changed the way I'm storing milestones data' (#1) from changes into main
Reviewed-on: #1
This commit was merged in pull request #1.
This commit is contained in:
@@ -1 +1,10 @@
|
||||
<fbi-chart></fbi-chart> <fbi-select></fbi-select>
|
||||
<div>
|
||||
<fbi-select label="Class" key="cohort" [data]="data.Cohorts"></fbi-select>
|
||||
<fbi-select label="Year" key="year" [data]="data.Years"></fbi-select>
|
||||
<fbi-select label="Grade" key="grade" [data]="data.Grades"></fbi-select>
|
||||
<fbi-select label="County" key="county" [data]="data.Counties"></fbi-select>
|
||||
<fbi-select label="School" key="school" [data]="data.Schools"></fbi-select>
|
||||
</div>
|
||||
<div>
|
||||
<fbi-table [header]="header" [rows]="rows"></fbi-table>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
div {
|
||||
flex: 1;
|
||||
flex-direction: row;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@@ -1,15 +1,49 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, OnInit, inject } from '@angular/core';
|
||||
import { RouterOutlet } from '@angular/router';
|
||||
import { ChartComponent } from '../components/chart/chart.component';
|
||||
import { SelectComponent } from '../components/select/select.component';
|
||||
import { DataService } from '../services/data.service';
|
||||
import { TableComponent } from '../components/table/table.component';
|
||||
import { Header } from '../components/table/header';
|
||||
import { FilterService } from '../services/filters.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
standalone: true,
|
||||
imports: [RouterOutlet, ChartComponent, SelectComponent],
|
||||
imports: [RouterOutlet, ChartComponent, SelectComponent, TableComponent],
|
||||
templateUrl: './app.component.html',
|
||||
styleUrl: './app.component.scss',
|
||||
})
|
||||
export class AppComponent {
|
||||
export class AppComponent implements OnInit {
|
||||
title = 'Georgia Milestones';
|
||||
|
||||
data = inject(DataService);
|
||||
filter = inject(FilterService);
|
||||
|
||||
header = [new Header({ label: 'School', source: 'school' })];
|
||||
rows: Record<string, any>[] = [];
|
||||
|
||||
ngOnInit(): void {
|
||||
// this.filter.filters$.subscribe((filters: KeyValue[]) => {
|
||||
// this.data.Data.pipe(take(1)).subscribe((data: Milestone[]) => {
|
||||
// const result = data.filter((d: Milestone) => {
|
||||
// const match = (filters ?? []).findIndex((f: KeyValue) => {
|
||||
// switch (f.key) {
|
||||
// case 'year':
|
||||
// return d.Year.toString() === f.value;
|
||||
// case 'grade':
|
||||
// return d.Grade.toString() === f.value;
|
||||
// case 'county':
|
||||
// return d.County === f.value;
|
||||
// case 'school':
|
||||
// return d.School === f.value;
|
||||
// }
|
||||
// return false;
|
||||
// });
|
||||
// return match > -1;
|
||||
// });
|
||||
// this.rows = result.map((r: Milestone) => ({ school: r.School }));
|
||||
// });
|
||||
// });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ export enum ChartType {
|
||||
export interface IDataset {
|
||||
type?: ChartType;
|
||||
borderColor?: string;
|
||||
label?: string;
|
||||
data: string[];
|
||||
backgroundColor?: string | string[];
|
||||
pointBackgroundColor?: string;
|
||||
@@ -20,6 +21,6 @@ export interface IDataset {
|
||||
|
||||
export interface IData {
|
||||
labels?: string[];
|
||||
labelsSource: string[];
|
||||
labelsSource?: string[];
|
||||
datasets: IDataset[];
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
OnDestroy,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import { Chart, ChartOptions } from 'chart.js';
|
||||
import { Chart, ChartOptions } from 'chart.js/auto';
|
||||
import { PluginNodata } from './plugin-nodata';
|
||||
import { PluginMoreColors } from './plugin-more-colors';
|
||||
import { ChartType, IData } from './chart-type';
|
||||
@@ -22,9 +22,20 @@ export class ChartComponent implements AfterViewInit, OnDestroy {
|
||||
|
||||
private chart: any = undefined;
|
||||
private data: IData = {
|
||||
labels: [],
|
||||
labelsSource: [],
|
||||
datasets: [],
|
||||
labels: Array.from(
|
||||
{ length: 10 },
|
||||
() => `${Math.floor(Math.random() * 100)}`
|
||||
),
|
||||
// labelsSource: [],
|
||||
datasets: [
|
||||
{
|
||||
label: 'test',
|
||||
data: Array.from(
|
||||
{ length: 10 },
|
||||
() => `${Math.floor(Math.random() * 100)}`
|
||||
),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
constructor() {}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<label for="select">{{ label }}</label>
|
||||
<select name="select">
|
||||
<option *ngFor="let item of data" [value]="item">{{ item }}</option>
|
||||
<select name="select" (change)="onChange($event)">
|
||||
<option selected disabled hidden value="">{{ label }}</option>
|
||||
<option *ngFor="let kv of data | async" [value]="kv.key">
|
||||
{{ kv.value }}
|
||||
</option>
|
||||
</select>
|
||||
<!-- <label for="select">{{ label }}</label> -->
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
:host {
|
||||
display: block;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
// label {
|
||||
// padding-right: 10px;
|
||||
// float: right;
|
||||
// }
|
||||
|
||||
select {
|
||||
width: 200px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { Component, Input, inject } from '@angular/core';
|
||||
import { DataService } from '../../services/data.service';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Observable } from 'rxjs';
|
||||
import { KeyValue } from '../../models/key-value';
|
||||
import { FilterService } from '../../services/filters.service';
|
||||
|
||||
@Component({
|
||||
selector: 'fbi-select',
|
||||
@@ -11,7 +13,12 @@ import { CommonModule } from '@angular/common';
|
||||
})
|
||||
export class SelectComponent {
|
||||
@Input() label!: string;
|
||||
@Input() data!: (string | number)[];
|
||||
@Input() key!: string;
|
||||
@Input() data!: Observable<KeyValue[]>;
|
||||
|
||||
private dataService = inject(DataService);
|
||||
private filterService = inject(FilterService);
|
||||
|
||||
onChange(event: any): void {
|
||||
this.filterService.set(this.key, event?.target?.value);
|
||||
}
|
||||
}
|
||||
|
||||
9
src/components/table/header.ts
Normal file
9
src/components/table/header.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export class Header {
|
||||
label: string;
|
||||
source: string;
|
||||
|
||||
constructor(data: Partial<Header>) {
|
||||
this.label = data?.label ?? '';
|
||||
this.source = data?.source ?? '';
|
||||
}
|
||||
}
|
||||
12
src/components/table/table.component.html
Normal file
12
src/components/table/table.component.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<table>
|
||||
<tr>
|
||||
<th *ngFor="let h of header">
|
||||
{{ h.label }}
|
||||
</th>
|
||||
</tr>
|
||||
<tr *ngFor="let row of rows; let index">
|
||||
<td *ngFor="let h of header">
|
||||
{{ row[h.source] }}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
0
src/components/table/table.component.scss
Normal file
0
src/components/table/table.component.scss
Normal file
23
src/components/table/table.component.spec.ts
Normal file
23
src/components/table/table.component.spec.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { TableComponent } from './table.component';
|
||||
|
||||
describe('TableComponent', () => {
|
||||
let component: TableComponent;
|
||||
let fixture: ComponentFixture<TableComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [TableComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(TableComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
15
src/components/table/table.component.ts
Normal file
15
src/components/table/table.component.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Header } from './header';
|
||||
|
||||
@Component({
|
||||
selector: 'fbi-table',
|
||||
standalone: true,
|
||||
imports: [CommonModule],
|
||||
templateUrl: './table.component.html',
|
||||
styleUrl: './table.component.scss',
|
||||
})
|
||||
export class TableComponent {
|
||||
@Input() header!: Header[];
|
||||
@Input() rows!: Record<string, any>[];
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
export enum Column {
|
||||
County = 2,
|
||||
School = 3,
|
||||
ELA_Count = 4,
|
||||
ELA_Mean = 5,
|
||||
ELA_0 = 6,
|
||||
ELA_1 = 7,
|
||||
ELA_2 = 8,
|
||||
ELA_3 = 9,
|
||||
Math_Count = 12,
|
||||
Math_Mean = 13,
|
||||
Math_0 = 14,
|
||||
Math_1 = 15,
|
||||
Math_2 = 16,
|
||||
Math_3 = 17,
|
||||
Sci_Count = 20,
|
||||
Sci_Mean = 21,
|
||||
Sci_0 = 22,
|
||||
Sci_1 = 23,
|
||||
Sci_2 = 24,
|
||||
Sci_3 = 25,
|
||||
Soc_Count = 28,
|
||||
Soc_Mean = 29,
|
||||
Soc_0 = 30,
|
||||
Soc_1 = 31,
|
||||
Soc_2 = 32,
|
||||
Soc_3 = 33,
|
||||
}
|
||||
30
src/enums/csv.ts
Normal file
30
src/enums/csv.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
export enum Csv {
|
||||
School_Code = 'School Code',
|
||||
School_Label = 'School Name',
|
||||
County_Code = 'System Code',
|
||||
County_Label = 'System Name',
|
||||
ELA_Count = 'English Language Arts Number Tested',
|
||||
ELA_Mean = 'English Language Arts Mean Scale Score',
|
||||
ELA_L0 = 'English Language Arts % Beginning Learner',
|
||||
ELA_L1 = 'English Language Arts % Developing Learner',
|
||||
ELA_L2 = 'English Language Arts % Proficient Learner',
|
||||
ELA_L3 = 'English Language Arts % Distinguished Learner',
|
||||
Math_Count = 'Mathematics Number Tested',
|
||||
Math_Mean = 'Mathematics Mean Scale Score',
|
||||
Math_L0 = 'Mathematics % Beginning Learner',
|
||||
Math_L1 = 'Mathematics % Developing Learner',
|
||||
Math_L2 = 'Mathematics % Proficient Learner',
|
||||
Math_L3 = 'Mathematics % Distinguished Learner',
|
||||
Science_Count = 'Science Number Tested',
|
||||
Science_Mean = 'Science Mean Scale Score',
|
||||
Science_L0 = 'Science % Beginning Learner',
|
||||
Science_L1 = 'Science % Developing Learner',
|
||||
Science_L2 = 'Science % Proficient Learner',
|
||||
Science_L3 = 'Science % Distinguished Learner',
|
||||
Social_Count = 'Social Studies Number Tested',
|
||||
Social_Mean = 'Social Studies Mean Scale Score',
|
||||
Social_L0 = 'Social Studies % Beginning Learner',
|
||||
Social_L1 = 'Social Studies % Developing Learner',
|
||||
Social_L2 = 'Social Studies % Proficient Learner',
|
||||
Social_L3 = 'Social Studies % Distinguished Learner',
|
||||
}
|
||||
35
src/enums/milestone.ts
Normal file
35
src/enums/milestone.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
export enum Milestone {
|
||||
Cohort = 'cohort',
|
||||
Year = 'year',
|
||||
County = 'mcode',
|
||||
School = 'lcode',
|
||||
Grade = 'grade',
|
||||
ELACount = 'ecount',
|
||||
ELAMean = 'emean',
|
||||
ELA0 = 'ela0',
|
||||
ELA1 = 'ela1',
|
||||
ELA2 = 'ela2',
|
||||
ELA3 = 'ela3',
|
||||
ELARank = 'elarank',
|
||||
MathCount = 'mcount',
|
||||
MathMean = 'mmean',
|
||||
Math0 = 'math0',
|
||||
Math1 = 'math1',
|
||||
Math2 = 'math2',
|
||||
Math3 = 'math3',
|
||||
MathRank = 'mathrank',
|
||||
SciCount = 'ccount',
|
||||
SciMean = 'cmean',
|
||||
Sci0 = 'sci0',
|
||||
Sci1 = 'sci1',
|
||||
Sci2 = 'sci2',
|
||||
Sci3 = 'sci3',
|
||||
SciRank = 'scirank',
|
||||
SocCount = 'ocount',
|
||||
SocMean = 'omean',
|
||||
Soc0 = 'soc0',
|
||||
Soc1 = 'soc1',
|
||||
Soc2 = 'soc2',
|
||||
Soc3 = 'soc3',
|
||||
SocRank = 'socrank',
|
||||
}
|
||||
9
src/models/key-value.ts
Normal file
9
src/models/key-value.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export class KeyValue {
|
||||
key: string;
|
||||
value: string;
|
||||
|
||||
constructor(data: Partial<KeyValue>) {
|
||||
this.key = data?.key ?? '';
|
||||
this.value = data?.value ?? '';
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import { Score } from './score';
|
||||
|
||||
export class Milestone {
|
||||
Year: number;
|
||||
Grade: number;
|
||||
County: string;
|
||||
School: string;
|
||||
ELA: Score;
|
||||
Math: Score;
|
||||
Science: Score;
|
||||
Social: Score;
|
||||
|
||||
constructor(data: Partial<Milestone>) {
|
||||
this.Year = data?.Year ?? 2000;
|
||||
this.Grade = data?.Grade ?? 0;
|
||||
this.County = data?.County ?? '';
|
||||
this.School = data?.School ?? '';
|
||||
this.ELA = new Score(data?.ELA ?? {});
|
||||
this.Math = new Score(data?.Math ?? {});
|
||||
this.Science = new Score(data?.Science ?? {});
|
||||
this.Social = new Score(data?.Social ?? {});
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
export class Score {
|
||||
Count: number;
|
||||
Mean: number;
|
||||
L0: number;
|
||||
L1: number;
|
||||
L2: number;
|
||||
L3: number;
|
||||
|
||||
constructor(data: Partial<Score>) {
|
||||
this.Count = data?.Count ?? 0;
|
||||
this.Mean = data?.Mean ?? 0;
|
||||
this.L0 = data?.L0 ?? 0;
|
||||
this.L1 = data?.L1 ?? 0;
|
||||
this.L2 = data?.L2 ?? 0;
|
||||
this.L3 = data?.L3 ?? 0;
|
||||
}
|
||||
}
|
||||
@@ -1,118 +1,210 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import Papa, { ParseRemoteConfig, ParseResult } from 'papaparse';
|
||||
import { Milestone } from '../models/milestone';
|
||||
import { Column } from '../enums/column';
|
||||
import { Subject } from 'rxjs';
|
||||
import { ReplaySubject } from 'rxjs';
|
||||
import { KeyValue } from '../models/key-value';
|
||||
import { Csv } from '../enums/csv';
|
||||
import { Milestone } from '../enums/milestone';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class DataService {
|
||||
private _data: Milestone[] = [];
|
||||
private _data: Record<string, any>[] = [];
|
||||
private data = new ReplaySubject<Record<string, any>[]>();
|
||||
readonly Data = this.data.asObservable();
|
||||
|
||||
private counties = new Subject<string[]>();
|
||||
private _counties = new Map<string, string>();
|
||||
private counties = new ReplaySubject<KeyValue[]>();
|
||||
readonly Counties = this.counties.asObservable();
|
||||
|
||||
private schools = new Subject<string[]>();
|
||||
private _schools = new Map<string, string>();
|
||||
private schools = new ReplaySubject<KeyValue[]>();
|
||||
readonly Schools = this.schools.asObservable();
|
||||
|
||||
private years = new Subject<number[]>();
|
||||
private _years = Array(2023 - 2015 + 1)
|
||||
.fill(0)
|
||||
.map((_, index) => 2015 + index)
|
||||
.filter((y) => y !== 2020);
|
||||
private years = new ReplaySubject<KeyValue[]>();
|
||||
readonly Years = this.years.asObservable();
|
||||
|
||||
private grades = new Subject<number[]>();
|
||||
private _grades = Array(8 - 3 + 1)
|
||||
.fill(0)
|
||||
.map((_, index) => 3 + index);
|
||||
private grades = new ReplaySubject<KeyValue[]>();
|
||||
readonly Grades = this.grades.asObservable();
|
||||
|
||||
private _cohorts = new Map<string, string>();
|
||||
private cohorts = new ReplaySubject<KeyValue[]>();
|
||||
readonly Cohorts = this.cohorts.asObservable();
|
||||
|
||||
constructor() {
|
||||
this.load();
|
||||
}
|
||||
|
||||
private load(): void {
|
||||
let files: { url: string; year: number; grade: number }[] = [];
|
||||
for (let year = 2015; year < 2024; ++year) {
|
||||
if (year !== 2020) {
|
||||
for (let grade = 3; grade < 9; ++grade) {
|
||||
files.push({
|
||||
url: `assets/data/${year}-${grade}.csv`,
|
||||
year: year,
|
||||
grade: grade,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
let count = this._years.length * this._grades.length;
|
||||
this._years.forEach((year: number) => {
|
||||
this._grades.forEach((grade: number) => {
|
||||
Papa.parse(`assets/data/${year}-${grade}.csv`, {
|
||||
download: true,
|
||||
header: false,
|
||||
complete: (results: ParseResult<Record<string, any>>) => {
|
||||
const headers = this.getHeaders((results?.data ?? []).slice(1, 3));
|
||||
const lookup = (x: string, record: Record<string, any>): any => {
|
||||
const idx = headers.findIndex((h: string) => x === h);
|
||||
return (idx > -1 ? record[idx] : '') as any;
|
||||
};
|
||||
const cohort = year + 12 - grade;
|
||||
this._cohorts.set(`${cohort}`, `Class of ${cohort}`);
|
||||
|
||||
let count = files.length;
|
||||
files.forEach((file: { url: string; year: number; grade: number }) => {
|
||||
Papa.parse(file.url, {
|
||||
download: true,
|
||||
header: false,
|
||||
complete: (results: ParseResult<Record<string, any>>) => {
|
||||
(results?.data ?? []).forEach(
|
||||
(record: Record<string, any>, index: number) => {
|
||||
if (index < 3) return;
|
||||
this._data.push(
|
||||
new Milestone({
|
||||
Year: file.year,
|
||||
Grade: file.grade,
|
||||
County: record[Column.County],
|
||||
School: record[Column.School],
|
||||
ELA: {
|
||||
Count: record[Column.ELA_Count],
|
||||
Mean: record[Column.ELA_Mean],
|
||||
L0: record[Column.ELA_0],
|
||||
L1: record[Column.ELA_1],
|
||||
L2: record[Column.ELA_2],
|
||||
L3: record[Column.ELA_3],
|
||||
},
|
||||
Math: {
|
||||
Count: record[Column.Math_Count],
|
||||
Mean: record[Column.Math_Mean],
|
||||
L0: record[Column.Math_0],
|
||||
L1: record[Column.Math_1],
|
||||
L2: record[Column.Math_2],
|
||||
L3: record[Column.Math_3],
|
||||
},
|
||||
Science: {
|
||||
Count: record[Column.Sci_Count],
|
||||
Mean: record[Column.Sci_Mean],
|
||||
L0: record[Column.Sci_0],
|
||||
L1: record[Column.Sci_1],
|
||||
L2: record[Column.Sci_2],
|
||||
L3: record[Column.Sci_3],
|
||||
},
|
||||
Social: {
|
||||
Count: record[Column.Soc_Count],
|
||||
Mean: record[Column.Soc_Mean],
|
||||
L0: record[Column.Soc_0],
|
||||
L1: record[Column.Soc_1],
|
||||
L2: record[Column.Soc_2],
|
||||
L3: record[Column.Soc_3],
|
||||
},
|
||||
})
|
||||
const data: Record<string, any>[] = [];
|
||||
|
||||
(results?.data ?? []).forEach((record: Record<string, any>) => {
|
||||
let code = parseInt(lookup(Csv.County_Code, record));
|
||||
// skip non-data rows on the csv
|
||||
if (isNaN(code)) return;
|
||||
|
||||
const ms = {} as Record<string, any>;
|
||||
// const ms = new Milestone({});
|
||||
ms[Milestone.Year] = year;
|
||||
ms[Milestone.Grade] = grade;
|
||||
ms[Milestone.Cohort] = cohort;
|
||||
ms[Milestone.County] = lookup(Csv.County_Code, record);
|
||||
ms[Milestone.School] = lookup(Csv.School_Code, record);
|
||||
ms[Milestone.ELACount] = lookup(Csv.ELA_Count, record);
|
||||
ms[Milestone.ELAMean] = lookup(Csv.ELA_Mean, record);
|
||||
ms[Milestone.ELA0] = lookup(Csv.ELA_L0, record);
|
||||
ms[Milestone.ELA1] = lookup(Csv.ELA_L1, record);
|
||||
ms[Milestone.ELA2] = lookup(Csv.ELA_L2, record);
|
||||
ms[Milestone.ELA3] = lookup(Csv.ELA_L3, record);
|
||||
ms[Milestone.MathCount] = lookup(Csv.Math_Count, record);
|
||||
ms[Milestone.MathMean] = lookup(Csv.Math_Mean, record);
|
||||
ms[Milestone.Math0] = lookup(Csv.Math_L0, record);
|
||||
ms[Milestone.Math1] = lookup(Csv.Math_L1, record);
|
||||
ms[Milestone.Math2] = lookup(Csv.Math_L2, record);
|
||||
ms[Milestone.Math3] = lookup(Csv.Math_L3, record);
|
||||
ms[Milestone.SciCount] = lookup(Csv.Science_Count, record);
|
||||
ms[Milestone.SciMean] = lookup(Csv.Science_Mean, record);
|
||||
ms[Milestone.Sci0] = lookup(Csv.Science_L0, record);
|
||||
ms[Milestone.Sci1] = lookup(Csv.Science_L1, record);
|
||||
ms[Milestone.Sci2] = lookup(Csv.Science_L2, record);
|
||||
ms[Milestone.Sci3] = lookup(Csv.Science_L3, record);
|
||||
ms[Milestone.SocCount] = lookup(Csv.Social_Count, record);
|
||||
ms[Milestone.SocMean] = lookup(Csv.Social_Mean, record);
|
||||
ms[Milestone.Soc0] = lookup(Csv.Social_L0, record);
|
||||
ms[Milestone.Soc1] = lookup(Csv.Social_L1, record);
|
||||
ms[Milestone.Soc2] = lookup(Csv.Social_L2, record);
|
||||
ms[Milestone.Soc3] = lookup(Csv.Social_L3, record);
|
||||
data.push(ms);
|
||||
|
||||
this._counties.set(
|
||||
lookup(Csv.County_Code, record),
|
||||
lookup(Csv.County_Label, record)
|
||||
);
|
||||
}
|
||||
);
|
||||
--count;
|
||||
if (count === 0) this.loadComplete();
|
||||
},
|
||||
error: (error: any) => {
|
||||
console.error(error);
|
||||
--count;
|
||||
if (count === 0) this.loadComplete();
|
||||
},
|
||||
} as ParseRemoteConfig);
|
||||
this._schools.set(
|
||||
`${lookup(Csv.County_Code, record)}-${lookup(
|
||||
Csv.School_Code,
|
||||
record
|
||||
)}`,
|
||||
lookup(Csv.School_Label, record)
|
||||
);
|
||||
});
|
||||
|
||||
[
|
||||
{ sort: Milestone.ELAMean, rank: Milestone.ELARank },
|
||||
{ sort: Milestone.MathMean, rank: Milestone.MathRank },
|
||||
{ sort: Milestone.SciMean, rank: Milestone.SciRank },
|
||||
{ sort: Milestone.SocMean, rank: Milestone.SocRank },
|
||||
].forEach((m) => {
|
||||
data
|
||||
.sort(
|
||||
(a: Record<string, any>, b: Record<string, any>) =>
|
||||
+a[m.sort] - b[m.sort]
|
||||
)
|
||||
.forEach(
|
||||
(a: Record<string, any>, index: number) => (a[m.rank] = index)
|
||||
);
|
||||
});
|
||||
|
||||
this._data.push(...data);
|
||||
|
||||
--count;
|
||||
if (count === 0) this.loadComplete();
|
||||
},
|
||||
error: (error: any) => {
|
||||
console.error(error);
|
||||
--count;
|
||||
if (count === 0) this.loadComplete();
|
||||
},
|
||||
} as ParseRemoteConfig);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private getHeaders(data: Record<string, any>[]): string[] {
|
||||
const headers: string[] = [];
|
||||
data.forEach((record: Record<string, any>) => {
|
||||
let prv = '';
|
||||
Object.keys(record).forEach((key: string, index: number) => {
|
||||
let value = (record[key] ?? '')
|
||||
.replace(/\n/g, '')
|
||||
.replace(/- EOG/g, '');
|
||||
if (value.length === 0) value = prv;
|
||||
|
||||
headers[index] = `${headers[index] ?? ''} ${value}`.trim();
|
||||
prv = value;
|
||||
});
|
||||
});
|
||||
return headers;
|
||||
}
|
||||
|
||||
private loadComplete(): void {
|
||||
this.cohorts.next(
|
||||
[...this._cohorts]
|
||||
.map(([key, value]) => new KeyValue({ key: key, value: value }))
|
||||
.sort((a: KeyValue, b: KeyValue) => a.value.localeCompare(b.value))
|
||||
);
|
||||
this.counties.next(
|
||||
[...new Set(this._data.map((data: Milestone) => data.County))].sort()
|
||||
[...this._counties]
|
||||
.map(
|
||||
([code, label]) =>
|
||||
new KeyValue({
|
||||
key: code,
|
||||
value: label
|
||||
.toLowerCase()
|
||||
.replace(/\b[a-z]/g, (c) => c.toUpperCase()),
|
||||
})
|
||||
)
|
||||
.sort((a: KeyValue, b: KeyValue) => a.value.localeCompare(b.value))
|
||||
);
|
||||
this.schools.next(
|
||||
[...new Set(this._data.map((data: Milestone) => data.School))].sort()
|
||||
[...this._schools]
|
||||
.map(
|
||||
([code, label]) =>
|
||||
new KeyValue({
|
||||
key: code,
|
||||
value: label
|
||||
.toLowerCase()
|
||||
.replace(/\b[a-z]/g, (c) => c.toUpperCase()),
|
||||
})
|
||||
)
|
||||
.sort((a: KeyValue, b: KeyValue) => a.value.localeCompare(b.value))
|
||||
);
|
||||
this.years.next(
|
||||
[...new Set(this._data.map((data: Milestone) => data.Year))].sort()
|
||||
this._years
|
||||
.map(
|
||||
(year: number) =>
|
||||
new KeyValue({ key: year.toString(), value: year.toString() })
|
||||
)
|
||||
.sort((a: KeyValue, b: KeyValue) => a.value.localeCompare(b.value))
|
||||
);
|
||||
this.grades.next(
|
||||
[...new Set(this._data.map((data: Milestone) => data.Grade))].sort()
|
||||
this._grades
|
||||
.map(
|
||||
(grade: number) =>
|
||||
new KeyValue({ key: grade.toString(), value: grade.toString() })
|
||||
)
|
||||
.sort((a: KeyValue, b: KeyValue) => a.value.localeCompare(b.value))
|
||||
);
|
||||
this.data.next(this._data);
|
||||
}
|
||||
}
|
||||
|
||||
25
src/services/filters.service.ts
Normal file
25
src/services/filters.service.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Subject } from 'rxjs';
|
||||
import { KeyValue } from '../models/key-value';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class FilterService {
|
||||
private _values: KeyValue[] = [];
|
||||
private set filters(value: KeyValue[]) {
|
||||
this._values = value;
|
||||
this.filterSubject.next(this._values);
|
||||
}
|
||||
private get filters(): KeyValue[] {
|
||||
return this._values;
|
||||
}
|
||||
private filterSubject = new Subject<KeyValue[]>();
|
||||
readonly filters$ = this.filterSubject.asObservable();
|
||||
|
||||
constructor() {}
|
||||
|
||||
set(key: string, value: any): void {
|
||||
const update = this.filters.filter((f: KeyValue) => f.key !== key);
|
||||
if (!!value) update.push(new KeyValue({ key: key, value: value }));
|
||||
this.filters = update;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user