1098 lines
30 KiB
TypeScript
1098 lines
30 KiB
TypeScript
import { Subscription } from 'rxjs'
|
|
import {
|
|
Component,
|
|
AfterViewInit,
|
|
AfterContentInit,
|
|
OnDestroy,
|
|
ChangeDetectorRef,
|
|
LOCALE_ID,
|
|
Input
|
|
} from '@angular/core'
|
|
import { SasStoreService } from '../services/sas-store.service'
|
|
import { globals } from '../_globals'
|
|
import { EventService } from '../services/event.service'
|
|
|
|
import { registerLocaleData } from '@angular/common'
|
|
import localeEnGB from '@angular/common/locales/en-GB'
|
|
import { HelperService } from '../services/helper.service'
|
|
import { QueryClause } from '../models/TableData'
|
|
import { PublicGetcolvalsSASResponse } from '../models/sas/public-getcolvals.model'
|
|
import { QueryDateTime } from './models/QueryDateTime.model'
|
|
import { isSpecialMissing } from '@sasjs/utils/input/validators'
|
|
import { get } from 'lodash-es'
|
|
import { OnLoadingMoreEvent } from '../shared/autocomplete/autocomplete.component'
|
|
|
|
registerLocaleData(localeEnGB)
|
|
@Component({
|
|
selector: 'app-query',
|
|
templateUrl: './query.component.html',
|
|
styleUrls: ['./query.component.scss'],
|
|
providers: [{ provide: LOCALE_ID, useValue: 'en-GB' }]
|
|
})
|
|
export class QueryComponent
|
|
implements AfterViewInit, AfterContentInit, OnDestroy
|
|
{
|
|
@Input() caching: boolean = true
|
|
@Input() viewboxId: number | undefined
|
|
|
|
private rows_increment: number = 100
|
|
private initial_rows: number = 1000
|
|
|
|
private columnsSub: Subscription | undefined
|
|
private valuesSub: Subscription | undefined
|
|
private _removeQry: Subscription | undefined
|
|
public cols: Array<any> | undefined
|
|
public libds: string | undefined
|
|
public variable: string | undefined
|
|
public value: string | undefined
|
|
public operator: string | undefined
|
|
public vals: Array<any> | undefined
|
|
public innerWidth: any
|
|
public noBorder: any
|
|
public clauseAmt: Array<any> = ['']
|
|
public numOperators: Array<any> = [
|
|
'=',
|
|
'<',
|
|
'>',
|
|
'<=',
|
|
'>=',
|
|
'BETWEEN',
|
|
'IN',
|
|
'NOT IN',
|
|
'NE'
|
|
]
|
|
public charOperators: Array<any> = [
|
|
'=',
|
|
'<',
|
|
'>',
|
|
'<=',
|
|
'>=',
|
|
'CONTAINS',
|
|
'IN',
|
|
'NOT IN',
|
|
'NE'
|
|
// "LIKE",
|
|
// "BEGINS_WITH",
|
|
]
|
|
public logic: string | undefined
|
|
public notIn: boolean = false
|
|
public notArr: boolean = true
|
|
public wasBetween: boolean = false
|
|
public whereClauseLoading: boolean = false
|
|
public dynamicWhereClause: boolean = true
|
|
public usePickers: boolean = false
|
|
|
|
private queryObj: {
|
|
elements: any[]
|
|
clauseLogic?: string
|
|
invalidClause?: boolean
|
|
} = {
|
|
elements: [
|
|
{
|
|
logic: null,
|
|
type: null,
|
|
ddtype: null,
|
|
variable: null,
|
|
operator: null,
|
|
value: null,
|
|
startrow: 0,
|
|
rows: 0,
|
|
nobs: 0,
|
|
values: <
|
|
{
|
|
formatted: any
|
|
unformatted: any
|
|
}[]
|
|
>[],
|
|
operators: []
|
|
}
|
|
],
|
|
clauseLogic: '',
|
|
invalidClause: false
|
|
}
|
|
|
|
public selVar: any = {
|
|
index: null,
|
|
format: null,
|
|
type: null
|
|
}
|
|
|
|
public inObj: Array<any> = [
|
|
{
|
|
checked: false,
|
|
value: null
|
|
}
|
|
]
|
|
|
|
public groupLogic: string = 'AND'
|
|
|
|
public clauses: any = {
|
|
queryObj: [this.queryObj],
|
|
clauseLogic: [],
|
|
groupLogic: this.groupLogic
|
|
}
|
|
|
|
public whereString: string | undefined
|
|
public val: any
|
|
public whereClause: string | undefined
|
|
public logicOperators: Array<string> = ['AND', 'OR']
|
|
|
|
/**
|
|
* Temporary values array used in pickers
|
|
* because they need particular format to work
|
|
* before sending values to backend, values are parsed
|
|
*/
|
|
public queryDateTime: QueryDateTime[] = []
|
|
|
|
public currentClauseIndex: number = -1
|
|
public currentQueryIndex: number = -1
|
|
public pendingINValuesSet: {
|
|
clauseIndex: number
|
|
queryIndex: number
|
|
} | null = null
|
|
|
|
constructor(
|
|
private sasStoreService: SasStoreService,
|
|
private eventService: EventService,
|
|
private helperService: HelperService,
|
|
private cdf: ChangeDetectorRef
|
|
) {
|
|
this.innerWidth = window.screen.width
|
|
if (this.innerWidth > 768) {
|
|
this.noBorder = 'border-left: 2px solid gray;'
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets and sets temporary values selected with DATETIME or TIME picker
|
|
* Those values are used for picker to work with format it lieks
|
|
* Later before sending values to backend, values are parsed
|
|
*/
|
|
getQueryDateTime(clauseIndex: number, queryIndex: number): QueryDateTime {
|
|
let existingQueryDateTime = this.queryDateTime.find(
|
|
(x) => x.clauseIndex === clauseIndex && x.queryIndex === queryIndex
|
|
)
|
|
|
|
if (!existingQueryDateTime) {
|
|
const lastIndex =
|
|
this.queryDateTime.push({
|
|
clauseIndex,
|
|
queryIndex,
|
|
date: '',
|
|
time: ''
|
|
}) - 1
|
|
|
|
existingQueryDateTime = this.queryDateTime[lastIndex]
|
|
}
|
|
|
|
return existingQueryDateTime
|
|
}
|
|
|
|
/**
|
|
* When toggling pickers feature we reset the temp picker values array
|
|
*/
|
|
usePickersChange() {
|
|
this.queryDateTime = []
|
|
}
|
|
|
|
/**
|
|
* Resets all variables used for filtering
|
|
*/
|
|
public resetFilter() {
|
|
this.whereString = undefined
|
|
this.whereClause = undefined
|
|
|
|
this.queryObj = {
|
|
elements: [
|
|
{
|
|
logic: null,
|
|
type: null,
|
|
ddtype: null,
|
|
variable: null,
|
|
operator: null,
|
|
value: null,
|
|
values: [],
|
|
operators: []
|
|
}
|
|
]
|
|
}
|
|
|
|
this.clauses = {
|
|
queryObj: [this.queryObj],
|
|
clauseLogic: [],
|
|
groupLogic: this.groupLogic
|
|
}
|
|
|
|
this.whereClauseFn(true)
|
|
}
|
|
|
|
/**
|
|
* `Globals` are used to store filtering state (variables) as a caching feature
|
|
* until browser reloads
|
|
*/
|
|
public setToGlobals() {
|
|
if (!this.caching) return
|
|
|
|
let objPath = ''
|
|
|
|
if (globals.rootParam === 'home' || globals.rootParam === 'editor') {
|
|
if (this.viewboxId) {
|
|
objPath = `viewboxes.${this.viewboxId}`
|
|
} else {
|
|
objPath = 'editor'
|
|
}
|
|
} else if (globals.rootParam === 'view') {
|
|
objPath = 'viewer'
|
|
}
|
|
|
|
get(globals, objPath).filter.groupLogic = this.groupLogic
|
|
if (typeof this.whereClause === 'string') {
|
|
get(globals, objPath).filter.whereClause = this.whereClause
|
|
}
|
|
if (typeof this.libds === 'string') {
|
|
get(globals, objPath).filter.libds = this.libds
|
|
}
|
|
get(globals, objPath).filter.clauses = this.clauses
|
|
|
|
console.log('globals', globals)
|
|
}
|
|
|
|
/**
|
|
* `Globals` are used to store filtering state (variables) as a caching feature
|
|
* until browser reloads
|
|
*/
|
|
public getFromGlobals() {
|
|
if (!this.caching) return
|
|
|
|
let objPath = ''
|
|
|
|
if (globals.rootParam === 'home' || globals.rootParam === 'editor') {
|
|
if (this.viewboxId) {
|
|
objPath = `viewboxes.${this.viewboxId}`
|
|
} else {
|
|
objPath = 'editor'
|
|
}
|
|
} else if (globals.rootParam === 'view') {
|
|
objPath = 'viewer'
|
|
}
|
|
|
|
if (get(globals, objPath).filter.cols.length > 0) {
|
|
this.cols = JSON.parse(JSON.stringify(get(globals, objPath).filter.cols))
|
|
}
|
|
|
|
if (get(globals, objPath).filter.vals.length > 0) {
|
|
this.vals = JSON.parse(JSON.stringify(get(globals, objPath).filter.vals))
|
|
}
|
|
|
|
if (get(globals, objPath).filter.groupLogic !== '') {
|
|
this.groupLogic = get(globals, objPath).filter.groupLogic
|
|
this.whereClause = get(globals, objPath).filter.whereClause
|
|
this.libds = get(globals, objPath).filter.libds
|
|
this.clauses = get(globals, objPath).filter.clauses
|
|
this.whereClauseFn()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets filtering multiple caluses group logic (and / or)
|
|
*
|
|
* @param groupLogic to set
|
|
*/
|
|
public setGroupLogic(groupLogic: any) {
|
|
this.groupLogic = groupLogic
|
|
this.clauses.groupLogic = groupLogic
|
|
this.whereClauseFn()
|
|
|
|
this.setToGlobals()
|
|
}
|
|
|
|
public dateChange(
|
|
pickerValue: any,
|
|
query: any,
|
|
queryIndex: number,
|
|
clauseIndex: number,
|
|
queryValueIndex?: number
|
|
) {
|
|
let valueInDays = this.helperService.convertJsDateToSasDate(pickerValue)
|
|
|
|
if (query.operator === 'BETWEEN' && queryValueIndex !== undefined) {
|
|
this.clauses.queryObj[clauseIndex].elements[queryIndex].value[
|
|
queryValueIndex
|
|
] = valueInDays
|
|
}
|
|
|
|
this.setVariableValues(valueInDays, queryIndex, clauseIndex)
|
|
}
|
|
|
|
public dateTimeChange(
|
|
query: any,
|
|
queryIndex: number,
|
|
clauseIndex: number,
|
|
queryValueIndex?: number
|
|
) {
|
|
const queryDateTime = this.getQueryDateTime(clauseIndex, queryIndex)
|
|
|
|
if (queryDateTime.date === '') {
|
|
if (query.value === '') return
|
|
|
|
queryDateTime.date = query.value.split(':')[0]
|
|
}
|
|
|
|
const dateHours = parseInt(queryDateTime.time.split(':')[0])
|
|
const dateMinutes = parseInt(queryDateTime.time.split(':')[1])
|
|
const dateSeconds = parseInt(queryDateTime.time.split(':')[2]) || 0
|
|
|
|
let dateObject = new Date(queryDateTime.date)
|
|
let dateTimeObject = new Date(
|
|
dateObject.getFullYear(),
|
|
dateObject.getMonth(),
|
|
dateObject.getDate(),
|
|
dateHours,
|
|
dateMinutes,
|
|
dateSeconds
|
|
)
|
|
|
|
let valueInSeconds = this.helperService.convertJsDateToSasDate(
|
|
dateTimeObject,
|
|
'seconds'
|
|
)
|
|
|
|
if (query.operator === 'BETWEEN' && queryValueIndex !== undefined) {
|
|
this.clauses.queryObj[clauseIndex].elements[queryIndex].value[
|
|
queryValueIndex
|
|
] = valueInSeconds
|
|
}
|
|
|
|
this.setVariableValues(valueInSeconds, queryIndex, clauseIndex)
|
|
}
|
|
|
|
public timeChange(
|
|
time: any,
|
|
query: any,
|
|
ind: number,
|
|
clauseIndex: number,
|
|
queryValueIndex?: number
|
|
) {
|
|
time = time.target.value
|
|
|
|
let hours = parseInt(time.split(':')[0])
|
|
let minutes = parseInt(time.split(':')[1])
|
|
let seconds = parseInt(time.split(':')[2]) || 0
|
|
|
|
let hourSeconds = hours * 60 * 60
|
|
let minuteSeconds = minutes * 60
|
|
|
|
let inputValue = hourSeconds + minuteSeconds + seconds
|
|
|
|
if (query.operator === 'BETWEEN' && queryValueIndex !== undefined) {
|
|
this.clauses.queryObj[clauseIndex].elements[ind].value[queryValueIndex] =
|
|
inputValue
|
|
}
|
|
|
|
this.setVariableValues(inputValue, ind, clauseIndex)
|
|
}
|
|
|
|
/**
|
|
* Takes the clause object, removes current clause and makes new array (table)
|
|
* ready to be sent to SAS
|
|
*/
|
|
public dynamicWhereClauseCreator(groupIndex: number, clauseIndex: number) {
|
|
let dynamicWhereClauseTable: QueryClause[] = []
|
|
|
|
if (
|
|
this.clauses.queryObj.length > 1 ||
|
|
this.clauses.queryObj[groupIndex].elements.length > 1
|
|
) {
|
|
for (
|
|
let groupInd = 0;
|
|
groupInd < this.clauses.queryObj.length;
|
|
groupInd++
|
|
) {
|
|
for (
|
|
let clauseInd = 0;
|
|
clauseInd < this.clauses.queryObj[groupInd].elements.length;
|
|
clauseInd++
|
|
) {
|
|
if (groupInd === groupIndex && clauseInd === clauseIndex) continue
|
|
if (
|
|
[null, undefined].includes(
|
|
this.clauses.queryObj[groupInd].elements[clauseInd].variable
|
|
) ||
|
|
[null, undefined].includes(
|
|
this.clauses.queryObj[groupInd].elements[clauseInd].operator
|
|
) ||
|
|
[null, undefined].includes(
|
|
this.clauses.queryObj[groupInd].elements[clauseInd].value
|
|
)
|
|
)
|
|
continue
|
|
|
|
let rawValue = ''
|
|
const operator =
|
|
this.clauses.queryObj[groupInd].elements[clauseInd].operator
|
|
const variable =
|
|
this.clauses.queryObj[groupInd].elements[clauseInd].variable
|
|
|
|
if (operator === 'BETWEEN') {
|
|
rawValue = `${this.clauses.queryObj[groupInd].elements[clauseInd].value[0]} AND ${this.clauses.queryObj[groupInd][clauseInd].value[1]}`
|
|
} else if (operator === 'IN' || operator === 'NOT IN') {
|
|
this.clauses.queryObj[groupInd][clauseInd].value.forEach(
|
|
(element: any) => {
|
|
if (element.checked) {
|
|
let value = element.val
|
|
|
|
if (typeof value === 'string' && !isSpecialMissing(value)) {
|
|
if (rawValue.length > 0) {
|
|
rawValue += `,'${value}'`
|
|
} else {
|
|
rawValue = `('${value}'`
|
|
}
|
|
} else {
|
|
if (rawValue.length > 0) {
|
|
rawValue += `,${value}`
|
|
} else {
|
|
rawValue = `(${value}`
|
|
}
|
|
}
|
|
}
|
|
}
|
|
)
|
|
|
|
if (rawValue.length > 0) {
|
|
rawValue += ')'
|
|
}
|
|
} else if (
|
|
this.clauses.queryObj[groupInd].elements[clauseInd].value === ''
|
|
) {
|
|
if (
|
|
this.clauses.queryObj[groupInd].elements[clauseInd].type ===
|
|
'char'
|
|
) {
|
|
rawValue = `' '`
|
|
} else {
|
|
rawValue = '.'
|
|
}
|
|
} else {
|
|
if (
|
|
this.clauses.queryObj[groupInd].elements[clauseInd].type ===
|
|
'char'
|
|
) {
|
|
rawValue = `'${this.clauses.queryObj[groupInd].elements[clauseInd].value}'`
|
|
}
|
|
}
|
|
|
|
dynamicWhereClauseTable.push({
|
|
GROUP_LOGIC: this.clauses.groupLogic,
|
|
SUBGROUP_LOGIC:
|
|
this.clauses.queryObj[groupInd].clauseLogic || 'AND',
|
|
SUBGROUP_ID: groupInd,
|
|
VARIABLE_NM: variable,
|
|
OPERATOR_NM: operator,
|
|
RAW_VALUE:
|
|
rawValue ||
|
|
this.clauses.queryObj[groupInd].elements[clauseInd].value
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
return dynamicWhereClauseTable
|
|
}
|
|
|
|
public async getValuesLoadMore(
|
|
event: OnLoadingMoreEvent,
|
|
variable: any,
|
|
ind: any,
|
|
clauseIndex: any
|
|
) {
|
|
const libds = this.libds || ''
|
|
|
|
try {
|
|
if (this.cols !== undefined) {
|
|
for (let index = 0; index < this.cols.length; index++) {
|
|
let element = this.cols[index]
|
|
if (element.NAME === variable) {
|
|
this.selVar.index = index
|
|
}
|
|
}
|
|
|
|
const DDTYPE = this.cols[this.selVar.index].DDTYPE
|
|
|
|
const { startrow: currentStartrow, rows: currentRows } =
|
|
this.clauses.queryObj[clauseIndex].elements[ind]
|
|
|
|
const ROWS_INCREMENT: number = this.rows_increment
|
|
const STARTROW = currentStartrow + currentRows
|
|
|
|
let dynamicWhereClauseTable: any[] = []
|
|
|
|
if (this.dynamicWhereClause) {
|
|
dynamicWhereClauseTable = this.dynamicWhereClauseCreator(
|
|
clauseIndex,
|
|
ind
|
|
)
|
|
}
|
|
|
|
const isDateOrTime = ['DATETIME', 'TIME', 'DATE'].includes(DDTYPE)
|
|
|
|
this.sasStoreService
|
|
.getQueryValues(
|
|
variable,
|
|
libds,
|
|
dynamicWhereClauseTable,
|
|
STARTROW,
|
|
ROWS_INCREMENT
|
|
)
|
|
.then((res: PublicGetcolvalsSASResponse) => {
|
|
const values = res.vals
|
|
const { STARTROW, ROWS, NOBS } = res.meta[0]
|
|
|
|
this.clauses.queryObj[clauseIndex].elements[ind].startrow = STARTROW
|
|
this.clauses.queryObj[clauseIndex].elements[ind].rows = ROWS
|
|
this.clauses.queryObj[clauseIndex].elements[ind].nobs = NOBS
|
|
|
|
if (
|
|
!(
|
|
values.length === 1 &&
|
|
values[0].FORMATTED === '' &&
|
|
values[0].UNFORMATTED === ''
|
|
)
|
|
) {
|
|
this.clauses.queryObj[clauseIndex].elements[ind].values.push(
|
|
...values.map((item: any) => {
|
|
const numeric = res.$vals.vars.UNFORMATTED.type === 'num'
|
|
const specialMissing =
|
|
numeric && isSpecialMissing(item.UNFORMATTED)
|
|
|
|
let unformatted = specialMissing
|
|
? '.' + item.UNFORMATTED
|
|
: item.UNFORMATTED
|
|
|
|
// If it's not special missing and is numeric and null then it is normal missing `.`
|
|
if (numeric && unformatted === null) unformatted = '.'
|
|
|
|
return {
|
|
formatted: isDateOrTime
|
|
? item.FORMATTED.split('.')[0]
|
|
: item.FORMATTED, //WORKAROUND SHOULD BE FIXED PROPERLY (#413): We will remove the milliseconds inside the formatted value, if decimal exists and it is date, time or datetime ddtype
|
|
unformatted: unformatted
|
|
}
|
|
})
|
|
)
|
|
|
|
const currentValues =
|
|
this.clauses.queryObj[clauseIndex].elements[ind].values.length
|
|
const moreExists = NOBS - currentValues > 0
|
|
|
|
event.loadMoreFinished(moreExists)
|
|
} else {
|
|
event.loadMoreFinished(false)
|
|
}
|
|
|
|
this.whereClauseFn()
|
|
this.setToGlobals()
|
|
this.whereClauseLoading = false
|
|
|
|
/**
|
|
* If getValues request did not finish when operator was changed to IN, we need to set values after the request is finished.
|
|
* Othervise values are not beign populated
|
|
*/
|
|
if (this.pendingINValuesSet !== null) {
|
|
const { clauseIndex, queryIndex } = this.pendingINValuesSet
|
|
|
|
this.setINValues(clauseIndex, queryIndex)
|
|
|
|
this.pendingINValuesSet = null
|
|
}
|
|
|
|
this.cdf.detectChanges()
|
|
})
|
|
.catch((err: any) => {
|
|
this.whereClauseLoading = false
|
|
})
|
|
}
|
|
} catch (error) {
|
|
this.eventService.catchResponseError('public/getcolvals', error)
|
|
}
|
|
}
|
|
|
|
public async getValues(variable: any, ind: any, clauseIndex: any) {
|
|
this.whereClauseLoading = true
|
|
|
|
let libds: string = ''
|
|
|
|
if (this.libds !== undefined) {
|
|
libds = this.libds
|
|
}
|
|
|
|
this.clearValues(ind, clauseIndex)
|
|
this.clauses.queryObj[clauseIndex].elements[ind].values = []
|
|
|
|
this.clauses.queryObj[clauseIndex].elements[ind].value = ''
|
|
this.clauses.queryObj[clauseIndex].elements[ind].operator = '='
|
|
this.clauses.queryObj[clauseIndex].elements[ind].variable = variable
|
|
|
|
try {
|
|
if (this.cols !== undefined) {
|
|
for (let index = 0; index < this.cols.length; index++) {
|
|
let element = this.cols[index]
|
|
if (element.NAME === variable) {
|
|
this.selVar.index = index
|
|
}
|
|
}
|
|
|
|
const DDTYPE = this.cols[this.selVar.index].DDTYPE
|
|
|
|
this.clauses.queryObj[clauseIndex].elements[ind].ddtype = DDTYPE
|
|
|
|
this.clauses.queryObj[clauseIndex].elements[ind].type =
|
|
this.cols[this.selVar.index].TYPE
|
|
|
|
if (this.clauses.queryObj[clauseIndex].elements[ind].type === 'num') {
|
|
this.clauses.queryObj[clauseIndex].elements[ind].operators =
|
|
this.numOperators
|
|
} else {
|
|
this.clauses.queryObj[clauseIndex].elements[ind].operators =
|
|
this.charOperators
|
|
}
|
|
|
|
let dynamicWhereClauseTable: any[] = []
|
|
|
|
if (this.dynamicWhereClause) {
|
|
dynamicWhereClauseTable = this.dynamicWhereClauseCreator(
|
|
clauseIndex,
|
|
ind
|
|
)
|
|
}
|
|
|
|
const isDateOrTime = ['DATETIME', 'TIME', 'DATE'].includes(DDTYPE)
|
|
|
|
this.sasStoreService
|
|
.getQueryValues(
|
|
variable,
|
|
libds,
|
|
dynamicWhereClauseTable,
|
|
1,
|
|
this.initial_rows
|
|
)
|
|
.then((res: PublicGetcolvalsSASResponse) => {
|
|
const values = res.vals
|
|
const { STARTROW, ROWS, NOBS } = res.meta[0]
|
|
|
|
this.clauses.queryObj[clauseIndex].elements[ind].startrow = STARTROW
|
|
this.clauses.queryObj[clauseIndex].elements[ind].rows = ROWS
|
|
this.clauses.queryObj[clauseIndex].elements[ind].nobs = NOBS
|
|
|
|
this.clauses.queryObj[clauseIndex].elements[ind].values = values
|
|
.map((item: any) => {
|
|
const numeric = res.$vals.vars.UNFORMATTED.type === 'num'
|
|
const specialMissing =
|
|
numeric && isSpecialMissing(item.UNFORMATTED)
|
|
|
|
let unformatted = specialMissing
|
|
? '.' + item.UNFORMATTED
|
|
: item.UNFORMATTED
|
|
|
|
// If it's not special missing and is numeric and null then it is normal missing `.`
|
|
if (numeric && unformatted === null) unformatted = '.'
|
|
|
|
return {
|
|
formatted: isDateOrTime
|
|
? item.FORMATTED.split('.')[0]
|
|
: item.FORMATTED, //WORKAROUND SHOULD BE FIXED PROPERLY (#413): We will remove the milliseconds inside the formatted value, if decimal exists and it is date, time or datetime ddtype
|
|
unformatted: unformatted
|
|
}
|
|
})
|
|
.slice(0, 2000)
|
|
|
|
this.whereClauseFn()
|
|
this.setToGlobals()
|
|
this.whereClauseLoading = false
|
|
|
|
/**
|
|
* If getValues request did not finish when operator was changed to IN, we need to set values after the request is finished.
|
|
* Othervise values are not beign populated
|
|
*/
|
|
if (this.pendingINValuesSet !== null) {
|
|
const { clauseIndex, queryIndex } = this.pendingINValuesSet
|
|
|
|
this.setINValues(clauseIndex, queryIndex)
|
|
|
|
this.pendingINValuesSet = null
|
|
}
|
|
|
|
this.cdf.detectChanges()
|
|
})
|
|
.catch((err: any) => {
|
|
this.whereClauseLoading = false
|
|
})
|
|
}
|
|
} catch (error) {
|
|
this.eventService.catchResponseError('public/getcolvals', error)
|
|
}
|
|
}
|
|
|
|
public getlogic(logic: any) {
|
|
this.logic = logic
|
|
this.whereClauseFn()
|
|
}
|
|
|
|
public setLogic() {
|
|
this.whereClauseFn()
|
|
this.setToGlobals()
|
|
}
|
|
|
|
public isArr(val: any) {
|
|
return (
|
|
val instanceof Array &&
|
|
val.length > 0 &&
|
|
typeof val[0].checked !== 'undefined'
|
|
)
|
|
}
|
|
|
|
public variableInputChange(
|
|
queryVariable: any,
|
|
index: number,
|
|
clauseIndex: number,
|
|
event: any
|
|
) {
|
|
this.getValues(queryVariable, index, clauseIndex)
|
|
}
|
|
|
|
public setVariableValues(valueObj: any, ind: any, clauseIndex: any) {
|
|
// clauseIndex is current clause in group clauses, ind is current query in clause
|
|
let operator = this.clauses.queryObj[clauseIndex].elements[ind].operator
|
|
|
|
if (operator !== 'BETWEEN' && operator !== 'IN' && operator !== 'NOT IN') {
|
|
this.clauses.queryObj[clauseIndex].elements[ind].value =
|
|
valueObj.toString()
|
|
}
|
|
this.whereClauseFn()
|
|
this.setToGlobals()
|
|
}
|
|
|
|
public setVariableOperator(ind: any, operator: any, clauseIndex: any) {
|
|
let currentValue = this.clauses.queryObj[clauseIndex].elements[ind].value
|
|
if (
|
|
currentValue instanceof Array &&
|
|
operator !== 'IN' &&
|
|
operator !== 'NOT IN'
|
|
) {
|
|
if (
|
|
currentValue instanceof Array &&
|
|
currentValue.length === 2 &&
|
|
typeof currentValue[0] === 'string'
|
|
) {
|
|
currentValue = currentValue[0]
|
|
} else {
|
|
currentValue = ''
|
|
}
|
|
}
|
|
|
|
this.clearValuesOperator(ind, clauseIndex)
|
|
this.clauses.queryObj[clauseIndex].elements[ind].operator = operator
|
|
// tslint:disable-next-line:max-line-length
|
|
if (
|
|
operator === '=' ||
|
|
operator === '>' ||
|
|
operator === '<' ||
|
|
operator === '<=' ||
|
|
operator === '>=' ||
|
|
operator === 'NE'
|
|
) {
|
|
this.clauses.queryObj[clauseIndex].elements[ind].value = currentValue
|
|
}
|
|
|
|
if (operator === 'BETWEEN') {
|
|
this.clauses.queryObj[clauseIndex].elements[ind].value = []
|
|
this.clauses.queryObj[clauseIndex].elements[ind].value.push(currentValue)
|
|
this.clauses.queryObj[clauseIndex].elements[ind].value.push('')
|
|
}
|
|
|
|
if (operator === 'IN' || operator === 'NOT IN') {
|
|
this.setINValues(clauseIndex, ind)
|
|
}
|
|
|
|
this.whereClauseFn()
|
|
this.setToGlobals()
|
|
}
|
|
|
|
public setINValues(clauseIndex: number, queryIndex: number) {
|
|
const vals = []
|
|
|
|
const inValues =
|
|
this.clauses.queryObj[clauseIndex].elements[queryIndex].values
|
|
|
|
if (inValues.length < 1) {
|
|
this.pendingINValuesSet = {
|
|
clauseIndex,
|
|
queryIndex
|
|
}
|
|
}
|
|
|
|
for (let index = 0; index < inValues.length; index++) {
|
|
vals.push({ checked: false, val: inValues[index].formatted })
|
|
}
|
|
|
|
this.clauses.queryObj[clauseIndex].elements[queryIndex].value = vals
|
|
}
|
|
|
|
public clearValues(ind: any, clauseIndex: any) {
|
|
this.clauses.queryObj[clauseIndex].elements[ind].value = ''
|
|
}
|
|
|
|
public clearValuesOperator(ind: any, clauseIndex: any) {
|
|
let operator = this.clauses.queryObj[clauseIndex].elements[ind].operator
|
|
if (
|
|
operator === 'BETWEEN' ||
|
|
operator === 'IN' ||
|
|
operator === 'NOT IN' ||
|
|
operator === 'CONTAINS'
|
|
) {
|
|
this.clearValues(ind, clauseIndex)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks if clause is invalid and sets bool property accordingly
|
|
* It is being used in html to mark invalid clauses red
|
|
*/
|
|
public hasInvalidCluase(clauses: any): boolean {
|
|
for (let clause of clauses) {
|
|
if (
|
|
clause.variable === null ||
|
|
clause.operator === null ||
|
|
clause.value === null ||
|
|
clause.value === ''
|
|
) {
|
|
clause['invalidClause'] = true
|
|
|
|
return true
|
|
} else {
|
|
clause['invalidClause'] = false
|
|
}
|
|
}
|
|
|
|
clauses.invalidClause = false
|
|
|
|
return false
|
|
}
|
|
|
|
/**
|
|
* Checks if group clause is invalid and sets bool property accordingly
|
|
* It is being used in html to mark invalid group clauses red
|
|
*/
|
|
public hasInvalidGroupCluase(): boolean {
|
|
for (let i = 0; i < this.clauses.queryObj.length; i++) {
|
|
if (this.hasInvalidCluase(this.clauses.queryObj[i].elements)) {
|
|
this.clauses.queryObj[i].invalidClause = true
|
|
|
|
return true
|
|
} else {
|
|
this.clauses.queryObj[i].invalidClause = false
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
public addClause(clauseIndex: number) {
|
|
if (this.hasInvalidCluase(this.clauses.queryObj[clauseIndex].elements)) {
|
|
this.eventService.showInfoModal(
|
|
'Error',
|
|
'Cannot add new clause, when one or more clauses are invalid or empty.'
|
|
)
|
|
return
|
|
}
|
|
|
|
this.clauseAmt.push('')
|
|
this.clauses.queryObj[clauseIndex].elements.push({
|
|
logic: null,
|
|
type: null,
|
|
variable: null,
|
|
operator: null,
|
|
value: null,
|
|
values: [],
|
|
operators: []
|
|
})
|
|
|
|
let clauseLogic = this.clauses.queryObj[clauseIndex].clauseLogic
|
|
|
|
if (typeof clauseLogic === 'undefined') {
|
|
this.clauses.queryObj[clauseIndex].clauseLogic = 'AND'
|
|
} else if (clauseLogic === 'OR') {
|
|
this.clauses.queryObj[clauseIndex].clauseLogic = 'OR'
|
|
} else {
|
|
this.clauses.queryObj[clauseIndex].clauseLogic = 'AND'
|
|
}
|
|
|
|
this.whereClauseFn()
|
|
this.setToGlobals()
|
|
}
|
|
|
|
public addGroupClause() {
|
|
if (this.hasInvalidGroupCluase()) {
|
|
this.eventService.showInfoModal(
|
|
'Error',
|
|
'Cannot add new clause, when one or more clauses are invalid or empty.'
|
|
)
|
|
return
|
|
}
|
|
|
|
this.clauses.queryObj.push({
|
|
elements: [
|
|
{
|
|
logic: null,
|
|
type: null,
|
|
variable: null,
|
|
operator: null,
|
|
value: null,
|
|
values: [],
|
|
operators: []
|
|
}
|
|
]
|
|
})
|
|
this.setToGlobals()
|
|
}
|
|
|
|
public isInHtmlCollection(
|
|
value: string,
|
|
collection: HTMLCollectionOf<HTMLOptionElement>
|
|
): boolean {
|
|
for (let i = 0; i < collection.length; i++) {
|
|
if (collection[i].value === value) return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
public removeGroupClause(clauseIndex: number) {
|
|
this.clauses.queryObj.splice(clauseIndex, 1)
|
|
this.whereClauseFn()
|
|
}
|
|
|
|
public removeClause(ind: number, clauseIndex: any) {
|
|
this.clauses.queryObj[clauseIndex].elements.splice(ind, 1)
|
|
this.whereClauseFn()
|
|
this.setToGlobals()
|
|
}
|
|
|
|
public whereClauseFn(reset: boolean = false) {
|
|
const clauses = this.helperService.deepClone(this.clauses)
|
|
|
|
if (reset) clauses.queryObj[0].elements = []
|
|
|
|
if (this.libds !== undefined) {
|
|
let result = this.sasStoreService.whereClauseCreator(
|
|
clauses,
|
|
this.groupLogic,
|
|
this.libds
|
|
)
|
|
|
|
if (!reset) {
|
|
this.whereClause = result.whereClause
|
|
this.whereString = result.string
|
|
}
|
|
}
|
|
}
|
|
|
|
async ngAfterViewInit() {
|
|
this._removeQry = this.sasStoreService.removeQuery.subscribe((remove) => {
|
|
let res = remove
|
|
if (this.clauses.queryObj.length >= 1) {
|
|
this.clauses.queryObj.length = 1
|
|
if (this.clauses.queryObj[0].elements.length >= 1) {
|
|
this.clauses.queryObj[0].elements.length = 1
|
|
this.clauses.queryObj[0].elements = []
|
|
this.clauses.queryObj[0].elements.push({
|
|
logic: null,
|
|
type: null,
|
|
variable: null,
|
|
operator: null,
|
|
value: null,
|
|
values: [],
|
|
operators: []
|
|
})
|
|
this.whereClauseFn()
|
|
}
|
|
}
|
|
})
|
|
|
|
this.columnsSub = this.sasStoreService.columns.subscribe(
|
|
(response: any) => {
|
|
let cols = response.data.cols
|
|
|
|
if (globals.rootParam === 'home' || globals.rootParam === 'editor') {
|
|
this.cols = cols
|
|
let some = cols[0].NAME
|
|
this.libds = response.libds
|
|
|
|
globals.editor.filter.cols = JSON.parse(JSON.stringify(cols))
|
|
}
|
|
|
|
if (globals.rootParam === 'view') {
|
|
if (globals.viewer.filter.cols.length < 1) {
|
|
this.cols = cols
|
|
let some = cols[0].NAME
|
|
this.libds = response.libds
|
|
|
|
globals.viewer.filter.cols = JSON.parse(JSON.stringify(cols))
|
|
}
|
|
}
|
|
|
|
if (!this.libds) {
|
|
this.libds = response.libds
|
|
}
|
|
}
|
|
)
|
|
|
|
this.valuesSub = this.sasStoreService.values.subscribe((res: any) => {
|
|
if (globals.rootParam === 'home' || globals.rootParam === 'editor') {
|
|
if (globals.editor.filter.vals.length < 1) {
|
|
this.vals = res.vals
|
|
|
|
globals.editor.filter.vals = JSON.parse(JSON.stringify(res.vals))
|
|
}
|
|
}
|
|
|
|
if (globals.rootParam === 'view') {
|
|
if (globals.viewer.filter.vals.length < 1) {
|
|
this.vals = res.vals
|
|
|
|
globals.viewer.filter.vals = JSON.parse(JSON.stringify(res.vals))
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
onAutocompleteLoadingMore(
|
|
event: OnLoadingMoreEvent,
|
|
variable: any,
|
|
ind: any,
|
|
clauseIndex: any
|
|
) {
|
|
this.getValuesLoadMore(event, variable, ind, clauseIndex)
|
|
}
|
|
|
|
ngAfterContentInit(): void {
|
|
this.getFromGlobals()
|
|
|
|
setTimeout(() => {
|
|
let varInput: HTMLElement | null =
|
|
document.querySelector('#vals_var_id0_0')
|
|
|
|
if (varInput) varInput.focus()
|
|
}, 500)
|
|
}
|
|
|
|
ngOnDestroy(): void {
|
|
if (this.columnsSub) {
|
|
this.columnsSub.unsubscribe()
|
|
}
|
|
|
|
if (this.valuesSub) {
|
|
this.valuesSub.unsubscribe()
|
|
}
|
|
}
|
|
}
|