← back to Rentv Adintel
lib/classification.js
93 lines
'use strict';
/**
* Classification helpers (spec §2, §6.16, §6.17).
*
* Pure functions — no database I/O. Import from lib/types (single vocabulary).
*/
const { RELATIONSHIP_STATUS, VERIFIED_STATUSES, STATUS_LABELS, normalizeName } = require('./types');
/** Returns true if the given status is one of the VERIFIED_* statuses. */
function isVerifiedStatus(status) {
return VERIFIED_STATUSES.includes(status);
}
/**
* Guard: prevents promoting a SPEAKER_OR_PANELIST_ONLY to any VERIFIED_*SPONSOR
* or VERIFIED_ADVERTISER status without explicit sponsor evidence (§6.16, §6.17).
*
* @param {string} fromStatus - current relationship status
* @param {string} toStatus - proposed new status
* @param {boolean} hasSponsorEvidence - caller must have verified separate sponsor evidence
* @throws {Error} if the promotion is prohibited
*/
function assertNotPanelistMislabeledAsSponsor(fromStatus, toStatus, hasSponsorEvidence) {
if (fromStatus !== 'SPEAKER_OR_PANELIST_ONLY') return; // only applies to panelists
const SPONSOR_OR_ADVERTISER_STATUSES = [
'VERIFIED_ADVERTISER',
'VERIFIED_CONFERENCE_SPONSOR',
'VERIFIED_EXHIBITOR',
'VERIFIED_MEDIA_PARTNER',
];
if (!SPONSOR_OR_ADVERTISER_STATUSES.includes(toStatus)) return; // not a protected promotion
if (!hasSponsorEvidence) {
throw new Error(
`Classification error: cannot promote "${fromStatus}" to "${toStatus}" without separate, ` +
`explicit sponsor/advertiser evidence. Add a dated evidence record that directly proves ` +
`the paid relationship before changing this status. (spec §6.16, §6.17)`
);
}
}
/**
* Return the appropriate default relationship status for a given intake signal.
*
* @param {'AD' | 'EMAIL_AD' | 'CONFERENCE_SPONSOR' | 'PANELIST' | 'CONTENT_PRESENTATION' | 'PROSPECT' | 'UNKNOWN'} signal
* @returns {string} one of RELATIONSHIP_STATUS values
*/
function defaultStatusForSignal(signal) {
switch (signal) {
case 'AD': return 'VERIFIED_ADVERTISER';
case 'EMAIL_AD': return 'VERIFIED_ADVERTISER';
case 'CONFERENCE_SPONSOR': return 'VERIFIED_CONFERENCE_SPONSOR';
case 'EXHIBITOR': return 'VERIFIED_EXHIBITOR';
case 'MEDIA_PARTNER': return 'VERIFIED_MEDIA_PARTNER';
case 'CONTENT_PRESENTATION': return 'VERIFIED_CONTENT_PARTNER';
case 'PANELIST': return 'SPEAKER_OR_PANELIST_ONLY';
case 'SPEAKER': return 'SPEAKER_OR_PANELIST_ONLY';
case 'PROSPECT': return 'LIKELY_PROSPECT';
case 'PAST': return 'PAST_ADVERTISER';
case 'UNKNOWN': return 'RESEARCH_NEEDED';
default: return 'RESEARCH_NEEDED';
}
}
/**
* Return the plain-English Simple View label for a status (spec §25).
* @param {string} status
* @returns {string}
*/
function statusLabel(status) {
return STATUS_LABELS[status] || status;
}
/**
* Validate that a given status string is in the canonical vocabulary.
* @param {string} status
* @returns {boolean}
*/
function isKnownStatus(status) {
return RELATIONSHIP_STATUS.includes(status);
}
module.exports = {
isVerifiedStatus,
assertNotPanelistMislabeledAsSponsor,
defaultStatusForSignal,
statusLabel,
isKnownStatus,
};