data lake

This commit is contained in:
string 2025-10-18 08:36:44 +05:30
parent 251bc56428
commit 7f80ea6330
5 changed files with 905 additions and 181 deletions

View File

@ -55,11 +55,13 @@
<clr-dg-column [clrDgField]="'sure_connect_id'"> <ng-container *clrDgHideableColumn="{hidden: false}"> SureConnect
</ng-container></clr-dg-column>
<clr-dg-column [clrDgField]="'calculated_field_json'"> <ng-container *clrDgHideableColumn="{hidden: false}"> Calculated Fields
<clr-dg-column [clrDgField]="'calculated_field_json'"> <ng-container *clrDgHideableColumn="{hidden: false}">
Calculated Fields
</ng-container></clr-dg-column>
<!-- who column -->
<clr-dg-column> <ng-container *clrDgHideableColumn="{hidden: false}">
<clr-dg-column>
<ng-container *clrDgHideableColumn="{hidden: false}">
<clr-icon shape="bars"></clr-icon> Action
</ng-container></clr-dg-column>
<!-- end -->
@ -99,8 +101,9 @@
<clr-dg-cell>{{user.sureconnect_name}}</clr-dg-cell>
<clr-dg-cell (click)="goToReplaceStringjson(user.calculated_field_json)" style="cursor: pointer; align-items: center;"><clr-icon
shape="details" *ngIf="user.calculated_field_json"></clr-icon></clr-dg-cell>
<clr-dg-cell (click)="goToReplaceStringjson(user.calculated_field_json)"
style="cursor: pointer; align-items: center;"><clr-icon shape="details"
*ngIf="user.calculated_field_json"></clr-icon></clr-dg-cell>
<!-- who column -->
<clr-dg-cell>
@ -165,7 +168,9 @@
</div>
</div>
</div>
</ng-template><ng-container *ngIf="isCardview">
</ng-template>
<ng-container *ngIf="isCardview">
<div *ngIf="product; else showInfo" class="clr-row clr-align-items-start clr-justify-content-start">
<div *ngFor="let app of product| filter:search; let index = i" class="clr-col-auto">
<div class="clr-row">
@ -227,6 +232,7 @@
{{afterText(item.fieldtext)}}
</div>
<div *ngIf="item.name === 'Line'" class="title-card card-title"
[style.text-align]="item.alignment !== '' ? item.alignment : 'left'"
[style.line-height]="item.textlineheight !== '' ? item.textlineheight : '1'"
@ -240,7 +246,6 @@
<hr>
</div>
<div *ngIf="item.name === 'Icon'" class="icon-card"
[style.text-align]="item.alignment !== '' ? item.alignment : 'left'"
[style.line-height]="item.textlineheight !== '' ? item.textlineheight : '1'"
@ -282,16 +287,6 @@
</div>
<clr-modal [(clrModalOpen)]="rsModaljson" [clrModalSize]="'xl'" [clrModalStaticBackdrop]="true">
<div class="modal-body">
<textarea class="form-control" style="width:100%; height: 400px;" readonly>{{rowSelected}}</textarea>
@ -522,21 +517,27 @@
<div class="modal-body">
<form [formGroup]="calculatedFieldForm">
<div class="clr-row">
<div class="clr-col-12">
<h4>Available Keys from API:</h4>
<ul>
<li *ngFor="let key of availableKeys">{{ key }}</li>
</ul>
<div class="clr-col-12 calculated-section">
<h4 class="section-title">
<clr-icon shape="key" class="section-icon"></clr-icon>
Available Keys from API
</h4>
<div class="field-container">
<span *ngFor="let key of availableKeys" class="field-tag" (click)="addFieldToEquation(key)">
<clr-icon shape="data-field" class="field-icon"></clr-icon>
{{ key }}
</span>
</div>
</div>
<div class="clr-col-12">
<label>Calculated Field Name</label>
<input class="clr-input" type="text" formControlName="fieldName" placeholder="Enter field name" />
<div class="clr-col-12 calculated-section">
<label class="field-label">Calculated Field Name</label>
<input class="clr-input field-input" type="text" formControlName="fieldName" placeholder="Enter field name" />
</div>
<div class="clr-col-12">
<label>Operation</label>
<select formControlName="operation">
<div class="clr-col-12 calculated-section">
<label class="field-label">Operation</label>
<select class="field-input" formControlName="operation">
<option value="">Select Operation</option>
<option value="add">Addition (+)</option>
<option value="subtract">Subtraction (-)</option>
@ -544,47 +545,128 @@
<option value="divide">Division (/)</option>
<option value="percentage">Percentage (%)</option>
<option value="concat">Concatenation (||)</option>
<option value="complex">Complex Equation</option>
</select>
</div>
<!-- Complex Equation Section -->
<div class="clr-col-12 calculated-section" *ngIf="calculatedFieldForm.get('operation')?.value === 'complex'">
<label class="field-label">Complex Equation</label>
<textarea class="clr-textarea equation-input" formControlName="complexEquation"
placeholder="Enter complex equation using actual field names" rows="3"></textarea>
<small class="clr-subtext equation-hint">
Use actual field names from the available keys list above, or predefined constants (constant_X).
Example: (field1 + field2) * constant_1 - (field3 / field4)
</small>
<!-- Available Operators -->
<div class="operators-container">
<h5 class="subsection-title">
<clr-icon shape="calculator" class="subsection-icon"></clr-icon>
Available Operators
</h5>
<div class="operator-tags">
<span class="operator-tag" (click)="addOperatorToEquation('+')">+</span>
<span class="operator-tag" (click)="addOperatorToEquation('-')">-</span>
<span class="operator-tag" (click)="addOperatorToEquation('*')">×</span>
<span class="operator-tag" (click)="addOperatorToEquation('/')">÷</span>
<span class="operator-tag" (click)="addOperatorToEquation('(')">(</span>
<span class="operator-tag" (click)="addOperatorToEquation(')')">)</span>
<span class="operator-tag" (click)="addOperatorToEquation('[')">[</span>
<span class="operator-tag" (click)="addOperatorToEquation(']')">]</span>
<!-- <span class="operator-tag" (click)="addOperatorToEquation('{')">{</span>
<span class="operator-tag" (click)="addOperatorToEquation('}')">}</span> -->
<span class="operator-tag" (click)="addOperatorToEquation('^')">^</span>
<span class="operator-tag" (click)="addOperatorToEquation('%')">%</span>
<span class="operator-tag" (click)="addOperatorToEquation('=')">=</span>
<span class="operator-tag" (click)="addOperatorToEquation('>')">&gt;</span>
<span class="operator-tag" (click)="addOperatorToEquation('<')">&lt;</span>
<span class="operator-tag" (click)="addOperatorToEquation('>=')">&gt;=</span>
<span class="operator-tag" (click)="addOperatorToEquation('<=')">&lt;=</span>
<span class="operator-tag" (click)="addOperatorToEquation('!=')">!=</span>
<span class="operator-tag" (click)="addOperatorToEquation('&&')">&&</span>
<span class="operator-tag" (click)="addOperatorToEquation('||')">||</span>
</div>
</div>
<!-- Available Fields -->
<div class="fields-container">
<h5 class="subsection-title">
<clr-icon shape="data-field" class="subsection-icon"></clr-icon>
Available Fields
</h5>
<div class="field-tags">
<span *ngFor="let key of availableKeys" class="field-tag" (click)="addFieldToEquation(key)">
<clr-icon shape="data-field" class="field-icon"></clr-icon>
{{ key }}
</span>
</div>
</div>
<!-- Defined Constants -->
<div class="constants-container" *ngIf="getDefinedConstants().length > 0">
<h5 class="subsection-title">
<clr-icon shape="pin" class="subsection-icon"></clr-icon>
Defined Constants
</h5>
<div class="constant-tags">
<span *ngFor="let constant of getDefinedConstants()" class="constant-tag"
(click)="addFieldToEquation(constant)">
<clr-icon shape="pin" class="constant-icon"></clr-icon>
{{ constant }}
</span>
</div>
</div>
<!-- Add New Constant -->
<div class="new-constant-container">
<button class="btn btn-sm btn-secondary add-constant-btn" (click)="addNewConstantToEquation()">
<clr-icon shape="plus"></clr-icon> Add New Constant
</button>
</div>
</div>
<!-- Multiple Fields Section -->
<div class="clr-col-12" *ngIf="calculatedFieldForm.get('operation')?.value">
<label>Fields and Constants</label>
<div class="clr-row">
<div class="clr-col-12">
<div class="clr-col-12 calculated-section"
*ngIf="calculatedFieldForm.get('operation')?.value && calculatedFieldForm.get('operation')?.value !== 'complex'">
<label class="field-label">Fields and Constants</label>
<div class="field-components-container">
<div formArrayName="fieldComponents">
<div *ngFor="let fieldComponent of getFieldComponents().controls; let i = index"
[formGroupName]="i" class="clr-row field-component-row">
<div class="clr-col-5">
<select formControlName="field">
<div *ngFor="let fieldComponent of getFieldComponents().controls; let i = index" [formGroupName]="i"
class="field-component-row">
<div class="field-select-container">
<select class="field-select" formControlName="field">
<option value="">Select Field</option>
<option *ngFor="let key of availableKeys" [value]="key">{{ key }}</option>
</select>
</div>
<div class="clr-col-5">
<input type="text" formControlName="constant" placeholder="Or enter constant value" />
<div class="constant-input-container">
<input type="text" class="constant-input" formControlName="constant" placeholder="Or enter constant value" />
</div>
<div class="clr-col-2">
<button type="button" class="btn btn-icon btn-danger" (click)="removeFieldComponent(i)"
<div class="remove-btn-container">
<button type="button" class="btn btn-icon btn-danger remove-field-btn" (click)="removeFieldComponent(i)"
*ngIf="getFieldComponents().length > 1">
<clr-icon shape="trash"></clr-icon>
</button>
</div>
</div>
</div>
<button type="button" class="btn btn-sm" (click)="addFieldComponent()">
<button type="button" class="btn btn-sm add-field-btn" (click)="addFieldComponent()">
<clr-icon shape="plus"></clr-icon> Add Field/Constant
</button>
</div>
</div>
</div>
</div>
</form>
<div class="clr-row" style="margin-top: 20px;">
<div class="clr-row created-fields-section">
<div class="clr-col-12">
<h4>Created Calculated Fields:</h4>
<table class="table">
<h4 class="section-title">
<clr-icon shape="list" class="section-icon"></clr-icon>
Created Calculated Fields
</h4>
<div class="table-container">
<table class="table calculated-fields-table">
<thead>
<tr>
<th>Field Name</th>
@ -595,18 +677,21 @@
</thead>
<tbody>
<tr *ngFor="let field of calculatedFields">
<td>{{ field.fieldName }}</td>
<td>{{ field.operation }}</td>
<td>
<td class="field-name-cell">{{ field.fieldName }}</td>
<td class="operation-cell">{{ field.operation }}</td>
<td class="expression-cell">
<span *ngIf="field.operation === 'complex'">{{ field.complexEquation }}</span>
<span *ngIf="field.operation !== 'complex'">
<span *ngFor="let component of field.fieldComponents; let last = last">
<span *ngIf="component.field && !component.isConstant">{{ component.field }}</span>
<span *ngIf="component.isConstant">"{{ component.constant }}"</span>
<span *ngIf="!component.field && component.constant">"{{ component.constant }}"</span>
<span *ngIf="!last"> {{ getOperationSymbol(field.operation) }} </span>
</span>
</span>
</td>
<td>
<button class="btn btn-icon btn-danger" (click)="deleteCalculatedField(field.id)">
<td class="actions-cell">
<button class="btn btn-icon btn-danger delete-field-btn" (click)="deleteCalculatedField(field.id)">
<clr-icon shape="trash"></clr-icon>
</button>
</td>
@ -616,6 +701,7 @@
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline" (click)="calculatedFieldModalOpen = false">Cancel</button>
<button type="button" class="btn btn-primary" (click)="addCalculatedField()">Add Field</button>
@ -626,6 +712,26 @@
</div>
</clr-modal>
<!-- Constant Value Modal -->
<clr-modal [(clrModalOpen)]="constantValueModalOpen" [clrModalSize]="'sm'" [clrModalStaticBackdrop]="true">
<h3 class="modal-title">Define Constant Value</h3>
<div class="modal-body">
<div class="clr-row">
<div class="clr-col-12">
<p>Enter a value for constant: <strong>{{ currentConstantName }}</strong></p>
</div>
<div class="clr-col-12">
<label>Constant Value</label>
<input class="clr-input" type="text" [(ngModel)]="currentConstantValue" placeholder="Enter constant value" />
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline" (click)="cancelConstantValue()">Cancel</button>
<button type="button" class="btn btn-primary" (click)="saveConstantValue()">Save</button>
</div>
</clr-modal>

View File

@ -1,85 +1,297 @@
//@import "../../../../assets/scss/var";
.s-info-bar {
display: flex;
flex-direction: row;
justify-content: space-between;
button {
outline: none;
}
}
.delete,.heading{
text-align: center;
color: red;
}
.entry-pg {
width: 750px;
/* Calculated Fields Modal Styles */
.calculated-section {
margin-bottom: 20px;
padding: 15px;
border-radius: 8px;
background-color: #f9f9f9;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
.button1::after {
content: none;
}
.button1:hover::after {
content: "ADD ROWS";
}
.section {
background-color: #dddddd;
height: 40px;
}
.section p {
//color: white;
padding: 10px;
font-size: 18px;
}
.clr-input {
color: #212529;
border: 1px solid #ced4da;
border-radius: 0.25rem;
padding: 0.75rem 0.75rem;
margin-top: 3px;
width: 100%;
margin-bottom: 10px;
}
.clr-file {
color: #212529;
border: 1px solid #ced4da;
border-radius: 0.25rem;
//padding: 0.6rem 0.75rem;
margin-top: 3px;
width: 100%;
margin-bottom: 10px;
}
.center {
text-align: center;
}
select{
width: 100%;
margin-top: 3px;
padding: 5px 5px;
border: 1px solid #ccc;
border-radius: 4px;
}
input[type=text],[type=date],[type=number],textarea {
width: 100%;
padding: 15px 15px;
background-color:rgb(255, 255, 255);
// margin: 8px 0;
display: inline-block;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
}
.error_mess {
color: red;
}
.universal-section-header {
margin: 24px 0 10px 0;
.section-title {
color: #0072a0;
font-weight: 600;
color: #1a237e;
letter-spacing: 0.5px;
font-size: 1.25rem;
margin-bottom: 15px;
display: flex;
align-items: center;
}
.section-icon {
margin-right: 10px;
}
.field-label {
display: block;
margin-bottom: 8px;
font-weight: 500;
color: #333;
}
.field-input {
width: 100%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 14px;
transition: border-color 0.3s;
}
.field-input:focus {
border-color: #0072a0;
outline: none;
box-shadow: 0 0 0 2px rgba(0, 114, 160, 0.2);
}
.field-container {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 10px;
}
.field-tag {
display: inline-flex;
align-items: center;
background-color: #e3f2fd;
border: 1px solid #bbdefb;
border-radius: 20px;
padding: 6px 12px;
margin: 4px;
font-size: 13px;
font-family: monospace;
cursor: pointer;
transition: all 0.2s;
}
.field-tag:hover {
background-color: #bbdefb;
transform: translateY(-2px);
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.field-icon {
margin-right: 5px;
color: #1976d2;
}
.equation-input {
width: 100%;
padding: 12px;
border: 1px solid #ccc;
border-radius: 6px;
font-family: monospace;
font-size: 14px;
margin-bottom: 8px;
}
.equation-hint {
display: block;
margin-bottom: 15px;
color: #666;
font-style: italic;
}
.operators-container,
.fields-container,
.constants-container,
.new-constant-container {
margin-bottom: 20px;
}
.subsection-title {
color: #555;
font-weight: 500;
margin-bottom: 10px;
display: flex;
align-items: center;
}
.subsection-icon {
margin-right: 8px;
color: #0072a0;
}
.operator-tags {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.operator-tag {
display: inline-flex;
align-items: center;
justify-content: center;
background-color: #0072a0;
color: white;
border: 1px solid #005a80;
border-radius: 4px;
padding: 6px 10px;
font-size: 14px;
font-family: monospace;
cursor: pointer;
transition: all 0.2s;
min-width: 36px;
}
.operator-tag:hover {
background-color: #005a80;
transform: scale(1.1);
}
.field-tags {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.constant-tags {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.constant-tag {
display: inline-flex;
align-items: center;
background-color: #4caf50;
color: white;
border: 1px solid #388e3c;
border-radius: 20px;
padding: 6px 12px;
margin: 4px;
font-size: 13px;
font-family: monospace;
cursor: pointer;
transition: all 0.2s;
}
.constant-tag:hover {
background-color: #388e3c;
transform: translateY(-2px);
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.constant-icon {
margin-right: 5px;
}
.add-constant-btn {
background-color: #ff9800;
border-color: #f57c00;
}
.add-constant-btn:hover {
background-color: #f57c00;
}
.field-components-container {
background-color: white;
border: 1px solid #e0e0e0;
border-radius: 6px;
padding: 15px;
}
.field-component-row {
display: flex;
align-items: center;
margin-bottom: 10px;
gap: 10px;
}
.field-select-container,
.constant-input-container {
flex: 1;
}
.field-select,
.constant-input {
width: 100%;
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
}
.remove-field-btn {
padding: 6px;
}
.add-field-btn {
margin-top: 10px;
background-color: #1976d2;
border-color: #1976d2;
color: white;
}
.add-field-btn:hover {
background-color: #1565c0;
}
.created-fields-section {
margin-top: 25px;
padding-top: 20px;
border-top: 1px solid #eee;
}
.table-container {
overflow-x: auto;
}
.calculated-fields-table {
width: 100%;
border-collapse: collapse;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
border-radius: 6px;
overflow: hidden;
}
.calculated-fields-table th {
background-color: #f5f5f5;
padding: 12px 15px;
text-align: left;
font-weight: 600;
color: #333;
}
.calculated-fields-table td {
padding: 12px 15px;
border-bottom: 1px solid #eee;
}
.field-name-cell {
font-weight: 500;
color: #0072a0;
}
.operation-cell {
font-family: monospace;
background-color: #e3f2fd;
border-radius: 4px;
padding: 4px 8px;
display: inline-block;
}
.expression-cell {
font-family: monospace;
font-size: 13px;
}
.actions-cell {
text-align: center;
}
.delete-field-btn {
padding: 6px;
}
/* Responsive adjustments */
@media (max-width: 768px) {
.field-component-row {
flex-direction: column;
align-items: stretch;
}
.field-select-container,
.constant-input-container {
margin-bottom: 8px;
}
}

View File

@ -70,6 +70,12 @@ export class Data_lakeComponent implements OnInit {
calculatedFields: any[] = [];
constantCounter = 1; // Persistent counter for unique constant names
// New properties for constant value input
constantValueModalOpen = false;
currentConstantName = '';
currentConstantValue = '';
pendingEquationUpdate = '';
constructor(
private extensionService: ExtensionService,
private userInfoService: UserInfoService,
@ -136,8 +142,16 @@ export class Data_lakeComponent implements OnInit {
this.calculatedFieldForm = this._fb.group({
fieldName: [''],
operation: [''],
complexEquation: [''], // Add complex equation field
fieldComponents: this._fb.array([this.createFieldComponent()])
});
// Add real-time validation for complex equation
this.calculatedFieldForm.get('complexEquation')?.valueChanges.subscribe(value => {
if (this.calculatedFieldForm.get('operation')?.value === 'complex' && value) {
this.analyzeComplexEquation(value);
}
});
}
// Helper method to create a field component group
@ -241,6 +255,7 @@ export class Data_lakeComponent implements OnInit {
this.calculatedFieldForm.reset({
fieldName: '',
operation: '',
complexEquation: '',
fieldComponents: [{}] // Start with one empty field component
});
@ -257,11 +272,73 @@ export class Data_lakeComponent implements OnInit {
addCalculatedField() {
const formData = this.calculatedFieldForm.value;
if (!formData.fieldName || !formData.operation) {
if (!formData.fieldName || (!formData.operation && formData.operation !== 'complex')) {
this.toastr.error('Field name and operation are required');
return;
}
// Handle complex equation separately
if (formData.operation === 'complex') {
if (!formData.complexEquation || formData.complexEquation.trim() === '') {
this.toastr.error('Complex equation is required');
return;
}
// Validate complex equation format
if (!this.validateComplexEquation(formData.complexEquation)) {
return;
}
// Extract constants used in the equation
const fieldRefs = this.extractFieldReferences(formData.complexEquation);
const equationConstants: any[] = [];
for (const fieldRef of fieldRefs) {
// Check if it's a constant (constant_X format)
if (fieldRef.startsWith('constant_') && /^\d+$/.test(fieldRef.substring(9))) {
// Find the constant value from existing calculated fields
let constantValue = '';
for (const field of this.calculatedFields) {
if (field.operation === 'constant' && field.fieldName === fieldRef) {
if (field.fieldComponents && field.fieldComponents.length > 0) {
constantValue = field.fieldComponents[0].constant;
break;
}
}
}
if (constantValue) {
equationConstants.push({
field: fieldRef,
constant: constantValue,
isConstant: true
});
}
}
}
// Create calculated field object for complex equation
const calculatedField = {
id: Date.now(),
fieldName: formData.fieldName,
operation: 'complex',
complexEquation: formData.complexEquation,
fieldComponents: equationConstants // Include constants used in the equation
};
this.calculatedFields.push(calculatedField);
this.toastr.success('Complex calculated field added successfully');
// Reset form but keep one empty field component
this.calculatedFieldForm.reset({
fieldName: '',
operation: '',
complexEquation: '',
fieldComponents: [{}]
});
return;
}
// Filter out empty field components
const validFieldComponents = formData.fieldComponents.filter(component =>
component.field || component.constant
@ -328,10 +405,187 @@ export class Data_lakeComponent implements OnInit {
this.calculatedFieldForm.reset({
fieldName: '',
operation: '',
complexEquation: '',
fieldComponents: [{}]
});
}
// Validate complex equation format
validateComplexEquation(equation: string): boolean {
// Updated validation to include additional operators
const validPattern = /^[a-zA-Z0-9_+\-*/().\[\]{}^%>=<!&|\s]+$/;
if (!validPattern.test(equation)) {
this.toastr.error('Invalid complex equation format. Only alphanumeric characters, underscores, and operators (+, -, *, /, (, ), [, ], {, }, ^, %, =, >, <, !, &, |) are allowed.');
return false;
}
// Check for empty equation
if (!equation || equation.trim() === '') {
this.toastr.error('Complex equation cannot be empty');
return false;
}
// Extract all field/constant references from the equation
const fieldRefs = this.extractFieldReferences(equation);
// Validate each reference
const invalidReferences: string[] = [];
for (const fieldRef of fieldRefs) {
// Skip if it's a direct numeric value
if (!isNaN(Number(fieldRef))) {
continue;
}
// Check if it's a predefined constant (constant_X format)
if (fieldRef.startsWith('constant_') && /^\d+$/.test(fieldRef.substring(9))) {
// Check if this constant has been defined in previous calculated fields
let constantFound = false;
for (const field of this.calculatedFields) {
if (field.operation === 'constant' && field.fieldName === fieldRef) {
constantFound = true;
break;
}
}
if (!constantFound) {
invalidReferences.push(`Referenced constant '${fieldRef}' has not been defined`);
}
continue;
}
// Check if field exists in available keys
if (!this.availableKeys.includes(fieldRef)) {
invalidReferences.push(`Field '${fieldRef}' not found in available keys`);
}
}
// Show all validation errors
if (invalidReferences.length > 0) {
invalidReferences.forEach(error => this.toastr.error(error));
return false;
}
// Try to parse the equation to check for basic syntax errors
try {
this.parseEquation(equation);
} catch (error) {
this.toastr.error(`Invalid equation syntax: ${error.message}`);
return false;
}
// Additional validation for complex expressions
this.analyzeComplexEquation(equation);
return true;
}
// Analyze complex equation and provide warnings
analyzeComplexEquation(equation: string): void {
// Check for potentially problematic patterns
if (/[+\-*/]{3,}/.test(equation)) {
this.toastr.warning('Equation contains multiple consecutive operators which may cause unexpected results');
}
// Check for unbalanced expressions
const operators = equation.match(/[+\-*/]/g) || [];
const operands = equation.match(/[a-zA-Z0-9_]+/g) || [];
if (operators.length > 0 && operands.length === 0) {
this.toastr.warning('Equation contains operators but no operands (fields or constants)');
}
// Check for division by zero patterns
if (/\/\s*0(?!\d)/.test(equation)) {
this.toastr.warning('Equation may contain division by zero');
}
// Check for excessive nesting
let parenDepth = 0;
let maxParenDepth = 0;
for (const char of equation) {
if (char === '(') {
parenDepth++;
maxParenDepth = Math.max(maxParenDepth, parenDepth);
} else if (char === ')') {
parenDepth--;
}
}
if (maxParenDepth > 10) {
this.toastr.warning('Equation has deep nesting which may affect performance');
}
// Check for repeated field usage
const fieldRefs = this.extractFieldReferences(equation);
const fieldCounts: {[key: string]: number} = {};
fieldRefs.forEach(field => {
if (!isNaN(Number(field))) return; // Skip numbers
fieldCounts[field] = (fieldCounts[field] || 0) + 1;
});
Object.keys(fieldCounts).forEach(field => {
if (fieldCounts[field] > 3) {
this.toastr.warning(`Field '${field}' is used ${fieldCounts[field]} times in the equation`);
}
});
}
// Extract field references from equation
extractFieldReferences(equation: string): string[] {
// Remove all operators and parentheses to isolate field names
const cleanEquation = equation.replace(/[+\-*/()[\]{}^%>=<!&|\s]+/g, ' ');
// Split by whitespace and filter out empty strings and numbers
return cleanEquation.split(/\s+/).filter(token => token.length > 0);
}
// Simple equation parser to check syntax (basic implementation)
parseEquation(equation: string): void {
// Remove spaces
const cleanEquation = equation.replace(/\s/g, '');
// Check for balanced parentheses
let parenCount = 0;
for (const char of cleanEquation) {
if (char === '(') parenCount++;
if (char === ')') parenCount--;
if (parenCount < 0) throw new Error('Mismatched parentheses');
}
if (parenCount !== 0) throw new Error('Mismatched parentheses');
// Check for balanced square brackets
let bracketCount = 0;
for (const char of cleanEquation) {
if (char === '[') bracketCount++;
if (char === ']') bracketCount--;
if (bracketCount < 0) throw new Error('Mismatched square brackets');
}
if (bracketCount !== 0) throw new Error('Mismatched square brackets');
// Check for balanced curly braces
let braceCount = 0;
for (const char of cleanEquation) {
if (char === '{') braceCount++;
if (char === '}') braceCount--;
if (braceCount < 0) throw new Error('Mismatched curly braces');
}
if (braceCount !== 0) throw new Error('Mismatched curly braces');
// Check for invalid sequences
if (/[+\-*/]{2,}/.test(cleanEquation)) {
throw new Error('Invalid operator sequence');
}
// Check that equation doesn't start or end with an operator (except minus for negative numbers)
if (/^[+*/^%=<>|&]/.test(cleanEquation) || /[+\-*/^%=<>|&]$/.test(cleanEquation)) {
throw new Error('Equation cannot start or end with an operator');
}
// Check for empty parentheses
if (/\(\)/.test(cleanEquation)) {
throw new Error('Empty parentheses are not allowed');
}
}
// Delete calculated field
deleteCalculatedField(id: number) {
this.calculatedFields = this.calculatedFields.filter(field => field.id !== id);
@ -562,4 +816,156 @@ export class Data_lakeComponent implements OnInit {
}
);
}
// Add operator to complex equation
addOperatorToEquation(operator: string) {
const equationControl = this.calculatedFieldForm.get('complexEquation');
if (equationControl) {
const currentValue = equationControl.value || '';
equationControl.setValue(currentValue + operator);
}
}
// Add field or constant to complex equation
addFieldToEquation(field: string) {
const equationControl = this.calculatedFieldForm.get('complexEquation');
if (equationControl) {
const currentValue = equationControl.value || '';
// Add space before and after if needed
const newValue = currentValue ? `${currentValue} ${field} ` : `${field} `;
equationControl.setValue(newValue);
// Validate after adding field
if (this.calculatedFieldForm.get('operation')?.value === 'complex') {
this.analyzeComplexEquation(equationControl.value);
}
}
}
// Get all defined constants from existing calculated fields
getDefinedConstants(): string[] {
const constants: string[] = [];
this.calculatedFields.forEach(field => {
// Check for actual constant operations
if (field.operation === 'constant' && field.fieldName) {
constants.push(field.fieldName);
}
});
return constants;
}
// Add a new constant to the equation
addNewConstantToEquation() {
// Generate a new constant name
this.currentConstantName = `constant_${this.constantCounter}`;
// Open the constant value modal
this.currentConstantValue = '';
this.constantValueModalOpen = true;
}
// Save constant value and update equation
saveConstantValue() {
if (this.currentConstantValue === '') {
this.toastr.error('Constant value is required');
return;
}
// Validate constant value based on operation type
const operation = this.calculatedFieldForm.get('operation')?.value;
if (operation !== 'concat' && operation !== 'complex') {
const numericValue = Number(this.currentConstantValue);
if (isNaN(numericValue)) {
this.toastr.error('Constant values must be numeric for this operation');
return;
}
}
// For non-complex operations, add to field components directly
if (operation && operation !== 'complex') {
const fieldComponents = this.getFieldComponents();
fieldComponents.push(this._fb.group({
field: [this.currentConstantName],
constant: [this.currentConstantValue]
}));
} else if (operation === 'complex') {
// For complex equations, add to the equation and create a constant field
const equationControl = this.calculatedFieldForm.get('complexEquation');
if (equationControl) {
const currentValue = equationControl.value || '';
const newValue = currentValue ? `${currentValue} ${this.currentConstantName} ` : `${this.currentConstantName} `;
equationControl.setValue(newValue);
}
// Also create a constant field that can be referenced
const constantField = {
id: Date.now(),
fieldName: this.currentConstantName,
operation: 'constant',
fieldComponents: [{
field: this.currentConstantName,
constant: this.currentConstantValue,
isConstant: true
}]
};
this.calculatedFields.push(constantField);
} else {
// If no operation is selected, just create the constant field
const constantField = {
id: Date.now(),
fieldName: this.currentConstantName,
operation: 'constant',
fieldComponents: [{
field: this.currentConstantName,
constant: this.currentConstantValue,
isConstant: true
}]
};
this.calculatedFields.push(constantField);
}
// Increment the counter for next constant
this.constantCounter++;
// Close modal and show success message
this.constantValueModalOpen = false;
this.toastr.success(`Constant ${this.currentConstantName} added with value ${this.currentConstantValue}`);
}
// Cancel constant value input
cancelConstantValue() {
this.constantValueModalOpen = false;
this.currentConstantName = '';
this.currentConstantValue = '';
}
// Test method to verify constant validation
testConstantFunctionality() {
// Clear existing calculated fields for testing
this.calculatedFields = [];
// Add a test constant
const testConstant = {
id: Date.now(),
fieldName: 'constant_1',
operation: 'constant',
fieldComponents: [{
field: 'constant_1',
constant: '15',
isConstant: true
}]
};
this.calculatedFields.push(testConstant);
// Test validation
const testEquation = 'pincode + mobno * constant_1';
const isValid = this.validateComplexEquation(testEquation);
console.log('Test equation:', testEquation);
console.log('Validation result:', isValid);
console.log('Defined constants:', this.getDefinedConstants());
}
}

View File

@ -14,7 +14,7 @@
<div class="clr-col-4" style="text-align: right;">
<button id="add" class="btn btn-primary" *ngIf="mcreate == 'true'" (click)="goToAdd()">
<button id="add" class="btn btn-primary" (click)="goToAdd()">
<clr-icon shape="plus"></clr-icon>ADD
</button>

View File

@ -31,12 +31,12 @@ export class SureconnectComponent implements OnInit {
ngOnInit(): void {
this.showdata = this.menuGroupService.getdata();
console.log(this.showdata);
this.mcreate = this.showdata.mcreate;
console.log(this.mcreate);
this.mdelete = this.showdata.mdelete
console.log(this.mdelete);
this.medit = this.showdata.medit
console.log(this.medit);
// this.mcreate = this.showdata.mcreate;
// console.log(this.mcreate);
// this.mdelete = this.showdata.mdelete
// console.log(this.mdelete);
// this.medit = this.showdata.medit
// console.log(this.medit);
this.getall();
}
getall() {