Compare commits
20 Commits
Author | SHA1 | Date | |
---|---|---|---|
9bf324c74b | |||
f13e909478 | |||
6a0fe287dd | |||
5a48f2e6e3 | |||
6565834ad4 | |||
837821fd01 | |||
cff5989559 | |||
60510a4d68 | |||
2b54034973 | |||
347b0f9065 | |||
eac0104d7a | |||
1c8e4604de | |||
e9624635ed | |||
f9beda1ddb | |||
53400de110 | |||
cf37ddab22 | |||
802d8a3b08 | |||
1a96bb1233 | |||
8f796aec36 | |||
6eb1aa85d2 |
34
CHANGELOG.md
34
CHANGELOG.md
@ -1,3 +1,37 @@
|
|||||||
|
# [6.6.0](https://git.datacontroller.io/dc/dc/compare/v6.5.2...v6.6.0) (2024-02-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* adjust the col numbers in extracted data ([cff5989](https://git.datacontroller.io/dc/dc/commit/cff598955930d2581349e5c6e8b2dd3f9ac96b4c))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* extra table metadata for [#75](https://git.datacontroller.io/dc/dc/issues/75) ([837821f](https://git.datacontroller.io/dc/dc/commit/837821fd01477d340524dfdaf8dd3d3758cf3095))
|
||||||
|
* show dsnote on hover title ([6565834](https://git.datacontroller.io/dc/dc/commit/6565834ad4089ecf2de39967e6ed6f217ee4a0a5))
|
||||||
|
|
||||||
|
## [6.5.2](https://git.datacontroller.io/dc/dc/compare/v6.5.1...v6.5.2) (2024-02-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* ordering mpe_selectbox data by the data values after selectbox_order ([2b54034](https://git.datacontroller.io/dc/dc/commit/2b5403497317632a4be8a00f21455c036f1e6461))
|
||||||
|
|
||||||
|
## [6.5.1](https://git.datacontroller.io/dc/dc/compare/v6.5.0...v6.5.1) (2024-02-02)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* ensuring submitter email can be pulled from mpe_emails ([eac0104](https://git.datacontroller.io/dc/dc/commit/eac0104d7aebaf98ff1d1c504c1ce3b25d4a0ce8))
|
||||||
|
|
||||||
|
# [6.5.0](https://git.datacontroller.io/dc/dc/compare/v6.4.0...v6.5.0) (2024-01-26)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* filtering by reference to Variables as well as Values ([6eb1aa8](https://git.datacontroller.io/dc/dc/commit/6eb1aa85d29294d63e6af377e622fbed7fd1fab8))
|
||||||
|
|
||||||
# [6.4.0](https://git.datacontroller.io/dc/dc/compare/v6.3.1...v6.4.0) (2024-01-24)
|
# [6.4.0](https://git.datacontroller.io/dc/dc/compare/v6.3.1...v6.4.0) (2024-01-24)
|
||||||
|
|
||||||
|
|
||||||
|
@ -186,7 +186,9 @@
|
|||||||
} as libdsParsed"
|
} as libdsParsed"
|
||||||
class="editor-title text-center mt-0-i"
|
class="editor-title text-center mt-0-i"
|
||||||
>
|
>
|
||||||
|
<clr-tooltip>
|
||||||
<clr-icon
|
<clr-icon
|
||||||
|
clrTooltipTrigger
|
||||||
(click)="datasetInfo = true"
|
(click)="datasetInfo = true"
|
||||||
shape="info-circle"
|
shape="info-circle"
|
||||||
class="is-highlight cursor-pointer"
|
class="is-highlight cursor-pointer"
|
||||||
@ -199,11 +201,22 @@
|
|||||||
class="color-yellow"
|
class="color-yellow"
|
||||||
></clr-icon>
|
></clr-icon>
|
||||||
|
|
||||||
|
<span clrTooltipTrigger>
|
||||||
{{ libdsParsed.libName }}.<a
|
{{ libdsParsed.libName }}.<a
|
||||||
class="mr-10"
|
class="mr-10"
|
||||||
[routerLink]="'/view/data/' + libds!"
|
[routerLink]="'/view/data/' + libds!"
|
||||||
>{{ libdsParsed.tableName.replace('-FC', '') }}</a
|
>{{ libdsParsed.tableName.replace('-FC', '') }}</a
|
||||||
>
|
>
|
||||||
|
</span>
|
||||||
|
<clr-tooltip-content
|
||||||
|
clrPosition="bottom-left"
|
||||||
|
clrSize="lg"
|
||||||
|
*clrIfOpen
|
||||||
|
>
|
||||||
|
{{ this.dsNote }}
|
||||||
|
</clr-tooltip-content>
|
||||||
|
</clr-tooltip>
|
||||||
|
|
||||||
<ng-container *ngIf="dataSource">
|
<ng-container *ngIf="dataSource">
|
||||||
<ng-container *ngIf="!zeroFilterRows">
|
<ng-container *ngIf="!zeroFilterRows">
|
||||||
({{ dataSource.length | thousandSeparator: ',' }}
|
({{ dataSource.length | thousandSeparator: ',' }}
|
||||||
|
@ -121,6 +121,7 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
|||||||
|
|
||||||
datasetInfo: boolean = false
|
datasetInfo: boolean = false
|
||||||
dsmeta: DSMeta[] = []
|
dsmeta: DSMeta[] = []
|
||||||
|
dsNote = ''
|
||||||
|
|
||||||
viewboxes: boolean = false
|
viewboxes: boolean = false
|
||||||
|
|
||||||
@ -2986,6 +2987,20 @@ export class EditorComponent implements OnInit, AfterViewInit {
|
|||||||
this.cols = response.data.cols
|
this.cols = response.data.cols
|
||||||
this.dsmeta = response.data.dsmeta
|
this.dsmeta = response.data.dsmeta
|
||||||
|
|
||||||
|
const notes = this.dsmeta.find((item) => item.NAME === 'NOTES')
|
||||||
|
const longDesc = this.dsmeta.find((item) => item.NAME === 'DD_LONGDESC')
|
||||||
|
const shortDesc = this.dsmeta.find((item) => item.NAME === 'DD_SHORTDESC')
|
||||||
|
|
||||||
|
if (notes && notes.VALUE) {
|
||||||
|
this.dsNote = notes.VALUE
|
||||||
|
} else if (longDesc && longDesc.VALUE) {
|
||||||
|
this.dsNote = longDesc.VALUE
|
||||||
|
} else if (shortDesc && shortDesc.VALUE) {
|
||||||
|
this.dsNote = shortDesc.VALUE
|
||||||
|
} else {
|
||||||
|
this.dsNote = ''
|
||||||
|
}
|
||||||
|
|
||||||
const hot: Handsontable = this.hotInstance
|
const hot: Handsontable = this.hotInstance
|
||||||
|
|
||||||
const approvers: Approver[] = response.data.approvers
|
const approvers: Approver[] = response.data.approvers
|
||||||
|
@ -656,8 +656,7 @@ export class LineageComponent {
|
|||||||
this.flatdata = res.flatdata
|
this.flatdata = res.flatdata
|
||||||
|
|
||||||
if (this.libraryList) {
|
if (this.libraryList) {
|
||||||
let libraryToSelect = this.libraryList.find(
|
let libraryToSelect = this.libraryList.find((library: any) =>
|
||||||
(library: any) =>
|
|
||||||
res.info[0]?.LIBURI?.toUpperCase()?.includes(
|
res.info[0]?.LIBURI?.toUpperCase()?.includes(
|
||||||
library?.LIBRARYID?.toUpperCase()
|
library?.LIBRARYID?.toUpperCase()
|
||||||
)
|
)
|
||||||
|
@ -6,6 +6,7 @@ export interface FilterClause {
|
|||||||
operators: string[]
|
operators: string[]
|
||||||
type: string
|
type: string
|
||||||
value: any
|
value: any
|
||||||
|
valueVariable: boolean
|
||||||
values: { formatted: string; unformatted: any }[]
|
values: { formatted: string; unformatted: any }[]
|
||||||
variable: string
|
variable: string
|
||||||
}
|
}
|
||||||
|
@ -413,7 +413,10 @@
|
|||||||
>
|
>
|
||||||
<app-soft-select
|
<app-soft-select
|
||||||
label="Value"
|
label="Value"
|
||||||
|
[secondLabel]="'Variable'"
|
||||||
|
[emitOnlySelected]="query.valueVariable"
|
||||||
[inputId]="'vals_' + queryIndex + '_' + clauseIndex"
|
[inputId]="'vals_' + queryIndex + '_' + clauseIndex"
|
||||||
|
(selectedLabelChange)="selectedLabelChange($event, query)"
|
||||||
[(value)]="query.value"
|
[(value)]="query.value"
|
||||||
[enableLoadMore]="query.nobs > query.values.length"
|
[enableLoadMore]="query.nobs > query.values.length"
|
||||||
(onInputEvent)="
|
(onInputEvent)="
|
||||||
@ -423,9 +426,19 @@
|
|||||||
onAutocompleteLoadingMore($event, query.variable, queryIndex, clauseIndex)
|
onAutocompleteLoadingMore($event, query.variable, queryIndex, clauseIndex)
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
|
<div *ngIf="!query.valueVariable">
|
||||||
<option [value]="column.unformatted" *ngFor="let column of query.values">
|
<option [value]="column.unformatted" *ngFor="let column of query.values">
|
||||||
{{ column.formatted.trim() }}
|
{{ column.formatted.trim() }}
|
||||||
</option>
|
</option>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="query.valueVariable">
|
||||||
|
<ng-container *ngFor="let column of cols">
|
||||||
|
<option [value]="column.NAME" *ngIf="column.TYPE === query.type">
|
||||||
|
{{ column.NAME }}
|
||||||
|
</option>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
</app-soft-select>
|
</app-soft-select>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
|
@ -95,6 +95,7 @@ export class QueryComponent
|
|||||||
variable: null,
|
variable: null,
|
||||||
operator: null,
|
operator: null,
|
||||||
value: null,
|
value: null,
|
||||||
|
valueVariable: false,
|
||||||
startrow: 0,
|
startrow: 0,
|
||||||
rows: 0,
|
rows: 0,
|
||||||
nobs: 0,
|
nobs: 0,
|
||||||
@ -193,6 +194,20 @@ export class QueryComponent
|
|||||||
*/
|
*/
|
||||||
usePickersChange() {
|
usePickersChange() {
|
||||||
this.queryDateTime = []
|
this.queryDateTime = []
|
||||||
|
if (this.usePickers) {
|
||||||
|
this.clauses.queryObj.forEach((queryObj: any) => {
|
||||||
|
queryObj.elements.forEach((element: any) => {
|
||||||
|
const isDateOrTime = ['DATETIME', 'TIME', 'DATE'].includes(
|
||||||
|
element.ddtype
|
||||||
|
)
|
||||||
|
|
||||||
|
if (isDateOrTime && element.valueVariable) {
|
||||||
|
element.value = ''
|
||||||
|
element.valueVariable = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -253,8 +268,6 @@ export class QueryComponent
|
|||||||
get(globals, objPath).filter.libds = this.libds
|
get(globals, objPath).filter.libds = this.libds
|
||||||
}
|
}
|
||||||
get(globals, objPath).filter.clauses = this.clauses
|
get(globals, objPath).filter.clauses = this.clauses
|
||||||
|
|
||||||
console.log('globals', globals)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -750,6 +763,12 @@ export class QueryComponent
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public selectedLabelChange(label: string, query: any) {
|
||||||
|
query.valueVariable = label === 'Variable'
|
||||||
|
query.value = ''
|
||||||
|
this.whereClauseFn()
|
||||||
|
}
|
||||||
|
|
||||||
public variableInputChange(
|
public variableInputChange(
|
||||||
queryVariable: any,
|
queryVariable: any,
|
||||||
index: number,
|
index: number,
|
||||||
|
@ -416,14 +416,18 @@ export class SasStoreService {
|
|||||||
for (let index = 0; index < clauses.queryObj.length; index++) {
|
for (let index = 0; index < clauses.queryObj.length; index++) {
|
||||||
let string = ''
|
let string = ''
|
||||||
let clause = clauses.queryObj[index]
|
let clause = clauses.queryObj[index]
|
||||||
|
|
||||||
for (let ind = 0; ind < clause.elements.length; ind++) {
|
for (let ind = 0; ind < clause.elements.length; ind++) {
|
||||||
let query = clause.elements[ind]
|
let query = clause.elements[ind]
|
||||||
|
|
||||||
if (ind < clause.elements.length - 1) {
|
if (ind < clause.elements.length - 1) {
|
||||||
opr = clause.clauseLogic
|
opr = clause.clauseLogic
|
||||||
} else {
|
} else {
|
||||||
opr = ''
|
opr = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
let val: any
|
let val: any
|
||||||
|
|
||||||
for (let k = 0; k < query.values.length; k++) {
|
for (let k = 0; k < query.values.length; k++) {
|
||||||
if (
|
if (
|
||||||
typeof query.value === 'string' &&
|
typeof query.value === 'string' &&
|
||||||
@ -494,6 +498,8 @@ export class SasStoreService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let type = query.type
|
let type = query.type
|
||||||
|
//if the value is variable, omit quotes in the 'where' string
|
||||||
|
const isValueVariable = query.valueVariable
|
||||||
let variable = query.variable === null ? '' : query.variable
|
let variable = query.variable === null ? '' : query.variable
|
||||||
let oper = query.operator === null ? '' : query.operator
|
let oper = query.operator === null ? '' : query.operator
|
||||||
// let value = val === null ? "''" : val;
|
// let value = val === null ? "''" : val;
|
||||||
@ -507,10 +513,14 @@ export class SasStoreService {
|
|||||||
if (type === 'char' && oper !== 'IN' && oper !== 'NOT IN') {
|
if (type === 'char' && oper !== 'IN' && oper !== 'NOT IN') {
|
||||||
if (typeof value === 'undefined') {
|
if (typeof value === 'undefined') {
|
||||||
value = ''
|
value = ''
|
||||||
value = " '" + value + "' "
|
|
||||||
} else {
|
|
||||||
value = " '" + value + "' "
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isValueVariable) {
|
||||||
|
value = ' ' + value + ' ' //without quotes, with spaces
|
||||||
|
} else {
|
||||||
|
value = " '" + value + "' " //with quotes and spaces
|
||||||
|
}
|
||||||
|
|
||||||
string = string + ' ' + variable + ' ' + oper + value + opr
|
string = string + ' ' + variable + ' ' + oper + value + opr
|
||||||
} else {
|
} else {
|
||||||
if (type === 'num' && typeof value === 'undefined') {
|
if (type === 'num' && typeof value === 'undefined') {
|
||||||
@ -604,7 +614,7 @@ export class SasStoreService {
|
|||||||
rawValue = '.'
|
rawValue = '.'
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (filterClause.type === 'char') {
|
if (filterClause.type === 'char' && !filterClause.valueVariable) {
|
||||||
rawValue = `'${filterClause.value.replace(/'/g, "''")}'`
|
rawValue = `'${filterClause.value.replace(/'/g, "''")}'`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,22 @@
|
|||||||
<label *ngIf="label" class="clr-control-label">{{ label }}</label>
|
<label
|
||||||
|
*ngIf="label"
|
||||||
|
[class.secondLabelActive]="secondLabel && secondLabel.length > 0"
|
||||||
|
class="clr-control-label"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
(click)="onChangeLabel('first')"
|
||||||
|
[class.value-type-selected]="labelSelected === 'first'"
|
||||||
|
>{{ label }}</span
|
||||||
|
>
|
||||||
|
<ng-container *ngIf="secondLabel">
|
||||||
|
/
|
||||||
|
<span
|
||||||
|
(click)="onChangeLabel('second')"
|
||||||
|
[class.value-type-selected]="labelSelected === 'second'"
|
||||||
|
>{{ secondLabel }}</span
|
||||||
|
>
|
||||||
|
</ng-container>
|
||||||
|
</label>
|
||||||
<ng-container [ngSwitch]="type">
|
<ng-container [ngSwitch]="type">
|
||||||
<ng-container *ngSwitchCase="'date'">
|
<ng-container *ngSwitchCase="'date'">
|
||||||
<clr-date-container>
|
<clr-date-container>
|
||||||
|
@ -29,3 +29,11 @@ clr-date-container {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
label.secondLabelActive span {
|
||||||
|
&:not(.value-type-selected) {
|
||||||
|
text-decoration: line-through;
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
}
|
@ -18,6 +18,7 @@ import { OnLoadingMoreEvent } from '../autocomplete/autocomplete.component'
|
|||||||
export class SoftSelectComponent implements OnInit, OnChanges {
|
export class SoftSelectComponent implements OnInit, OnChanges {
|
||||||
@Input() inputId: string = ''
|
@Input() inputId: string = ''
|
||||||
@Input() label: string | undefined
|
@Input() label: string | undefined
|
||||||
|
@Input() secondLabel: string | undefined
|
||||||
@Input() value: Date | string | null = ''
|
@Input() value: Date | string | null = ''
|
||||||
@Input() disabled: boolean = false
|
@Input() disabled: boolean = false
|
||||||
@Input() type: string = 'text'
|
@Input() type: string = 'text'
|
||||||
@ -30,21 +31,25 @@ export class SoftSelectComponent implements OnInit, OnChanges {
|
|||||||
@Output() focusinInput: EventEmitter<any> = new EventEmitter()
|
@Output() focusinInput: EventEmitter<any> = new EventEmitter()
|
||||||
@Output() onAutocompleteLoadingMore: EventEmitter<OnLoadingMoreEvent> =
|
@Output() onAutocompleteLoadingMore: EventEmitter<OnLoadingMoreEvent> =
|
||||||
new EventEmitter()
|
new EventEmitter()
|
||||||
|
@Output() selectedLabelChange: EventEmitter<string> = new EventEmitter()
|
||||||
|
|
||||||
@ViewChild('input') inputElement: any
|
@ViewChild('input') inputElement: any
|
||||||
|
|
||||||
temp: Date | string | null = ''
|
temp: Date | string | null = ''
|
||||||
inputFocused: boolean = false
|
inputFocused: boolean = false
|
||||||
|
|
||||||
|
labelSelected: LabelTypes = 'first'
|
||||||
|
|
||||||
constructor() {}
|
constructor() {}
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges): void {
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
if (
|
if (
|
||||||
changes.value &&
|
changes.value &&
|
||||||
changes.value.currentValue !== changes.value.previousValue
|
changes.value.currentValue !== changes.value.previousValue
|
||||||
)
|
) {
|
||||||
this.valueChange.emit(changes.value.currentValue)
|
this.valueChange.emit(changes.value.currentValue)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {}
|
ngOnInit(): void {}
|
||||||
|
|
||||||
@ -85,4 +90,14 @@ export class SoftSelectComponent implements OnInit, OnChanges {
|
|||||||
onFocusinInput(event: any) {
|
onFocusinInput(event: any) {
|
||||||
this.focusinInput.emit(event)
|
this.focusinInput.emit(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onChangeLabel(label: LabelTypes) {
|
||||||
|
this.labelSelected = label
|
||||||
|
|
||||||
|
const selectedLabelText = label === 'first' ? this.label : this.secondLabel
|
||||||
|
|
||||||
|
this.selectedLabelChange.emit(selectedLabelText)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type LabelTypes = 'first' | 'second'
|
||||||
|
@ -358,7 +358,12 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<div class="title-col clr-col-auto clr-flex-column clr-flex-sm-row">
|
<div class="title-col clr-col-auto clr-flex-column clr-flex-sm-row">
|
||||||
|
<h3
|
||||||
|
class="viewerTitle clr-flex-column d-flex clr-flex-sm-row clr-align-items-center clr-justify-content-center"
|
||||||
|
>
|
||||||
|
<clr-tooltip class="d-flex">
|
||||||
<clr-icon
|
<clr-icon
|
||||||
|
clrTooltipTrigger
|
||||||
(click)="datasetInfo = true"
|
(click)="datasetInfo = true"
|
||||||
shape="info-circle"
|
shape="info-circle"
|
||||||
class="is-highlight cursor-pointer"
|
class="is-highlight cursor-pointer"
|
||||||
@ -371,12 +376,19 @@
|
|||||||
class="color-yellow mt-5 mr-5"
|
class="color-yellow mt-5 mr-5"
|
||||||
></clr-icon>
|
></clr-icon>
|
||||||
|
|
||||||
<h3
|
<span clrTooltipTrigger *ngIf="tableTitle && tableTitle.length > 0">
|
||||||
*ngIf="tableTitle && tableTitle.length > 0"
|
|
||||||
class="viewerTitle clr-flex-column d-flex clr-flex-sm-row clr-align-items-center"
|
|
||||||
>
|
|
||||||
{{ tableTitle?.replace('-FC', '') }}
|
{{ tableTitle?.replace('-FC', '') }}
|
||||||
|
</span>
|
||||||
|
<clr-tooltip-content
|
||||||
|
clrPosition="bottom-left"
|
||||||
|
clrSize="lg"
|
||||||
|
*clrIfOpen
|
||||||
|
>
|
||||||
|
{{ this.dsNote }}
|
||||||
|
</clr-tooltip-content>
|
||||||
|
</clr-tooltip>
|
||||||
|
|
||||||
|
<ng-container *ngIf="tableTitle && tableTitle.length > 0">
|
||||||
<span *ngIf="numberOfRows !== null">
|
<span *ngIf="numberOfRows !== null">
|
||||||
({{ numberOfRows | thousandSeparator: ',' }}
|
({{ numberOfRows | thousandSeparator: ',' }}
|
||||||
{{ numberOfRows! === 1 ? 'row' : 'rows' }}, {{ filterCols.length
|
{{ numberOfRows! === 1 ? 'row' : 'rows' }}, {{ filterCols.length
|
||||||
@ -388,6 +400,7 @@
|
|||||||
class="refresh-table"
|
class="refresh-table"
|
||||||
shape="refresh"
|
shape="refresh"
|
||||||
></clr-icon>
|
></clr-icon>
|
||||||
|
</ng-container>
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -95,6 +95,7 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
|
|||||||
public $dataFormats: $DataFormats | null = null
|
public $dataFormats: $DataFormats | null = null
|
||||||
public datasetInfo: boolean = false
|
public datasetInfo: boolean = false
|
||||||
public dsmeta: DSMeta[] = []
|
public dsmeta: DSMeta[] = []
|
||||||
|
public dsNote = ''
|
||||||
|
|
||||||
public licenceState = this.licenceService.licenceState
|
public licenceState = this.licenceService.licenceState
|
||||||
public Infinity = Infinity
|
public Infinity = Infinity
|
||||||
@ -246,6 +247,7 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
|
|||||||
this.hotTable.data = res.viewdata
|
this.hotTable.data = res.viewdata
|
||||||
this.$dataFormats = res.$viewdata
|
this.$dataFormats = res.$viewdata
|
||||||
this.dsmeta = res.dsmeta
|
this.dsmeta = res.dsmeta
|
||||||
|
this.setDSNote()
|
||||||
this.numberOfRows = res.sasparams[0].NOBS
|
this.numberOfRows = res.sasparams[0].NOBS
|
||||||
this.queryText = res.sasparams[0].FILTER_TEXT
|
this.queryText = res.sasparams[0].FILTER_TEXT
|
||||||
this.headerPks = res.sasparams[0].PK_FIELDS.split(' ')
|
this.headerPks = res.sasparams[0].PK_FIELDS.split(' ')
|
||||||
@ -803,6 +805,7 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
|
|||||||
this.hotTable.data = res.viewdata
|
this.hotTable.data = res.viewdata
|
||||||
this.$dataFormats = res.$viewdata
|
this.$dataFormats = res.$viewdata
|
||||||
this.dsmeta = res.dsmeta
|
this.dsmeta = res.dsmeta
|
||||||
|
this.setDSNote()
|
||||||
this.queryText = res.sasparams[0].FILTER_TEXT
|
this.queryText = res.sasparams[0].FILTER_TEXT
|
||||||
let columns: any[] = []
|
let columns: any[] = []
|
||||||
let colArr = []
|
let colArr = []
|
||||||
@ -1016,6 +1019,22 @@ export class ViewerComponent implements AfterContentInit, AfterViewInit {
|
|||||||
this.sasStoreService.removeClause()
|
this.sasStoreService.removeClause()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private setDSNote() {
|
||||||
|
const notes = this.dsmeta.find((item) => item.NAME === 'NOTES')
|
||||||
|
const longDesc = this.dsmeta.find((item) => item.NAME === 'DD_LONGDESC')
|
||||||
|
const shortDesc = this.dsmeta.find((item) => item.NAME === 'DD_SHORTDESC')
|
||||||
|
|
||||||
|
if (notes && notes.VALUE) {
|
||||||
|
this.dsNote = notes.VALUE
|
||||||
|
} else if (longDesc && longDesc.VALUE) {
|
||||||
|
this.dsNote = longDesc.VALUE
|
||||||
|
} else if (shortDesc && shortDesc.VALUE) {
|
||||||
|
this.dsNote = shortDesc.VALUE
|
||||||
|
} else {
|
||||||
|
this.dsNote = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private setupHot() {
|
private setupHot() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (!this.loadingTableView && this.libDataset) {
|
if (!this.loadingTableView && this.libDataset) {
|
||||||
|
@ -145,6 +145,13 @@ export class XLMapComponent implements AfterContentInit, AfterViewInit, OnInit {
|
|||||||
public xlmapOnClick(xlmap: XLMapListItem) {
|
public xlmapOnClick(xlmap: XLMapListItem) {
|
||||||
if (xlmap.id !== this.selectedXLMap?.id) {
|
if (xlmap.id !== this.selectedXLMap?.id) {
|
||||||
this.selectedXLMap = xlmap
|
this.selectedXLMap = xlmap
|
||||||
|
this.xlData = []
|
||||||
|
this.filename = ''
|
||||||
|
this.uploader.queue = []
|
||||||
|
if (this.fileUploadInputCompList.first) {
|
||||||
|
this.fileUploadInputCompList.first.nativeElement.value = ''
|
||||||
|
}
|
||||||
|
this.selectedTab = Tabs.Rules
|
||||||
this.viewXLMapRules()
|
this.viewXLMapRules()
|
||||||
this.router.navigateByUrl('/home/files/' + xlmap.id)
|
this.router.navigateByUrl('/home/files/' + xlmap.id)
|
||||||
}
|
}
|
||||||
@ -270,6 +277,7 @@ export class XLMapComponent implements AfterContentInit, AfterViewInit, OnInit {
|
|||||||
this.isLoadingDesc = ''
|
this.isLoadingDesc = ''
|
||||||
this.status = Status.ReadyToUpload
|
this.status = Status.ReadyToUpload
|
||||||
this.xlData = []
|
this.xlData = []
|
||||||
|
this.selectedTab = Tabs.Rules
|
||||||
this.filename = ''
|
this.filename = ''
|
||||||
this.uploader.queue = []
|
this.uploader.queue = []
|
||||||
if (this.fileUploadInputCompList.first) {
|
if (this.fileUploadInputCompList.first) {
|
||||||
@ -377,24 +385,28 @@ export class XLMapComponent implements AfterContentInit, AfterViewInit, OnInit {
|
|||||||
const start = getCellAddress(rule.XLMAP_START, arrayOfObjects)
|
const start = getCellAddress(rule.XLMAP_START, arrayOfObjects)
|
||||||
const finish = getFinishingCell(start, rule.XLMAP_FINISH, arrayOfObjects)
|
const finish = getFinishingCell(start, rule.XLMAP_FINISH, arrayOfObjects)
|
||||||
|
|
||||||
const range = `${start}:${finish}`
|
const a1Range = `${start}:${finish}`
|
||||||
|
|
||||||
|
const range = XLSX.utils.decode_range(a1Range)
|
||||||
|
|
||||||
const rangedData = <any[]>XLSX.utils.sheet_to_json(sheet, {
|
const rangedData = <any[]>XLSX.utils.sheet_to_json(sheet, {
|
||||||
raw: true,
|
raw: true,
|
||||||
range: range,
|
range: a1Range,
|
||||||
header: 'A',
|
header: 'A',
|
||||||
blankrows: true
|
blankrows: true
|
||||||
})
|
})
|
||||||
|
|
||||||
for (let i = 0; i < rangedData.length; i++) {
|
for (let i = 0; i < rangedData.length; i++) {
|
||||||
const row = rangedData[i]
|
const row = rangedData[i]
|
||||||
// Get the keys of the object (excluding '__rowNum__')
|
|
||||||
const keys = Object.keys(row).filter((key) => key !== '__rowNum__')
|
|
||||||
|
|
||||||
for (let j = 0; j < keys.length; j++) {
|
// `range.s.c` is the index of first column in the range
|
||||||
const key = keys[j]
|
// `range.e.c` is the index of last column in the range
|
||||||
const val = row[key]
|
// we'll iterate from first column to last column and
|
||||||
|
// extract value where defined and push to extracted data array
|
||||||
|
for (let j = range.s.c, x = 0; j <= range.e.c; j++, x++) {
|
||||||
|
const col = XLSX.utils.encode_col(j)
|
||||||
|
|
||||||
|
if (col in row) {
|
||||||
// in excel's R1C1 notation indexing starts from 1 but in JS it starts from 0
|
// in excel's R1C1 notation indexing starts from 1 but in JS it starts from 0
|
||||||
// therefore, we'll have to add 1 to rows and cols
|
// therefore, we'll have to add 1 to rows and cols
|
||||||
extractedData.push({
|
extractedData.push({
|
||||||
@ -402,11 +414,12 @@ export class XLMapComponent implements AfterContentInit, AfterViewInit, OnInit {
|
|||||||
XLMAP_ID: rule.XLMAP_ID,
|
XLMAP_ID: rule.XLMAP_ID,
|
||||||
XLMAP_RANGE_ID: rule.XLMAP_RANGE_ID,
|
XLMAP_RANGE_ID: rule.XLMAP_RANGE_ID,
|
||||||
ROW_NO: i + 1,
|
ROW_NO: i + 1,
|
||||||
COL_NO: j + 1,
|
COL_NO: x + 1,
|
||||||
VALUE_TXT: val
|
VALUE_TXT: row[col]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
this.status = Status.ReadyToSubmit
|
this.status = Status.ReadyToSubmit
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "dcfrontend",
|
"name": "dcfrontend",
|
||||||
"version": "6.4.0",
|
"version": "6.6.0",
|
||||||
"description": "Data Controller",
|
"description": "Data Controller",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@saithodev/semantic-release-gitea": "^2.1.0",
|
"@saithodev/semantic-release-gitea": "^2.1.0",
|
||||||
|
@ -42,3 +42,42 @@ create table &dclib..mpe_xlmap_info(
|
|||||||
tx_to num not null,
|
tx_to num not null,
|
||||||
constraint pk_mpe_xlmap_info
|
constraint pk_mpe_xlmap_info
|
||||||
primary key(tx_from,XLMAP_ID));
|
primary key(tx_from,XLMAP_ID));
|
||||||
|
|
||||||
|
|
||||||
|
/* add mpe_tables entries */
|
||||||
|
insert into &dclib..mpe_tables
|
||||||
|
set tx_from=0
|
||||||
|
,tx_to='31DEC5999:23:59:59'dt
|
||||||
|
,libref="&dclib"
|
||||||
|
,dsn='MPE_XLMAP_INFO'
|
||||||
|
,num_of_approvals_required=1
|
||||||
|
,loadtype='TXTEMPORAL'
|
||||||
|
,var_txfrom='TX_FROM'
|
||||||
|
,var_txto='TX_TO'
|
||||||
|
,buskey='XLMAP_ID'
|
||||||
|
,notes='Docs: https://docs.datacontroller.io/complex-excel-uploads'
|
||||||
|
,post_edit_hook='services/hooks/mpe_xlmap_info_postedit'
|
||||||
|
;
|
||||||
|
insert into &dclib..mpe_tables
|
||||||
|
set tx_from=0
|
||||||
|
,tx_to='31DEC5999:23:59:59'dt
|
||||||
|
,libref="&dclib"
|
||||||
|
,dsn='MPE_XLMAP_RULES'
|
||||||
|
,num_of_approvals_required=1
|
||||||
|
,loadtype='TXTEMPORAL'
|
||||||
|
,var_txfrom='TX_FROM'
|
||||||
|
,var_txto='TX_TO'
|
||||||
|
,buskey='XLMAP_ID XLMAP_RANGE_ID'
|
||||||
|
,notes='Docs: https://docs.datacontroller.io/complex-excel-uploads'
|
||||||
|
,post_edit_hook='services/hooks/mpe_xlmap_rules_postedit'
|
||||||
|
;
|
||||||
|
insert into &dclib..mpe_tables
|
||||||
|
set tx_from=0
|
||||||
|
,tx_to='31DEC5999:23:59:59'dt
|
||||||
|
,libref="&dclib"
|
||||||
|
,dsn='MPE_XLMAP_DATA'
|
||||||
|
,num_of_approvals_required=1
|
||||||
|
,loadtype='UPDATE'
|
||||||
|
,buskey='LOAD_REF XLMAP_ID XLMAP_RANGE_ID ROW_NO COL_NO'
|
||||||
|
,notes='Docs: https://docs.datacontroller.io/complex-excel-uploads'
|
||||||
|
;
|
@ -46,7 +46,7 @@
|
|||||||
/* get users TO which the email should be sent */
|
/* get users TO which the email should be sent */
|
||||||
|
|
||||||
proc sql noprint;
|
proc sql noprint;
|
||||||
create table users as select distinct a.alert_user,
|
create table work.users as select distinct a.alert_user,
|
||||||
b.user_displayname,
|
b.user_displayname,
|
||||||
b.user_email
|
b.user_email
|
||||||
from &mpelib..mpe_alerts
|
from &mpelib..mpe_alerts
|
||||||
@ -58,16 +58,26 @@ create table users as select distinct a.alert_user,
|
|||||||
and a.alert_lib in ("&alert_lib","*ALL*")
|
and a.alert_lib in ("&alert_lib","*ALL*")
|
||||||
and a.alert_ds in ("&alert_ds","*ALL*");
|
and a.alert_ds in ("&alert_ds","*ALL*");
|
||||||
|
|
||||||
%local isThere;
|
/* ensure the submitter is included on the email */
|
||||||
|
%local isThere userdisp user_eml;
|
||||||
|
%let isThere=0;
|
||||||
select count(*) into: isThere from &syslast where alert_user="&from_user";
|
select count(*) into: isThere from &syslast where alert_user="&from_user";
|
||||||
%if &isThere>0 %then %do;
|
%if &isThere=0 %then %do;
|
||||||
insert into &syslast set alert_user="&from_user";
|
select user_displayname, user_email
|
||||||
|
into: userdisp trimmed, :user_eml trimmed
|
||||||
|
from &mpelib..mpe_emails
|
||||||
|
where &dc_dttmtfmt. lt tx_to
|
||||||
|
and user_name="&from_user";
|
||||||
|
insert into work.users
|
||||||
|
set alert_user="&from_user"
|
||||||
|
,user_displayname="&userdisp"
|
||||||
|
,user_email="&user_eml";
|
||||||
%end;
|
%end;
|
||||||
|
|
||||||
|
|
||||||
/* if no email / displayname is provided, then extract from metadata */
|
/* if no email / displayname is provided, then extract from metadata */
|
||||||
data emails;
|
data work.emails;
|
||||||
set users;
|
set work.users;
|
||||||
length emailuri uri text $256; call missing(emailuri,uri); drop emailuri uri;
|
length emailuri uri text $256; call missing(emailuri,uri); drop emailuri uri;
|
||||||
|
|
||||||
/* get displayname */
|
/* get displayname */
|
||||||
@ -92,11 +102,13 @@ data emails;
|
|||||||
end;
|
end;
|
||||||
/* only keep valid emails */
|
/* only keep valid emails */
|
||||||
if index(user_email,'@') ;
|
if index(user_email,'@') ;
|
||||||
|
/* dump contents for debugging */
|
||||||
|
if _n_<21 then putlog (_all_)(=);
|
||||||
run;
|
run;
|
||||||
|
|
||||||
%local emails;
|
%local emails;
|
||||||
proc sql noprint;
|
proc sql noprint;
|
||||||
select user_email into: emails separated by '" "' from emails;
|
select quote(trim(user_email)) into: emails separated by ' ' from work.emails;
|
||||||
|
|
||||||
/* exit if nobody to email */
|
/* exit if nobody to email */
|
||||||
%if %mf_getattrn(emails,NLOBS)=0 %then %do;
|
%if %mf_getattrn(emails,NLOBS)=0 %then %do;
|
||||||
@ -110,7 +122,7 @@ data _null_;
|
|||||||
put optname '=' setting;
|
put optname '=' setting;
|
||||||
run;
|
run;
|
||||||
|
|
||||||
filename __out email ("&emails")
|
filename __out email (&emails)
|
||||||
subject="Table &alert_lib..&alert_ds has been &alert_event";
|
subject="Table &alert_lib..&alert_ds has been &alert_event";
|
||||||
|
|
||||||
%local SUBMITTED_TXT;
|
%local SUBMITTED_TXT;
|
||||||
|
70
sas/sasjs/macros/mpe_dsmeta.sas
Normal file
70
sas/sasjs/macros/mpe_dsmeta.sas
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Gets table metadata
|
||||||
|
@details Runs mp_dsmeta and adds datadictionary info
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mp_dsmeta.sas
|
||||||
|
|
||||||
|
@version 9.2
|
||||||
|
@author 4GL Apps Ltd
|
||||||
|
@copyright 4GL Apps Ltd. This code may only be used within Data Controller
|
||||||
|
and may not be re-distributed or re-sold without the express permission of
|
||||||
|
4GL Apps Ltd.
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mpe_dsmeta(libds, outds=dsmeta);
|
||||||
|
%local ddsd ddld notes lenstmt;
|
||||||
|
%let lenstmt=length ods_table $18 name $100 value $1000;
|
||||||
|
%let libds=%upcase(&libds);
|
||||||
|
%mp_dsmeta(&libds, outds=&outds)
|
||||||
|
|
||||||
|
data _null_;
|
||||||
|
set &mpelib..mpe_datadictionary;
|
||||||
|
where &dc_dttmtfmt < tx_to & dd_source=%upcase("&libds") & dd_type='TABLE';
|
||||||
|
call symputx('ddsd',dd_shortdesc,'l');
|
||||||
|
call symputx('ddld',dd_longdesc,'l');
|
||||||
|
run;
|
||||||
|
|
||||||
|
data &outds;
|
||||||
|
&lenstmt;
|
||||||
|
if last then do;
|
||||||
|
ODS_TABLE='MPE_DATADICTIONARY';
|
||||||
|
NAME='DD_SHORTDESC';
|
||||||
|
VALUE="&ddsd";
|
||||||
|
output;
|
||||||
|
NAME='DD_LONGDESC';
|
||||||
|
VALUE="&ddld";
|
||||||
|
output;
|
||||||
|
end;
|
||||||
|
set &outds end=last;
|
||||||
|
output;
|
||||||
|
run;
|
||||||
|
|
||||||
|
data _data_;
|
||||||
|
set &mpelib..mpe_tables;
|
||||||
|
where libref="%scan(&libds,1,.)"
|
||||||
|
& dsn="%scan(&libds,2,.)"
|
||||||
|
& &dc_dttmtfmt<tx_to;
|
||||||
|
&lenstmt;
|
||||||
|
ODS_TABLE='MPE_TABLES';
|
||||||
|
array c _character_;
|
||||||
|
array n _numeric_;
|
||||||
|
do over c;
|
||||||
|
name=upcase(vname(c));
|
||||||
|
value=c;
|
||||||
|
output;
|
||||||
|
end;
|
||||||
|
do over n;
|
||||||
|
name=upcase(vname(n));
|
||||||
|
value=cats(n);
|
||||||
|
output;
|
||||||
|
end;
|
||||||
|
keep ods_table name value;
|
||||||
|
run;
|
||||||
|
|
||||||
|
proc append base=&outds data=&syslast;
|
||||||
|
run;
|
||||||
|
|
||||||
|
|
||||||
|
%mend mpe_dsmeta;
|
37
sas/sasjs/macros/mpe_dsmeta.test.sas
Normal file
37
sas/sasjs/macros/mpe_dsmeta.test.sas
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mpe_dsmeta macro
|
||||||
|
@details Checking functionality of mpe_dsmeta.sas macro
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mp_assertdsobs.sas
|
||||||
|
@li mp_assertscope.sas
|
||||||
|
@li mpe_dsmeta.sas
|
||||||
|
|
||||||
|
@author 4GL Apps Ltd
|
||||||
|
@copyright 4GL Apps Ltd. This code may only be used within Data Controller
|
||||||
|
and may not be re-distributed or re-sold without the express permission of
|
||||||
|
4GL Apps Ltd.
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
/* run the macro*/
|
||||||
|
%mp_assertscope(SNAPSHOT)
|
||||||
|
%mpe_dsmeta(&mpelib..mpe_security, outds=test1)
|
||||||
|
%mp_assertscope(COMPARE,
|
||||||
|
desc=Checking macro variables against previous snapshot
|
||||||
|
)
|
||||||
|
|
||||||
|
data work.test1;
|
||||||
|
set work.test1;
|
||||||
|
where ods_table in ('MPE_DATADICTIONARY','MPE_TABLES');
|
||||||
|
putlog (_all_)(=);
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_assertdsobs(work.test1,
|
||||||
|
desc=Test 1 - 27 records returned,
|
||||||
|
test=EQUALS 27,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
|||||||
<h5> sasdata </h5>
|
<h5> sasdata </h5>
|
||||||
<h5> sasparams </h5>
|
<h5> sasparams </h5>
|
||||||
Contains info on the request. One row is returned.
|
Contains info on the request. One row is returned.
|
||||||
@li CLS_FLG - set to 0 if there are no CLS rules (everything should be editable)
|
@li CLS_FLG - set to 0 if there are no CLS rules (everything editable)
|
||||||
else set to 1 (CLS rules exist)
|
else set to 1 (CLS rules exist)
|
||||||
@li ISMAP - set to 1 if the target DS is an excel map target, else 0
|
@li ISMAP - set to 1 if the target DS is an excel map target, else 0
|
||||||
|
|
||||||
@ -56,12 +56,12 @@
|
|||||||
@li mf_wordsinstr1butnotstr2.sas
|
@li mf_wordsinstr1butnotstr2.sas
|
||||||
@li mp_abort.sas
|
@li mp_abort.sas
|
||||||
@li mp_cntlout.sas
|
@li mp_cntlout.sas
|
||||||
@li mp_dsmeta.sas
|
|
||||||
@li mp_getcols.sas
|
@li mp_getcols.sas
|
||||||
@li mp_getmaxvarlengths.sas
|
@li mp_getmaxvarlengths.sas
|
||||||
@li mp_validatecol.sas
|
@li mp_validatecol.sas
|
||||||
@li mpe_accesscheck.sas
|
@li mpe_accesscheck.sas
|
||||||
@li mpe_columnlevelsecurity.sas
|
@li mpe_columnlevelsecurity.sas
|
||||||
|
@li mpe_dsmeta.sas
|
||||||
@li mpe_getlabels.sas
|
@li mpe_getlabels.sas
|
||||||
@li mpe_filtermaster.sas
|
@li mpe_filtermaster.sas
|
||||||
@li mpe_runhook.sas
|
@li mpe_runhook.sas
|
||||||
@ -645,7 +645,8 @@ create table dqdata as
|
|||||||
%dq_selects()
|
%dq_selects()
|
||||||
|
|
||||||
proc sort data=dqdata;
|
proc sort data=dqdata;
|
||||||
by base_col selectbox_order;
|
/* order by selectbox_order then the value */
|
||||||
|
by base_col selectbox_order rule_data;
|
||||||
run;
|
run;
|
||||||
|
|
||||||
%mp_getmaxvarlengths(work.sasdata1,outds=maxvarlengths)
|
%mp_getmaxvarlengths(work.sasdata1,outds=maxvarlengths)
|
||||||
@ -664,7 +665,7 @@ data xl_rules;
|
|||||||
keep xl_column xl_rule;
|
keep xl_column xl_rule;
|
||||||
run;
|
run;
|
||||||
|
|
||||||
%mp_dsmeta(&libds, outds=dsmeta)
|
%mpe_dsmeta(&libds, outds=dsmeta)
|
||||||
|
|
||||||
/* send to the client */
|
/* send to the client */
|
||||||
%webout(OPEN)
|
%webout(OPEN)
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
data _null_;
|
data _null_;
|
||||||
file &f1 termstr=crlf;
|
file &f1 termstr=crlf;
|
||||||
put 'XLMAP_ID:$char12.';
|
put 'XLMAP_ID:$char12.';
|
||||||
put "Sample";
|
put "BASEL-KM1";
|
||||||
run;
|
run;
|
||||||
|
|
||||||
%mx_testservice(&_program,
|
%mx_testservice(&_program,
|
||||||
@ -38,7 +38,7 @@ run;
|
|||||||
|
|
||||||
%mp_assertdsobs(work.xlmaprules,
|
%mp_assertdsobs(work.xlmaprules,
|
||||||
test=ATLEAST 2,
|
test=ATLEAST 2,
|
||||||
desc=Checking successful return of at least 2 rules for the Sample map,
|
desc=Checking successful return of at least 2 rules for the BASEL-KM1 map,
|
||||||
outds=work.test_results
|
outds=work.test_results
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -44,13 +44,13 @@
|
|||||||
@li mf_verifymacvars.sas
|
@li mf_verifymacvars.sas
|
||||||
@li mp_abort.sas
|
@li mp_abort.sas
|
||||||
@li mp_cntlout.sas
|
@li mp_cntlout.sas
|
||||||
@li mp_dsmeta.sas
|
|
||||||
@li mp_getcols.sas
|
@li mp_getcols.sas
|
||||||
@li mp_getpk.sas
|
@li mp_getpk.sas
|
||||||
@li mp_jsonout.sas
|
@li mp_jsonout.sas
|
||||||
@li mp_searchdata.sas
|
@li mp_searchdata.sas
|
||||||
@li mp_validatecol.sas
|
@li mp_validatecol.sas
|
||||||
@li mpe_columnlevelsecurity.sas
|
@li mpe_columnlevelsecurity.sas
|
||||||
|
@li mpe_dsmeta.sas
|
||||||
@li mpe_filtermaster.sas
|
@li mpe_filtermaster.sas
|
||||||
|
|
||||||
|
|
||||||
@ -351,7 +351,7 @@ run;
|
|||||||
|
|
||||||
%mp_getcols(&libds, outds=cols)
|
%mp_getcols(&libds, outds=cols)
|
||||||
|
|
||||||
%mp_dsmeta(&libds, outds=dsmeta)
|
%mpe_dsmeta(&libds, outds=dsmeta)
|
||||||
|
|
||||||
%webout(OPEN)
|
%webout(OPEN)
|
||||||
%webout(OBJ,cls_rules)
|
%webout(OBJ,cls_rules)
|
||||||
|
Reference in New Issue
Block a user