@lineai/gov-deals
Version:
Explore Federal contracts for government building renovations, city hall renovations, courthouse updates, library modernizations, federal building improvement contracts, base housing and facilities upgrades.
128 lines • 9.23 kB
JavaScript
;
/**
* CSV-based data source for opportunities
* Provides same interface as SAM.gov API but reads from local CSV file
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.CsvClient = void 0;
const fs_1 = require("fs");
const sync_1 = require("csv-parse/sync");
const field_mapping_1 = require("./field-mapping");
const opportunities_1 = require("../../types/opportunities");
const errors_1 = require("../../core/errors");
/**
* CSV-based client that mimics SAM.gov API
*/
class CsvClient {
constructor(options) {
var _a, _b;
this.cache = null;
if (!options.csvPath) {
throw new Error('CSV file path is required');
}
this.csvPath = options.csvPath;
this.cacheResults = (_a = options.cacheResults) !== null && _a !== void 0 ? _a : true;
this.encoding = (_b = options.encoding) !== null && _b !== void 0 ? _b : 'utf-8';
}
/**
* Load and parse CSV data
*/
async loadData() {
// Return cached data if available
if (this.cache && this.cacheResults) {
return this.cache;
}
try {
// Read CSV file
const csvContent = await fs_1.promises.readFile(this.csvPath, this.encoding);
// Parse CSV with proper options
const records = (0, sync_1.parse)(csvContent, {
columns: true,
skip_empty_lines: true,
relax_quotes: true,
relax_column_count: true,
skip_records_with_error: true, // Skip problematic rows
});
// Cache if enabled
if (this.cacheResults) {
this.cache = records;
}
return records;
}
catch (error) {
throw new Error(`Failed to load CSV file: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Search opportunities with filtering and pagination
*/
async search(filters, pagination) {
// Load all data
const allRecords = await this.loadData();
// Apply filters
let filteredRecords = allRecords;
if (filters) {
filteredRecords = allRecords.filter(row => (0, field_mapping_1.filterCsvRow)(row, filters));
}
// Calculate pagination
const limit = (pagination === null || pagination === void 0 ? void 0 : pagination.limit) || 100;
const page = (pagination === null || pagination === void 0 ? void 0 : pagination.page) || 1;
const offset = (page - 1) * limit;
// Get paginated results
const paginatedRecords = filteredRecords.slice(offset, offset + limit);
// Transform to SAM format
const opportunities = paginatedRecords.map(field_mapping_1.mapCsvToSamOpportunity);
// Build response
const response = {
totalRecords: filteredRecords.length,
limit,
offset,
opportunitiesData: opportunities,
links: [], // CSV doesn't have links
};
// Validate response format
const result = opportunities_1.SamOpportunitySearchResponseSchema.safeParse(response);
if (!result.success) {
console.error('CSV mapping validation errors:', result.error.errors);
// Continue anyway - the data is still useful even if not perfectly typed
}
return response;
}
/**
* Get opportunity by ID
*/
async getById(noticeId) {
const allRecords = await this.loadData();
const record = allRecords.find(row => row.NoticeId === noticeId);
if (!record) {
throw new errors_1.ValidationError(`Opportunity with ID ${noticeId} not found`);
}
return (0, field_mapping_1.mapCsvToSamOpportunity)(record);
}
/**
* Get opportunity description (from CSV Description field)
*/
async getDescription(noticeId) {
const allRecords = await this.loadData();
const record = allRecords.find(row => row.NoticeId === noticeId);
if (!record) {
throw new errors_1.ValidationError(`Opportunity with ID ${noticeId} not found`);
}
return record.Description || 'No description available';
}
/**
* Search for construction opportunities
*/
async searchConstruction(additionalFilters, pagination) {
const constructionFilters = Object.assign({ naicsCodes: ['236', '238'], keywords: 'renovation construction building modernization' }, additionalFilters);
return this.search(constructionFilters, pagination);
}
/**
* Clear cache if needed
*/
clearCache() {
this.cache = null;
}
}
exports.CsvClient = CsvClient;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2RhdGFzb3VyY2VzL2Nzdi9jbGllbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7R0FHRzs7O0FBRUgsMkJBQW9DO0FBQ3BDLHlDQUF1QztBQUN2QyxtREFJeUI7QUFDekIsNkRBS21DO0FBRW5DLDhDQUFvRDtBQVFwRDs7R0FFRztBQUNILE1BQWEsU0FBUztJQU1wQixZQUFZLE9BQXlCOztRQUo3QixVQUFLLEdBQStCLElBQUksQ0FBQztRQUsvQyxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sRUFBRTtZQUNwQixNQUFNLElBQUksS0FBSyxDQUFDLDJCQUEyQixDQUFDLENBQUM7U0FDOUM7UUFFRCxJQUFJLENBQUMsT0FBTyxHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUM7UUFDL0IsSUFBSSxDQUFDLFlBQVksR0FBRyxNQUFBLE9BQU8sQ0FBQyxZQUFZLG1DQUFJLElBQUksQ0FBQztRQUNqRCxJQUFJLENBQUMsUUFBUSxHQUFHLE1BQUEsT0FBTyxDQUFDLFFBQVEsbUNBQUksT0FBTyxDQUFDO0lBQzlDLENBQUM7SUFFRDs7T0FFRztJQUNLLEtBQUssQ0FBQyxRQUFRO1FBQ3BCLGtDQUFrQztRQUNsQyxJQUFJLElBQUksQ0FBQyxLQUFLLElBQUksSUFBSSxDQUFDLFlBQVksRUFBRTtZQUNuQyxPQUFPLElBQUksQ0FBQyxLQUFLLENBQUM7U0FDbkI7UUFFRCxJQUFJO1lBQ0YsZ0JBQWdCO1lBQ2hCLE1BQU0sVUFBVSxHQUFHLE1BQU0sYUFBRSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQztZQUVsRSxnQ0FBZ0M7WUFDaEMsTUFBTSxPQUFPLEdBQUcsSUFBQSxZQUFLLEVBQUMsVUFBVSxFQUFFO2dCQUNoQyxPQUFPLEVBQUUsSUFBSTtnQkFDYixnQkFBZ0IsRUFBRSxJQUFJO2dCQUN0QixZ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