2025-10-16 10:44:49 -06:00

282 lines
9.0 KiB
JavaScript

const representAPI = require('../services/represent-api');
const nocoDB = require('../services/nocodb');
// Helper function to cache representatives
async function cacheRepresentatives(postalCode, representatives, representData) {
try {
// Cache the postal code info
await nocoDB.storePostalCodeInfo({
postal_code: postalCode,
city: representData.city,
province: representData.province
});
// Cache representatives using the existing method
const result = await nocoDB.storeRepresentatives(postalCode, representatives);
if (result.success) {
console.log(`Successfully cached ${result.count} representatives for ${postalCode}`);
} else {
console.log(`Partial success caching representatives for ${postalCode}: ${result.error || 'unknown error'}`);
}
} catch (error) {
console.log(`Failed to cache representatives for ${postalCode}:`, error.message);
// Don't throw - caching is optional and should never break the main flow
}
}
class RepresentativesController {
async testConnection(req, res, next) {
try {
const result = await representAPI.testConnection();
res.json(result);
} catch (error) {
console.error('Test connection error:', error);
res.status(500).json({
error: 'Failed to test connection',
message: error.message
});
}
}
async getByPostalCode(req, res, next) {
try {
const { postalCode } = req.params;
const formattedPostalCode = postalCode.replace(/\s/g, '').toUpperCase();
// Try to check cached data first, but don't fail if NocoDB is down
let cachedData = [];
try {
cachedData = await nocoDB.getRepresentativesByPostalCode(formattedPostalCode);
console.log(`Cache check for ${formattedPostalCode}: found ${cachedData.length} records`);
if (cachedData && cachedData.length > 0) {
return res.json({
success: true,
source: 'Local Cache',
data: {
postalCode: formattedPostalCode,
location: {
city: cachedData[0]?.city || 'Alberta',
province: 'AB'
},
representatives: cachedData
}
});
}
} catch (cacheError) {
console.log(`Cache unavailable for ${formattedPostalCode}, proceeding with API call:`, cacheError.message);
}
// If not in cache, fetch from Represent API
console.log(`Fetching representatives from Represent API for ${postalCode}`);
const representData = await representAPI.getRepresentativesByPostalCode(postalCode);
if (!representData) {
return res.json({
success: false,
message: 'No data found for this postal code',
data: {
postalCode,
location: null,
representatives: []
}
});
}
// Process representatives from both concordance and centroid
let representatives = [];
// Add concordance representatives (if any)
if (representData.boundaries_concordance && representData.boundaries_concordance.length > 0) {
representatives = representatives.concat(representData.boundaries_concordance);
}
// Add centroid representatives (if any) - these are the actual elected officials
if (representData.representatives_centroid && representData.representatives_centroid.length > 0) {
representatives = representatives.concat(representData.representatives_centroid);
}
// Representatives already include office information, no need for additional API calls
console.log('Using representatives data with existing office information');
console.log(`Representatives concordance count: ${representData.boundaries_concordance ? representData.boundaries_concordance.length : 0}`);
console.log(`Representatives centroid count: ${representData.representatives_centroid ? representData.representatives_centroid.length : 0}`);
console.log(`Total representatives found: ${representatives.length}`);
if (representatives.length === 0) {
return res.json({
success: false,
message: 'No representatives found for this postal code',
data: {
postalCode,
location: {
city: representData.city,
province: representData.province
},
representatives: []
}
});
}
// Try to cache the results (will fail gracefully if NocoDB is down)
console.log(`Attempting to cache ${representatives.length} representatives for ${postalCode}`);
await cacheRepresentatives(postalCode, representatives, representData);
res.json({
success: true,
source: 'Open North',
data: {
postalCode,
location: {
city: representData.city,
province: representData.province
},
representatives
}
});
} catch (error) {
console.error('Get representatives error:', error);
res.status(500).json({
error: 'Failed to fetch representatives',
message: error.message
});
}
}
async refreshPostalCode(req, res, next) {
try {
const { postalCode } = req.params;
const formattedPostalCode = postalCode.replace(/\s/g, '').toUpperCase();
// Clear cached data
await nocoDB.clearRepresentativesByPostalCode(formattedPostalCode);
// Fetch fresh data from API
const representData = await representAPI.getRepresentativesByPostalCode(formattedPostalCode);
if (!representData || !representData.representatives_concordance) {
return res.status(404).json({
error: 'No representatives found for this postal code',
postalCode: formattedPostalCode
});
}
// Cache the fresh results
await nocoDB.storeRepresentatives(formattedPostalCode, representData.representatives_concordance);
res.json({
source: 'Open North',
postalCode: formattedPostalCode,
representatives: representData.representatives_concordance,
city: representData.city,
province: representData.province
});
} catch (error) {
console.error('Refresh representatives error:', error);
res.status(500).json({
error: 'Failed to refresh representatives',
message: error.message
});
}
}
async trackCall(req, res, next) {
try {
const {
representativeName,
representativeTitle,
phoneNumber,
officeType,
userEmail,
userName,
postalCode
} = req.body;
// Validate required fields
if (!representativeName || !phoneNumber) {
return res.status(400).json({
success: false,
error: 'Representative name and phone number are required'
});
}
// Log the call
await nocoDB.logCall({
representativeName,
representativeTitle: representativeTitle || null,
phoneNumber,
officeType: officeType || null,
callerName: userName || null,
callerEmail: userEmail || null,
postalCode: postalCode || null,
campaignId: null,
campaignSlug: null,
callerIP: req.ip || req.connection?.remoteAddress || null,
timestamp: new Date().toISOString()
});
res.json({
success: true,
message: 'Call tracked successfully'
});
} catch (error) {
console.error('Track call error:', error);
res.status(500).json({
success: false,
error: 'Failed to track call',
message: error.message
});
}
}
async geocodeAddress(req, res, next) {
try {
const { address } = req.body;
const axios = require('axios');
console.log(`Geocoding address: ${address}`);
// Use Nominatim API (OpenStreetMap)
const encodedAddress = encodeURIComponent(address);
const url = `https://nominatim.openstreetmap.org/search?format=json&q=${encodedAddress}&limit=1&countrycodes=ca`;
const response = await axios.get(url, {
headers: {
'User-Agent': 'BNKops-Influence-Tool/1.0'
},
timeout: 5000
});
if (response.data && response.data.length > 0 && response.data[0].lat && response.data[0].lon) {
const result = {
lat: parseFloat(response.data[0].lat),
lng: parseFloat(response.data[0].lon),
display_name: response.data[0].display_name
};
console.log(`Geocoded "${address}" to:`, result);
res.json({
success: true,
data: result
});
} else {
console.log(`No geocoding results for: ${address}`);
res.json({
success: false,
message: 'No results found for this address'
});
}
} catch (error) {
console.error('Geocoding error:', error);
res.status(500).json({
success: false,
error: 'Geocoding failed',
message: error.message
});
}
}
}
module.exports = new RepresentativesController();