defarm-sdk
Version:
DeFarm SDK - On-premise blockchain data processing and tokenization engine for agriculture supply chain
223 lines (185 loc) • 6.98 kB
text/typescript
import { BaseProcessor } from './BaseProcessor';
import type { Asset, ValidationError } from '../types';
/**
* Processor for livestock assets (cattle, pigs, sheep, chickens)
*/
export class LivestockProcessor extends BaseProcessor {
name = 'LivestockProcessor';
version = '1.0.0';
protected async validateCategory(asset: Asset): Promise<ValidationError[]> {
const errors: ValidationError[] = [];
// Validate subcategory
const validSubcategories = ['cattle', 'pigs', 'sheep', 'chickens', 'goats'];
if (!validSubcategories.includes(asset.subcategory)) {
errors.push({
field: 'subcategory',
message: `Invalid livestock subcategory. Must be one of: ${validSubcategories.join(', ')}`,
code: 'INVALID_SUBCATEGORY'
});
}
// Validate weight if present
const weightError = this.validateNumericField(asset, 'weight_kg', 0, 10000);
if (weightError) errors.push(weightError);
// Validate age if present
const ageError = this.validateNumericField(asset, 'age_months', 0, 600);
if (ageError) errors.push(ageError);
// Validate birth date if present
const birthDateError = this.validateDateField(asset, 'birth_date', false);
if (birthDateError) errors.push(birthDateError);
// Subcategory-specific validations
switch (asset.subcategory) {
case 'cattle':
errors.push(...this.validateCattle(asset));
break;
case 'pigs':
errors.push(...this.validatePigs(asset));
break;
case 'chickens':
errors.push(...this.validateChickens(asset));
break;
}
return errors;
}
protected async processCategory(asset: Asset): Promise<Asset> {
// Ensure standard livestock fields exist
this.ensureDataField(asset, 'health_status', 'healthy');
this.ensureDataField(asset, 'vaccination_status', 'unknown');
// Calculate age from birth date if not provided
if (asset.data.birth_date && !asset.data.age_months) {
const birthDate = new Date(asset.data.birth_date);
const now = new Date();
const ageMonths = Math.floor(
(now.getTime() - birthDate.getTime()) / (1000 * 60 * 60 * 24 * 30)
);
asset.data.age_months = ageMonths;
}
// Process based on subcategory
switch (asset.subcategory) {
case 'cattle':
asset = this.processCattle(asset);
break;
case 'pigs':
asset = this.processPigs(asset);
break;
case 'chickens':
asset = this.processChickens(asset);
break;
}
// Add processing timestamp
asset.data.last_processed = new Date().toISOString();
return asset;
}
private validateCattle(asset: Asset): ValidationError[] {
const errors: ValidationError[] = [];
// Cattle-specific validations
if (asset.data.breed) {
const validBreeds = ['angus', 'hereford', 'nelore', 'brahman', 'simmental', 'limousin', 'charolais', 'other'];
if (!validBreeds.includes(asset.data.breed.toLowerCase())) {
errors.push({
field: 'data.breed',
message: `Invalid cattle breed. Common breeds: ${validBreeds.join(', ')}`,
code: 'INVALID_BREED'
});
}
}
// Validate ear tag format if present
if (asset.data.ear_tag) {
const earTagPattern = /^[A-Z0-9]{3,20}$/;
if (!earTagPattern.test(asset.data.ear_tag)) {
errors.push({
field: 'data.ear_tag',
message: 'Ear tag must be 3-20 alphanumeric characters',
code: 'INVALID_EAR_TAG'
});
}
}
return errors;
}
private validatePigs(asset: Asset): ValidationError[] {
const errors: ValidationError[] = [];
// Pig-specific validations
if (asset.data.production_type) {
const validTypes = ['breeding', 'finishing', 'nursery', 'farrow-to-finish'];
if (!validTypes.includes(asset.data.production_type)) {
errors.push({
field: 'data.production_type',
message: `Invalid production type. Must be one of: ${validTypes.join(', ')}`,
code: 'INVALID_PRODUCTION_TYPE'
});
}
}
return errors;
}
private validateChickens(asset: Asset): ValidationError[] {
const errors: ValidationError[] = [];
// Chicken-specific validations
if (asset.data.production_type) {
const validTypes = ['broiler', 'layer', 'breeder', 'free-range'];
if (!validTypes.includes(asset.data.production_type)) {
errors.push({
field: 'data.production_type',
message: `Invalid production type. Must be one of: ${validTypes.join(', ')}`,
code: 'INVALID_PRODUCTION_TYPE'
});
}
}
// Validate flock size if present
const flockSizeError = this.validateNumericField(asset, 'flock_size', 1, 1000000);
if (flockSizeError) errors.push(flockSizeError);
return errors;
}
private processCattle(asset: Asset): Asset {
// Auto-categorize based on age and gender if not specified
if (!asset.data.category && asset.data.age_months !== undefined) {
if (asset.data.age_months < 12) {
asset.data.category = 'calf';
} else if (asset.data.gender === 'male' && asset.data.age_months < 24) {
asset.data.category = 'steer';
} else if (asset.data.gender === 'male') {
asset.data.category = 'bull';
} else if (asset.data.gender === 'female' && asset.data.age_months >= 24) {
asset.data.category = 'cow';
} else {
asset.data.category = 'heifer';
}
}
return asset;
}
private processPigs(asset: Asset): Asset {
// Auto-calculate market readiness for finishing pigs
if (asset.data.production_type === 'finishing' && asset.data.weight_kg) {
asset.data.market_ready = asset.data.weight_kg >= 100 && asset.data.weight_kg <= 130;
}
return asset;
}
private processChickens(asset: Asset): Asset {
// Calculate production metrics for layers
if (asset.data.production_type === 'layer' && asset.data.eggs_per_day && asset.data.flock_size) {
asset.data.laying_percentage = (asset.data.eggs_per_day / asset.data.flock_size) * 100;
}
return asset;
}
// Optional: Override hooks for livestock-specific needs
async beforeProcess(asset: Asset): Promise<Asset> {
// Example: Normalize breed names
if (asset.data.breed) {
asset.data.breed = asset.data.breed.toLowerCase().trim();
}
return asset;
}
async afterProcess(asset: Asset): Promise<Asset> {
// Example: Add quality score based on various factors
let qualityScore = 100;
if (!asset.data.vaccination_status || asset.data.vaccination_status === 'unknown') {
qualityScore -= 20;
}
if (asset.data.health_status !== 'healthy') {
qualityScore -= 30;
}
if (!asset.data.ear_tag && !asset.data.rfid) {
qualityScore -= 10;
}
asset.data.quality_score = Math.max(0, qualityScore);
return asset;
}
}