@lineai/municipal-intel
Version:
AI-first municipal data API providing natural language descriptions of building permits and planning applications from major US cities
333 lines • 26.6 kB
JavaScript
"use strict";
/**
* Socrata API client for municipal data
* Used by San Francisco, NYC, Oakland, Sacramento
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SocrataClient = void 0;
const axios_1 = __importDefault(require("axios"));
const base_client_1 = require("../base-client");
/**
* Socrata API client
*/
class SocrataClient extends base_client_1.BaseMunicipalClient {
constructor(config, params) {
super(config);
this.resetTime = Date.now() + 60000; // 1 minute from now
if (!this.source.api || this.source.api.type !== 'socrata') {
throw new Error('SocrataClient requires a source with api.type = "socrata"');
}
this.appToken = config.appToken;
this.params = params;
// const apiSource = this.source.api as ApiSource;
this.datasetConfig = this.source.api.datasets[params.datasetId || this.source.api.defaultDataset];
// Create axios instance
this.api = axios_1.default.create({
baseURL: this.source.api.baseUrl,
timeout: this.timeout,
headers: Object.assign({ 'User-Agent': config.userAgent || 'municipal-intel/0.1.0' }, (this.appToken && { 'X-App-Token': this.appToken }))
});
// Add response interceptor for error handling
this.api.interceptors.response.use((response) => {
return response;
}, (error) => {
var _a, _b, _c;
if (((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) === 429) {
const resetTime = this.resetTime;
throw new base_client_1.RateLimitError(this.source.id, new Date(resetTime));
}
throw new base_client_1.MunicipalDataError(error.message, this.source.id, (_b = error.response) === null || _b === void 0 ? void 0 : _b.status, (_c = error.response) === null || _c === void 0 ? void 0 : _c.data);
});
}
/**
* Execute a SoQL query against a dataset
*/
async query(sq = {}) {
const response = await this.api.get(this.datasetConfig.endpoint, {
params: this.cleanParams(sq)
});
this.log(`Retrieved ${response.data.length} records`);
return response.data;
}
/**
* Search for municipal projects
*/
async search() {
var _a;
const adjustments = [];
const soqlQuery = this.buildSoQLQuery(adjustments);
const data = await this.query(soqlQuery);
const projects = data.map(item => this.normalizeProject(item));
// Get total count if needed
let total = projects.length;
if (this.params.limit && projects.length === this.params.limit) {
const countQuery = Object.assign(Object.assign({}, soqlQuery), { $select: 'count(*) as total', $limit: 1, $order: undefined });
const countResult = await this.query(countQuery);
total = parseInt(((_a = countResult[0]) === null || _a === void 0 ? void 0 : _a.total) || '0');
}
return {
projects,
total,
page: Math.floor((this.params.offset || 0) / (this.params.limit || 100)) + 1,
pageSize: this.params.limit || 100,
hasMore: total > (this.params.offset || 0) + projects.length,
adjustments
};
}
/**
* Get a project by its URL
*/
async getByUrl(url) {
try {
// Extract the ID from the URL
const id = this.extractIdFromUrl(url);
if (!id) {
return null;
}
return this.getProject(id);
}
catch (error) {
console.warn(`Error getting project by URL ${url}: ${error}`);
return null;
}
}
/**
* Extract project ID from a municipal-intel URL
*/
extractIdFromUrl(url) {
try {
const urlObj = new URL(url);
// Expected format: /projects/{sourceId}/{datasetId}/{projectId}
const pathParts = urlObj.pathname.split('/');
if (pathParts.length >= 5 && pathParts[1] === 'projects') {
return pathParts[4]; // Return the project ID part
}
return null;
}
catch (error) {
return null;
}
}
/**
* Get a specific project by ID
*/
async getProject(id) {
const idField = this.getIdField();
if (!idField) {
throw new base_client_1.MunicipalDataError(`Missing field mapping for 'id' in source ${this.source.id}. Please add to fieldMappings in registry.`, this.source.id);
}
const query = {
$where: `${idField} = '${id.replace(`${this.source.id}-`, '')}'`,
$limit: 1
};
const data = await this.query(query);
return data.length > 0 ? this.normalizeProject(data[0]) : null;
}
/**
* Get available project types
*/
async getAvailableTypes() {
const typeField = this.getTypeField();
if (!typeField)
return [];
const query = {
$select: `distinct ${typeField}`,
$limit: 1000
};
const data = await this.query(query);
return data.map(item => item[typeField]).filter(Boolean);
}
/**
* Check if the data source is healthy
*/
async healthCheck() {
const startTime = Date.now();
try {
// Simple health check - try to fetch one record
const query = { $limit: 1 };
await this.query(query);
const latency = Date.now() - startTime;
return {
status: 'healthy',
latency,
lastChecked: new Date()
};
}
catch (error) {
const latency = Date.now() - startTime;
return {
status: 'unhealthy',
latency,
error: error.message,
lastChecked: new Date()
};
}
}
/**
* Build SoQL query from search parameters
*/
buildSoQLQuery(adjustments = []) {
const params = this.params;
const query = {
$limit: params.limit || 100,
$offset: this.params.offset || 0,
$order: this.buildOrderClause(params)
};
const whereConditions = [];
// Date filters
if (params.submitDateFrom) {
const field = this.getDateField('submit');
if (field) {
this.validateDateParameter(params.submitDateFrom, 'submitDateFrom');
// Socrata doesn't like Z timezone indicator, remove it
const dateString = params.submitDateFrom.toISOString().replace('Z', '');
whereConditions.push(`${field} >= '${dateString}'`);
}
}
if (params.submitDateTo) {
const field = this.getDateField('submit');
if (field) {
this.validateDateParameter(params.submitDateTo, 'submitDateTo');
// Socrata doesn't like Z timezone indicator, remove it
const dateString = params.submitDateTo.toISOString().replace('Z', '');
whereConditions.push(`${field} <= '${dateString}'`);
}
}
// Value filters
if (params.minValue) {
const field = this.getValueField();
if (field) {
// All Socrata sources store numeric values as text strings, requires casting
whereConditions.push(`${field}::number >= ${params.minValue}`);
}
else {
// No value field available - skip filter and record adjustment
adjustments.push(`${this.source.id.toUpperCase()}: Skipped minValue filter - no value field available in dataset`);
}
}
// Status filters
if (params.statuses && params.statuses.length > 0) {
const field = this.getStatusField();
if (field) {
const statusList = params.statuses.map(s => `'${s}'`).join(',');
whereConditions.push(`${field} in (${statusList})`);
}
}
// Address filters
if (params.addresses && params.addresses.length > 0) {
const field = this.getAddressField();
if (field) {
const addressConditions = params.addresses.map(addr => `upper(${field}) like upper('%${addr}%')`);
whereConditions.push(`(${addressConditions.join(' OR ')})`);
}
}
// Keywords
if (params.keywords && params.keywords.length > 0) {
const searchText = params.keywords.join(' ');
query.$q = searchText;
}
if (whereConditions.length > 0) {
query.$where = whereConditions.join(' AND ');
}
return query;
}
/**
* Clean query parameters (remove undefined values)
*/
cleanParams(sq) {
const cleaned = {};
for (const [key, value] of Object.entries(sq)) {
if (value !== undefined && value !== null) {
cleaned[key] = value;
}
}
return cleaned;
}
/**
* Build ORDER BY clause
*/
buildOrderClause(params) {
const sortBy = params.sortBy || 'submitDate';
const order = params.sortOrder || 'desc';
const field = this.getFieldMapping(sortBy);
if (!field) {
// If field mapping not available, try to use a default field or skip ordering
return `:created_at ${order}`; // Most Socrata datasets have this system field
}
return `${field} ${order}`;
}
/**
* Get field mapping for this source (returns null if missing)
*/
getFieldMapping(logicalField) {
var _a;
const mappings = (_a = this.datasetConfig) === null || _a === void 0 ? void 0 : _a.fieldMappings;
return (mappings === null || mappings === void 0 ? void 0 : mappings[logicalField]) || null;
}
// Placeholder methods - would be implemented per source
getIdField() { return this.getFieldMapping('id'); }
getTypeField() { return this.getFieldMapping('title'); }
getDateField(type) {
return type === 'submit'
? this.getFieldMapping('submitDate')
: this.getFieldMapping('approvalDate');
}
getValueField() {
return this.getFieldMapping('value');
}
getStatusField() { return this.getFieldMapping('status'); }
getAddressField() { return this.getFieldMapping('address'); }
/**
* Normalize raw data to MunicipalProject using dataset-specific description
*/
normalizeProject(data) {
var _a;
// Get ID field for unique identifier
const idField = this.getFieldMapping('id');
const id = idField ? data[idField] || 'unknown' : 'unknown';
// Use dataset-specific description method
let description = 'Municipal Project';
if ((_a = this.datasetConfig) === null || _a === void 0 ? void 0 : _a.getDescription) {
try {
description = this.datasetConfig.getDescription(data);
}
catch (error) {
console.warn(`Error generating description for ${this.source.id}: ${error}`);
description = `${this.source.name} Record`;
}
}
return {
id: `${this.source.id}-${id}`,
source: this.source.id,
description,
url: this.generateProjectUrl(id),
rawData: data,
lastUpdated: new Date()
};
}
/**
* Generate a project URL for accessing full details
*/
generateProjectUrl(id) {
var _a;
const datasetId = this.params.datasetId || ((_a = this.source.api) === null || _a === void 0 ? void 0 : _a.defaultDataset) || 'default';
return `https://municipal-intel.lineai.com/projects/${this.source.id}/${datasetId}/${id}`;
}
/**
* Validate that date parameter is a proper Date object
*/
validateDateParameter(dateParam, paramName) {
if (!(dateParam instanceof Date)) {
const actualType = Array.isArray(dateParam) ? 'array' : typeof dateParam;
throw new base_client_1.MunicipalDataError(`Invalid ${paramName}: expected Date object, got ${actualType}. Use: new Date('2024-01-01') or new Date()`, this.source.id);
}
if (isNaN(dateParam.getTime())) {
throw new base_client_1.MunicipalDataError(`Invalid ${paramName}: Date object contains invalid date. Check your date values.`, this.source.id);
}
}
}
exports.SocrataClient = SocrataClient;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2NsaWVudHMvc29jcmF0YS9jbGllbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7R0FHRzs7Ozs7O0FBRUgsa0RBQTREO0FBSTVELGdEQUF3SDtBQXNCeEg7O0dBRUc7QUFDSCxNQUFhLGFBQWMsU0FBUSxpQ0FBbUI7SUFPcEQsWUFBWSxNQUEyQixFQUFFLE1BQTZCO1FBQ3BFLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUxDLGNBQVMsR0FBVyxJQUFJLENBQUMsR0FBRyxFQUFFLEdBQUcsS0FBSyxDQUFDLENBQUMsb0JBQW9CO1FBTzNFLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLEdBQUcsSUFBSSxJQUFJLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxJQUFJLEtBQUssU0FBUyxFQUFFO1lBQzFELE1BQU0sSUFBSSxLQUFLLENBQUMsMkRBQTJELENBQUMsQ0FBQztTQUM5RTtRQUVELElBQUksQ0FBQyxRQUFRLEdBQUcsTUFBTSxDQUFDLFFBQVEsQ0FBQztRQUNoQyxJQUFJLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQztRQUNyQixrREFBa0Q7UUFDbEQsSUFBSSxDQUFDLGFBQWEsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLFNBQVMsSUFBSSxJQUFJLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxjQUFjLENBQUMsQ0FBQztRQUdsRyx3QkFBd0I7UUFDeEIsSUFBSSxDQUFDLEdBQUcsR0FBRyxlQUFLLENBQUMsTUFBTSxDQUFDO1lBQ3RCLE9BQU8sRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxPQUFPO1lBQ2hDLE9BQU8sRUFBRSxJQUFJLENBQUMsT0FBTztZQUNyQixPQUFPLGtCQUNMLFlBQVksRUFBRSxNQUFNLENBQUMsU0FBUyxJQUFJLHVCQUF1QixJQUN0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