File

src/app/modules/dashboard/components/dataset/dataset.component.ts

Implements

OnInit OnDestroy

Metadata

Index

Properties
Methods
Inputs
Accessors

Constructor

constructor(datasetService: DatasetService, formBuilder: UntypedFormBuilder, reportService: ReportService, activatedRoute: ActivatedRoute, resourceService: ResourceService)
Parameters :
Name Type Optional
datasetService DatasetService No
formBuilder UntypedFormBuilder No
reportService ReportService No
activatedRoute ActivatedRoute No
resourceService ResourceService No

Inputs

dataset
Type : IDataset
markdownUpdated$
Type : Subject<literal type>

Methods

Private getDataset
getDataset(undefined)
Parameters :
Name Optional
No
Returns : any
Private getDateChunks
getDateChunks(dates: string[], chunkSize: number)
Parameters :
Name Type Optional Default value
dates string[] No
chunkSize number No 7
Returns : any
Private getDateRange
getDateRange(startDate, endDate, formatted)
Parameters :
Name Optional Default value
startDate No
endDate No
formatted No true
Returns : {}
getPickerMaxDate
getPickerMaxDate()
Returns : any
getPickerMinDate
getPickerMinDate()
Returns : any
Private handleDownload
handleDownload(event)
Parameters :
Name Optional
event No
Returns : void
handleMarkdownSubmission
handleMarkdownSubmission(event)
Parameters :
Name Optional
event No
Returns : void
ngOnDestroy
ngOnDestroy()
Returns : void
ngOnInit
ngOnInit()
Returns : void
onMarkdownChange
onMarkdownChange(updatedData: string, type: "dataDictionary" | "examples")
Parameters :
Name Type Optional
updatedData string No
type "dataDictionary" | "examples" No
Returns : void
onSubmit
onSubmit()
Returns : void
Private prepareForm
prepareForm()
Returns : void
Private prepareTable
prepareTable(el)
Parameters :
Name Optional
el No
Returns : void
Private reRenderTable
reRenderTable(data)
Parameters :
Name Optional
data No
Returns : void
Private setMarkdowns
setMarkdowns()
Returns : void
subscribeToTimePicker
subscribeToTimePicker()
Returns : void

Properties

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

Accessors

pickerMaxDate
getpickerMaxDate()
pickerMinDate
getpickerMinDate()
initTable
setinitTable(element: ElementRef | null)
Parameters :
Name Type Optional
element ElementRef | null No
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;
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""