src/app/modules/dashboard/components/dataset/dataset.component.ts
OnInit
OnDestroy
selector | app-dataset |
styleUrls | ./dataset.component.scss |
templateUrl | ./dataset.component.html |
Properties |
|
Methods |
|
Inputs |
Accessors |
constructor(datasetService: DatasetService, formBuilder: UntypedFormBuilder, reportService: ReportService, activatedRoute: ActivatedRoute, resourceService: ResourceService)
|
||||||||||||||||||
Parameters :
|
dataset | |
Type : IDataset
|
|
markdownUpdated$ | |
Type : Subject<literal type>
|
|
Private getDataset | |||
getDataset(undefined)
|
|||
Parameters :
Returns :
any
|
Private getDateChunks | ||||||||||||
getDateChunks(dates: string[], chunkSize: number)
|
||||||||||||
Parameters :
Returns :
any
|
Private getDateRange | ||||||||||||
getDateRange(startDate, endDate, formatted)
|
||||||||||||
Parameters :
Returns :
{}
|
getPickerMaxDate |
getPickerMaxDate()
|
Returns :
any
|
getPickerMinDate |
getPickerMinDate()
|
Returns :
any
|
Private handleDownload | ||||
handleDownload(event)
|
||||
Parameters :
Returns :
void
|
handleMarkdownSubmission | ||||
handleMarkdownSubmission(event)
|
||||
Parameters :
Returns :
void
|
ngOnDestroy |
ngOnDestroy()
|
Returns :
void
|
ngOnInit |
ngOnInit()
|
Returns :
void
|
onMarkdownChange | |||||||||
onMarkdownChange(updatedData: string, type: "dataDictionary" | "examples")
|
|||||||||
Parameters :
Returns :
void
|
onSubmit |
onSubmit()
|
Returns :
void
|
Private prepareForm |
prepareForm()
|
Returns :
void
|
Private prepareTable | ||||
prepareTable(el)
|
||||
Parameters :
Returns :
void
|
Private reRenderTable | ||||
reRenderTable(data)
|
||||
Parameters :
Returns :
void
|
Private setMarkdowns |
setMarkdowns()
|
Returns :
void
|
subscribeToTimePicker |
subscribeToTimePicker()
|
Returns :
void
|
Private customTimePicker |
Default value : new BehaviorSubject({ from: dayjs().subtract(7, 'day').toDate(), to: dayjs().subtract(1, 'day').toDate() })
|
data |
Type : any
|
Public dataDictionary |
Type : string
|
Public examples |
Type : string
|
Private getDefaultTableOptions |
Default value : () => {...}
|
options |
Type : any
|
Default value : { maxLines: 1000, printMargin: false }
|
Public reportService |
Type : ReportService
|
Public resourceService |
Type : ResourceService
|
Public showLoader |
Default value : false
|
subscription |
Type : Subscription
|
table |
Type : any
|
Public timeRangePicker |
Type : UntypedFormGroup
|
pickerMaxDate |
getpickerMaxDate()
|
pickerMinDate |
getpickerMinDate()
|
initTable | ||||||
setinitTable(element: ElementRef | null)
|
||||||
Parameters :
Returns :
void
|
import { ResourceService } from '@sunbird/shared';
import { ActivatedRoute } from '@angular/router';
import { Component, OnInit, Input, ViewChild, ElementRef, OnDestroy } from '@angular/core';
import { IDataset } from '../../interfaces';
import { DatasetService, ReportService } from '../../services';
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { Subject, of, zip, Subscription, BehaviorSubject } from 'rxjs';
import { map, catchError, switchMap, distinctUntilChanged, tap, filter } from 'rxjs/operators';
import dayjs from 'dayjs';
import { get, chunk, map as _map, first, last, partition, forEach, flatMap } from 'lodash-es';
import $ from 'jquery';
import 'datatables.net';
@Component({
selector: 'app-dataset',
templateUrl: './dataset.component.html',
styleUrls: ['./dataset.component.scss']
})
export class DatasetComponent implements OnInit, OnDestroy {
@Input() dataset: IDataset;
@Input() markdownUpdated$: Subject<{ data: string, type: string }>;
public timeRangePicker: UntypedFormGroup;
public dataDictionary: string;
public examples: string;
public showLoader = false;
public get pickerMaxDate() {
return dayjs().subtract(1, 'day').toDate();
}
public get pickerMinDate() {
if (get(this.dataset, 'dataAvailableFrom')) {
return dayjs(this.dataset.dataAvailableFrom).toDate();
}
return dayjs().subtract(6, 'month').toDate();
}
subscription: Subscription;
table: any;
data: any;
options: any = { maxLines: 1000, printMargin: false };
private customTimePicker = new BehaviorSubject({ from: dayjs().subtract(7, 'day').toDate(), to: dayjs().subtract(1, 'day').toDate() });
@ViewChild('datasets') set initTable(element: ElementRef | null) {
if (!element) { return; }
this.prepareTable(element.nativeElement);
}
constructor(private datasetService: DatasetService, private formBuilder: UntypedFormBuilder,
public reportService: ReportService, private activatedRoute: ActivatedRoute,
public resourceService: ResourceService) { }
onMarkdownChange(updatedData: string, type: 'dataDictionary' | 'examples') {
this[type] = updatedData;
}
private prepareForm() {
this.timeRangePicker = this.formBuilder.group({
from: [dayjs().subtract(7, 'day').toDate()],
to: [dayjs().subtract(1, 'day').toDate()]
});
this.subscribeToTimePicker();
}
ngOnInit() {
this.prepareForm();
this.setMarkdowns();
}
private setMarkdowns() {
const { dataDictionary = '', examples = '' } = this.dataset || {};
try {
this.dataDictionary = atob(dataDictionary);
this.examples = atob(examples);
} catch (error) {
console.error(error);
this.dataDictionary = this.examples = '';
}
}
subscribeToTimePicker() {
this.subscription = this.customTimePicker.pipe(
tap(res => this.showLoader = true),
distinctUntilChanged((p: any, n: any) => JSON.stringify(p) === JSON.stringify(n)),
filter(res => res.from && res.to && res.from <= res.to),
switchMap((res: any) => {
const dateRange = this.getDateRange(res.from, res.to, false);
const chunks = this.getDateChunks(dateRange);
return zip(..._map(chunks, chunkObj => {
const [from, to] = chunkObj;
return this.getDataset({ from: dayjs(from), to: dayjs(to) });
}));
})
).subscribe(data => {
this.showLoader = false;
this.reRenderTable(data);
}, err => {
this.showLoader = false;
});
}
private reRenderTable(data) {
this.data = flatMap(data);
if (this.table) {
this.table.clear();
this.table.rows.add(this.data);
this.table.draw();
}
}
onSubmit() {
this.customTimePicker.next(this.timeRangePicker.value);
}
private getDataset({ from = dayjs().subtract(7, 'day'), to = dayjs().subtract(1, 'day') }) {
const dateRange = this.getDateRange(from, to);
const { hash } = this.activatedRoute.snapshot.params;
return this.datasetService.getDataSet({
datasetId: this.dataset.datasetId || 'raw',
from: from.format('YYYY-MM-DD'), to: to.format('YYYY-MM-DD'),
...(hash && {
header: {
['X-Channel-Id']: atob(hash)
}
})
})
.pipe(
map((response: { files: string[], periodWiseFiles: { [key: string]: string[] } }) => {
const { periodWiseFiles } = response;
return _map(dateRange, date => {
const files = get(periodWiseFiles, date);
const [json = [], csv = []] = partition(files, val => val.includes('.json'));
return { date, json, csv };
});
}),
catchError(err => {
return of([]);
})
);
}
private getDateRange(startDate, endDate, formatted = true) {
const dates = [];
while (startDate <= endDate) {
const date = dayjs(startDate);
if (formatted) {
dates.push(date.format('YYYY-MM-DD'));
} else {
dates.push(date.toDate());
}
startDate = date.add(1, 'day');
}
return dates;
}
private getDateChunks(dates: string[], chunkSize: number = 7) {
const chunks = chunk(dates, chunkSize);
return _map(chunks, chunkObj => [first(chunkObj), last(chunkObj)]);
}
getPickerMinDate() {
const startDate = get(this.timeRangePicker.get('from'), 'value');
if (!startDate) { return this.pickerMinDate; }
return startDate;
}
getPickerMaxDate() {
const endDate = get(this.timeRangePicker.get('to'), 'value');
if (!endDate) { return this.pickerMaxDate; }
return endDate;
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
/**
* @description returns default config options for master and child table
* @private
* @memberof ListAllReportsComponent
*/
private getDefaultTableOptions = () => ({
paging: true,
lengthChange: false,
searching: false,
ordering: true,
info: false,
autoWidth: true
})
private handleDownload(event) {
const fileType = event.target.getAttribute('filetype');
const tr = $(event.currentTarget).closest('tr');
const row = this.table.row(tr);
const data = row.data();
forEach(data[fileType], file => {
window.open(file, '_blank');
});
}
private prepareTable(el) {
this.table = $(el).DataTable({
...this.getDefaultTableOptions(),
data: this.data || [],
columns: [
{
title: 'Date',
data: 'date',
class: 'dt-center'
}, {
title: 'Download Files',
class: 'dt-center download-datasets',
render: (data, type, row) => {
const downloadFormat = this.dataset.downloadFormats || ['json', 'csv'];
const html = `<div>
${downloadFormat.map(format => {
return `<button filetype="${format}" class="sb-btn sb-btn-primary sb-left-icon-btn sb-btn-normal ${row[format] && !row[format].length ? 'sb-btn-disabled' : ''}">
<i class="download icon"></i>${format.toUpperCase()}</button>`;
})}
</div>`;
return html;
}
}
]
});
$(el).on('click', '.download-datasets', this.handleDownload.bind(this));
$(el).removeClass('no-footer');
}
handleMarkdownSubmission(event) {
this.markdownUpdated$.next({ data: this[event], type: event });
}
}
<div>
<div class="sb-table-container">
<div class="d-flex flex-ai-center">
<div class="d-flex flex-ai-center" [formGroup]="timeRangePicker">
<div class="sb-field-group">
<label>
{{ resourceService?.frmelmnts?.lbl?.tcfrom}}
</label>
<div class="sb-field">
<mat-form-field appearance="fill" class="sb-mat__datepicker">
<mat-label>Select From</mat-label>
<input matInput [matDatepicker]="startPicker" formControlName="from" [min]="pickerMinDate" [max]="getPickerMaxDate()">
<mat-datepicker-toggle matSuffix [for]="startPicker"></mat-datepicker-toggle>
<mat-datepicker #startPicker></mat-datepicker>
</mat-form-field>
</div>
</div>
<div class="sb-field-group ml-16">
<label>
{{ resourceService?.frmelmnts?.lbl?.tcto}}
</label>
<div class="sb-field">
<mat-form-field appearance="fill" class="sb-mat__datepicker">
<mat-label>Select To</mat-label>
<input matInput [matDatepicker]="endPicker" formControlName="to" [min]="getPickerMinDate()" [max]="pickerMaxDate">
<mat-datepicker-toggle matSuffix [for]="endPicker"></mat-datepicker-toggle>
<mat-datepicker #endPicker></mat-datepicker>
</mat-form-field>
</div>
</div>
<div class="sb-field-group ml-16">
<label></label>
<div class="sb-field">
<button (click)="onSubmit()" type="submit" tabindex="0"
class="sb-btn sb-btn-primary sb-btn-normal ripple">{{ resourceService?.frmelmnts?.lbl?.submit}}</button>
</div>
</div>
</div>
</div>
<div class="sb-table-responsive-div">
<div class="ui grid sb-form m-0 sb-workspace-modal">
<div class="twelve wide column pb-16" *ngIf="!showLoader">
<table #datasets class="sb-table sb-table-hover sb-table-striped sb-table-sortable width-100">
</table>
</div>
<div class="twelve wide column pb-16" *ngIf="showLoader">
<app-loader></app-loader>
</div>
</div>
</div>
</div>
<div>
<mat-accordion class="sb-mat__accordion">
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
{{ resourceService?.frmelmnts?.lbl?.dataDictionary}}
</mat-panel-title>
</mat-expansion-panel-header>
<div>
<div>
<div appMarkdown [input]="dataDictionary"
*ngIf="!reportService?.isUserSuperAdmin() && dataDictionary?.length"></div>
<div class="ui warning message" *ngIf="!reportService?.isUserSuperAdmin() && !dataDictionary?.length">
{{ resourceService?.frmelmnts?.lbl?.noDataAvailable}}
</div>
<mat-tab-group class="sb-mat__tab sb-mat__tab--tabinacc" *ngIf="reportService?.isUserSuperAdmin()">
<mat-tab label="{{ resourceService?.frmelmnts?.cert?.lbl?.preview}}">
<div>
<div *ngIf="dataDictionary?.length" appMarkdown [input]="dataDictionary"></div>
<div class="ui warning message" *ngIf="!dataDictionary?.length">
{{ resourceService?.frmelmnts?.lbl?.noDataAvailable}}
</div>
</div>
</mat-tab>
<mat-tab label="{{resourceService?.frmelmnts?.lbl?.edit}}">
<div ace-editor [text]="dataDictionary" [mode]="'markdown'" [theme]="'eclipse'" [options]="options"
[readOnly]="false" [autoUpdateContent]="true" [durationBeforeCallback]="1000"
(textChanged)="onMarkdownChange($event, 'dataDictionary')"
style="min-height: 200px; width:100%; overflow: auto;">
</div>
<div class="sb-table-container m-0">
<div class="sb-table-search-header px-0">
<button (click)="handleMarkdownSubmission('dataDictionary')" type="button" tabindex="0"
class="ml-auto sb-btn sb-btn-primary sb-btn-normal ripple">{{
resourceService?.frmelmnts?.lbl?.submit}}</button>
</div>
</div>
</mat-tab>
</mat-tab-group>
</div>
</div>
</mat-expansion-panel>
<mat-expansion-panel hideToggle>
<mat-expansion-panel-header>
<mat-panel-title>
{{ resourceService?.frmelmnts?.lbl?.datasetExamples}}
</mat-panel-title>
</mat-expansion-panel-header>
<div class="sb-accordion-content">
<div class="sb-accordion-text">
<div appMarkdown [input]="examples" *ngIf="!reportService?.isUserSuperAdmin() && examples?.length"></div>
<div class="ui warning message" *ngIf="!reportService?.isUserSuperAdmin() && !examples?.length">
{{ resourceService?.frmelmnts?.lbl?.noDataAvailable}}
</div>
<mat-tab-group *ngIf="reportService?.isUserSuperAdmin()">
<mat-tab label="{{ resourceService?.frmelmnts?.cert?.lbl?.preview}}">
<div class="ui bottom attached segment" style="min-height: 200px; width:100%; overflow: auto;">
<div *ngIf="examples?.length" appMarkdown [input]="examples"></div>
<div class="ui warning message" *ngIf="!examples?.length">
{{ resourceService?.frmelmnts?.lbl?.noDataAvailable}}
</div>
</div>
</mat-tab>
<mat-tab label="{{resourceService?.frmelmnts?.lbl?.edit}}">
<div ace-editor [text]="examples" [mode]="'markdown'" [theme]="'eclipse'" [options]="options"
[readOnly]="false" [autoUpdateContent]="true" [durationBeforeCallback]="1000"
(textChanged)="onMarkdownChange($event, 'examples')"
style="min-height: 200px; width:100%; overflow: auto;"></div>
<div class="sb-table-container m-0">
<div class="sb-table-search-header px-0">
<button (click)="handleMarkdownSubmission('examples')" type="button" tabindex="0"
class="ml-auto sb-btn sb-btn-primary sb-btn-normal ripple">{{
resourceService?.frmelmnts?.lbl?.submit}}</button>
</div>
</div>
</mat-tab>
</mat-tab-group>
</div>
</div>
</mat-expansion-panel>
</mat-accordion>
</div>
</div>
./dataset.component.scss
.toolbar {
float: left;
}