GreenPlusbyGXS / web /src /utils /accuracyValidator.js
gaialive's picture
Upload 106 files
759768a verified
/**
* ACCURACY VALIDATOR
* Validate data accuracy and consistency across the application
*/
export class AccuracyValidator {
constructor() {
this.validationRules = new Map();
this.validationHistory = [];
this.setupDefaultRules();
}
setupDefaultRules() {
// Environmental data validation rules
this.addRule('temperature', {
min: -50,
max: 60,
unit: '°C',
precision: 1
});
this.addRule('humidity', {
min: 0,
max: 100,
unit: '%',
precision: 1
});
this.addRule('airQuality', {
min: 0,
max: 500,
unit: 'AQI',
precision: 0
});
this.addRule('waterPH', {
min: 0,
max: 14,
unit: 'pH',
precision: 2
});
this.addRule('coordinates', {
latitude: { min: -90, max: 90 },
longitude: { min: -180, max: 180 },
precision: 6
});
// Device pricing validation
this.addRule('devicePrice', {
min: 0,
max: 10000,
unit: '$',
precision: 0
});
// Carbon footprint validation
this.addRule('carbonFootprint', {
min: 0,
max: 1000,
unit: 'kg CO2',
precision: 2
});
}
addRule(field, rule) {
this.validationRules.set(field, rule);
}
validateField(field, value, context = {}) {
const rule = this.validationRules.get(field);
if (!rule) {
return { valid: true, message: 'No validation rule found' };
}
const result = {
valid: true,
message: '',
warnings: [],
correctedValue: value
};
try {
// Type validation
if (typeof value !== 'number' && !Array.isArray(value) && typeof value !== 'object') {
const numValue = parseFloat(value);
if (isNaN(numValue)) {
result.valid = false;
result.message = `Invalid ${field}: must be a number`;
return result;
}
result.correctedValue = numValue;
value = numValue;
}
// Range validation
if (rule.min !== undefined && value < rule.min) {
result.valid = false;
result.message = `${field} value ${value} is below minimum ${rule.min}`;
result.correctedValue = rule.min;
}
if (rule.max !== undefined && value > rule.max) {
result.valid = false;
result.message = `${field} value ${value} is above maximum ${rule.max}`;
result.correctedValue = rule.max;
}
// Precision validation
if (rule.precision !== undefined) {
const rounded = parseFloat(value.toFixed(rule.precision));
if (rounded !== value) {
result.warnings.push(`${field} rounded to ${rule.precision} decimal places`);
result.correctedValue = rounded;
}
}
// Special validations
if (field === 'coordinates') {
return this.validateCoordinates(value, rule);
}
// Context-based validation
if (context.previousValue !== undefined) {
const change = Math.abs(value - context.previousValue);
const threshold = (rule.max - rule.min) * 0.1; // 10% change threshold
if (change > threshold) {
result.warnings.push(`Large change detected: ${change.toFixed(2)} ${rule.unit || ''}`);
}
}
} catch (error) {
result.valid = false;
result.message = `Validation error: ${error.message}`;
}
// Record validation
this.recordValidation(field, value, result);
return result;
}
validateCoordinates(coords, rule) {
const result = {
valid: true,
message: '',
warnings: [],
correctedValue: coords
};
if (!coords || typeof coords !== 'object') {
result.valid = false;
result.message = 'Coordinates must be an object with lat and lng properties';
return result;
}
const { lat, lng } = coords;
// Validate latitude
if (typeof lat !== 'number' || lat < rule.latitude.min || lat > rule.latitude.max) {
result.valid = false;
result.message = `Invalid latitude: ${lat}. Must be between ${rule.latitude.min} and ${rule.latitude.max}`;
}
// Validate longitude
if (typeof lng !== 'number' || lng < rule.longitude.min || lng > rule.longitude.max) {
result.valid = false;
result.message = `Invalid longitude: ${lng}. Must be between ${rule.longitude.min} and ${rule.longitude.max}`;
}
// Precision correction
if (rule.precision !== undefined) {
result.correctedValue = {
lat: parseFloat(lat.toFixed(rule.precision)),
lng: parseFloat(lng.toFixed(rule.precision))
};
}
return result;
}
validateDataSet(data, schema = {}) {
const results = {
valid: true,
errors: [],
warnings: [],
correctedData: { ...data }
};
for (const [field, value] of Object.entries(data)) {
const context = schema[field] || {};
const validation = this.validateField(field, value, context);
if (!validation.valid) {
results.valid = false;
results.errors.push({
field,
message: validation.message,
value,
correctedValue: validation.correctedValue
});
}
if (validation.warnings.length > 0) {
results.warnings.push(...validation.warnings.map(w => ({ field, warning: w })));
}
results.correctedData[field] = validation.correctedValue;
}
return results;
}
recordValidation(field, value, result) {
this.validationHistory.push({
field,
value,
result,
timestamp: Date.now()
});
// Keep only last 1000 validations
if (this.validationHistory.length > 1000) {
this.validationHistory = this.validationHistory.slice(-1000);
}
}
getValidationStats() {
const stats = {
totalValidations: this.validationHistory.length,
successRate: 0,
fieldStats: new Map(),
recentErrors: []
};
let successCount = 0;
const now = Date.now();
const oneHourAgo = now - (60 * 60 * 1000);
this.validationHistory.forEach(validation => {
if (validation.result.valid) {
successCount++;
} else if (validation.timestamp > oneHourAgo) {
stats.recentErrors.push({
field: validation.field,
message: validation.result.message,
timestamp: validation.timestamp
});
}
// Field statistics
if (!stats.fieldStats.has(validation.field)) {
stats.fieldStats.set(validation.field, {
total: 0,
errors: 0,
warnings: 0
});
}
const fieldStat = stats.fieldStats.get(validation.field);
fieldStat.total++;
if (!validation.result.valid) fieldStat.errors++;
if (validation.result.warnings && validation.result.warnings.length > 0) {
fieldStat.warnings++;
}
});
stats.successRate = stats.totalValidations > 0 ?
(successCount / stats.totalValidations) * 100 : 100;
return stats;
}
clearHistory() {
this.validationHistory = [];
}
}
// Global validator instance
export const accuracyValidator = new AccuracyValidator();
// Utility functions
export const validateField = (field, value, context) =>
accuracyValidator.validateField(field, value, context);
export const validateDataSet = (data, schema) =>
accuracyValidator.validateDataSet(data, schema);
export const getValidationStats = () =>
accuracyValidator.getValidationStats();
export const addValidationRule = (field, rule) =>
accuracyValidator.addRule(field, rule);