package com.lincfusion.udyamlinc.features; import jakarta.persistence.*; import lombok.*; import java.time.LocalDateTime; /** * The feature catalogue. A row here is the contract for one engineered * indicator: what it means, what unit it carries, and how old it is allowed to * be before anything downstream may act on it. * *

{@code stalenessToleranceDays} is the column trip-wires read. A rule firing * on a three-week-old price is worse than no rule, and the tolerance is per * feature because a mandi price goes stale in days while a CBS extract is * monthly by design. */ @Entity @Table(name = "feature_definition", indexes = @Index(name = "ix_feature_def_code", columnList = "code", unique = true)) @Getter @Setter @NoArgsConstructor @AllArgsConstructor @Builder public class FeatureDefinition { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; /** snake_case, matching the name a trip-wire expression uses. */ @Column(nullable = false, unique = true, length = 48) private String code; @Column(nullable = false, length = 96) private String label; /** LIQUIDITY | BEHAVIOURAL | TREND_VOLATILITY | OBLIGATION_LOAD | EXTERNAL_SHOCK | QUALITY */ @Column(nullable = false, length = 32) private String family; /** RATIO | PCT | DAYS | INDEX */ @Column(length = 16) private String unit; /** Which upstream table it derives from — AGMARKNET, CLIMATE, LEDGER, CBS, UPI. */ @Column(length = 32) private String sourceKind; /** Beyond this age the value must not be acted on. Null means it never goes stale. */ private Integer stalenessToleranceDays; /** HIGHER_IS_WORSE | LOWER_IS_WORSE */ @Column(length = 24) private String direction; @Column(length = 512) private String derivation; private Boolean active; private LocalDateTime updatedAt; @PrePersist @PreUpdate void stamp() { if (active == null) active = Boolean.TRUE; updatedAt = LocalDateTime.now(); } }