Function bodies 338 total
demonstrateSizeAdjustments function · javascript · L4-L144 (141 LOC)functions/demo/demo-size-adjusted-pricing.js
function demonstrateSizeAdjustments() {
console.log("📊 CURRENT APPROACH (Simple Linear):");
console.log("ARV = Living Area × Price Per SqFt × Improvement Factor");
console.log("Problem: Doesn't account for size economies/premiums");
console.log();
console.log("🏠 EXAMPLE SCENARIO:");
const subjectArea = 2141;
const avgCompArea = 1728;
const pricePerSqft = 267;
const impFactor = 1.03;
console.log(`Subject: ${subjectArea} sqft`);
console.log(`Average Comp: ${avgCompArea} sqft`);
console.log(`Base Price/SqFt: $${pricePerSqft}`);
console.log();
// Current approach
const currentARV = subjectArea * pricePerSqft * impFactor;
console.log(`Current ARV: ${subjectArea} × $${pricePerSqft} × ${impFactor} = $${Math.round(currentARV).toLocaleString()}`);
console.log();
console.log("🔧 PROPOSED SIZE-ADJUSTED APPROACHES:");
console.log("━".repeat(50));
console.log("\n1️⃣ DIMINISHING RETURNS MODEL:");
console.log("Larger homes have slightly lower $/sqft due tcalculateDiminishingReturns function · javascript · L32-L43 (12 LOC)functions/demo/demo-size-adjusted-pricing.js
function calculateDiminishingReturns(area, basePrice, avgArea) {
// Size factor: larger homes get slight discount, smaller get premium
const sizeRatio = area / avgArea;
const sizeFactor = Math.pow(sizeRatio, 0.85); // Power < 1 = diminishing returns
const adjustedPrice = basePrice * sizeFactor;
return {
sizeFactor: sizeFactor,
adjustedPrice: Math.round(adjustedPrice),
arv: Math.round(area * adjustedPrice * impFactor)
};
}calculateTieredPricing function · javascript · L54-L68 (15 LOC)functions/demo/demo-size-adjusted-pricing.js
function calculateTieredPricing(area, basePrice) {
let adjustedPrice = basePrice;
if (area <= 1000) adjustedPrice = basePrice * 1.10; // Small homes: 10% premium
else if (area <= 1500) adjustedPrice = basePrice * 1.05; // Medium homes: 5% premium
else if (area <= 2000) adjustedPrice = basePrice * 1.00; // Large homes: base price
else if (area <= 2500) adjustedPrice = basePrice * 0.95; // XL homes: 5% discount
else adjustedPrice = basePrice * 0.90; // XXL homes: 10% discount
return {
tier: getTier(area),
adjustedPrice: Math.round(adjustedPrice),
arv: Math.round(area * adjustedPrice * impFactor)
};
}getTier function · javascript · L70-L76 (7 LOC)functions/demo/demo-size-adjusted-pricing.js
function getTier(area) {
if (area <= 1000) return "Small (≤1000 sqft)";
if (area <= 1500) return "Medium (1001-1500 sqft)";
if (area <= 2000) return "Large (1501-2000 sqft)";
if (area <= 2500) return "XL (2001-2500 sqft)";
return "XXL (>2500 sqft)";
}calculateMarketBasedAdjustment function · javascript · L87-L102 (16 LOC)functions/demo/demo-size-adjusted-pricing.js
function calculateMarketBasedAdjustment(area, basePrice, avgArea) {
// Simulate market data showing how price/sqft varies by size
const sizeDeviation = (area - avgArea) / avgArea;
// Market research shows: every 25% size increase = 2% price/sqft decrease
const priceAdjustment = -sizeDeviation * 0.08; // 8% adjustment per 100% size change
const sizeFactor = 1 + priceAdjustment;
return {
sizeDeviation: sizeDeviation,
priceAdjustment: priceAdjustment,
sizeFactor: sizeFactor,
adjustedPrice: Math.round(basePrice * sizeFactor),
arv: Math.round(area * basePrice * sizeFactor * impFactor)
};
}calculateSizeAdjustedPrice function · javascript · L129-L134 (6 LOC)functions/demo/demo-size-adjusted-pricing.js
function calculateSizeAdjustedPrice(livingArea, basePricePerSqft, avgCompArea) {
// Diminishing returns: power factor < 1.0
const sizeRatio = livingArea / avgCompArea;
const sizeFactor = Math.pow(sizeRatio, 0.85); // 0.85 = moderate adjustment
return Math.round(basePricePerSqft * sizeFactor);
}demoSizeAdjustment function · javascript · L6-L116 (111 LOC)functions/demo/demo-size-adjustment-working.js
async function demoSizeAdjustment() {
console.log("🎯 SIMULATING REAL SCENARIO:");
console.log("Large property (2,141 sqft) vs smaller average comps (1,728 sqft)");
console.log();
// Test property from the original issue
const testProperty = {
zpid: "demo_size",
price: 499000,
livingArea: 2141,
bedrooms: 5,
bathrooms: 3
};
// Create mock comparable data that matches the real structure
const mockFilteredComps = [
{
price: { value: 420000 },
sqFt: { value: 1600 },
beds: 3
},
{
price: { value: 450000 },
sqFt: { value: 1700 },
beds: 3
},
{
price: { value: 480000 },
sqFt: { value: 1800 },
beds: 4
},
{
price: { value: 510000 },
sqFt: { value: 1750 },
beds: 4
},
{
price: { value: 490000 },
sqFt: { value: 1820 },
beds: 4
}
];
const avgCompSize = mockFilteredComps.reduce((sum, c) => sum + c.sqFt.value, 0) / mockFilteredComps.Repobility · MCP-ready · https://repobility.com
explainAddOnARV function · javascript · L4-L116 (113 LOC)functions/explain/explain-addon-arv.js
function explainAddOnARV() {
console.log("🏠 ADD-ON STRATEGY OVERVIEW:");
console.log("Add-On adds a bedroom (typically 120 sqft) to existing property");
console.log("ARV = Current Property Value + Addition Value + Extra Value");
console.log();
console.log("🔢 ADD-ON ARV FORMULA:");
console.log("━".repeat(50));
console.log("1. Current Property Value = Existing Area × Market Price/SqFt × Improvement Factor");
console.log("2. Addition Value = Addition Area × Avg Comp Price/SqFt × Improvement Factor");
console.log("3. Extra Value = Bedroom premium (if applicable)");
console.log("4. Total ARV = Current Value + Addition Value + Extra Value");
console.log();
console.log("⚙️ ADD-ON CONFIGURATION:");
console.log("━".repeat(50));
console.log("• Addition Area: 120 sqft (bedroom size)");
console.log("• Improvement Factor: 1.0 (no renovation premium)");
console.log("• Duration: 6 months (default)");
console.log("• Extra Value: Usually $0, can be bedroom premium");explainModifiedZScore function · javascript · L4-L152 (149 LOC)functions/explain/explain-modified-zscore.js
function explainModifiedZScore() {
console.log("🧮 WHAT IS THE MODIFIED Z-SCORE TEST?");
console.log("━".repeat(50));
console.log("The Modified Z-Score test is a statistical method for detecting outliers");
console.log("that is MORE ROBUST than the traditional Z-Score because it uses:");
console.log("• MEDIAN instead of MEAN (less affected by outliers)");
console.log("• MAD instead of Standard Deviation (more resistant to extreme values)");
console.log();
console.log("📊 TRADITIONAL Z-SCORE vs MODIFIED Z-SCORE");
console.log("━".repeat(50));
console.log("Traditional Z-Score:");
console.log(" Z = (value - mean) / standard_deviation");
console.log(" ❌ Problem: Mean and StdDev are skewed by outliers");
console.log();
console.log("Modified Z-Score:");
console.log(" Modified Z = 0.6745 × (value - median) / MAD");
console.log(" ✅ Benefit: Median and MAD are outlier-resistant");
console.log();
console.log("🔢 STEP-BY-STEP CALCULATION");
console.log("━"createFluidCMProject function · javascript · L22-L62 (41 LOC)functions/fluidcmHandoff.js
async function createFluidCMProject(dealData, dealId) {
const apiUrl = process.env.FLUIDCM_API_URL;
const apiToken = process.env.FLUIDCM_API_TOKEN;
const orgId = parseInt(process.env.FLUIDCM_ORG_ID || "1", 10);
if (!apiUrl || !apiToken) {
console.warn("[FluidCM] FLUIDCM_API_URL or FLUIDCM_API_TOKEN not configured, skipping handoff");
return null;
}
// Build project code from address (e.g., "123 Main St" -> "RD-123-MAIN")
const address = dealData.address || dealData.streetAddress || "Unknown";
const projectCode = buildProjectCode(address);
// Build project payload
const payload = {
code: projectCode,
name: address,
organization_id: orgId,
description: buildDescription(dealData, dealId),
zip_code: dealData.zipcode || dealData.zip || undefined,
base_value: dealData.price || dealData.listPrice || undefined,
};
const response = await axios.post(
`${apiUrl}/api/v1/projects`,
payload,
{
headers: {
"AuthorizabuildProjectCode function · javascript · L68-L74 (7 LOC)functions/fluidcmHandoff.js
function buildProjectCode(address) {
const parts = address.split(/[\s,]+/).filter(Boolean);
const number = parts.find((p) => /^\d+$/.test(p)) || "";
const street = parts.find((p) => /^[A-Za-z]{3,}$/.test(p) && !isStopWord(p)) || "";
const code = `RD-${number}-${street}`.toUpperCase().slice(0, 15);
return code || `RD-${Date.now().toString(36).toUpperCase()}`;
}isStopWord function · javascript · L76-L80 (5 LOC)functions/fluidcmHandoff.js
function isStopWord(word) {
return ["the", "and", "ave", "street", "drive", "road", "lane", "way", "blvd", "court"].includes(
word.toLowerCase(),
);
}buildDescription function · javascript · L85-L92 (8 LOC)functions/fluidcmHandoff.js
function buildDescription(dealData, dealId) {
const lines = [`Imported from RealDeal deal ${dealId}`];
if (dealData.method) lines.push(`Strategy: ${dealData.method}`);
if (dealData.price) lines.push(`List price: $${Number(dealData.price).toLocaleString()}`);
if (dealData.score) lines.push(`Deal score: ${dealData.score}`);
if (dealData.recommendation) lines.push(`Recommendation: ${dealData.recommendation}`);
return lines.join("\n");
}initializeGA4Client function · javascript · L18-L28 (11 LOC)functions/ga4Service.js
function initializeGA4Client(keyFilePath) {
if (analyticsDataClient) {
return analyticsDataClient;
}
analyticsDataClient = new BetaAnalyticsDataClient({
keyFilename: keyFilePath,
});
return analyticsDataClient;
}fetchLandingPageEvents function · javascript · L37-L97 (61 LOC)functions/ga4Service.js
async function fetchLandingPageEvents(propertyId, startDate = 'yesterday', endDate = 'yesterday') {
if (!analyticsDataClient) {
throw new Error('GA4 client not initialized. Call initializeGA4Client() first.');
}
try {
const [response] = await analyticsDataClient.runReport({
property: `properties/${propertyId}`,
dateRanges: [
{
startDate,
endDate,
},
],
dimensions: [
{ name: 'eventName' }, // Event type (page_view, user_engagement, etc.)
{ name: 'date' }, // Date (YYYYMMDD format)
{ name: 'pagePath' }, // URL path
{ name: 'sessionSource' }, // Traffic source (google, facebook, direct, etc.)
{ name: 'sessionMedium' }, // Traffic medium (organic, cpc, referral, etc.)
],
metrics: [
{ name: 'eventCount' }, // Number of events
{ name: 'activeUsers' }, // Number of unique users
{ name: 'averageSRepobility · code-quality intelligence · https://repobility.com
fetchConversionEvents function · javascript · L106-L147 (42 LOC)functions/ga4Service.js
async function fetchConversionEvents(propertyId, startDate = 'yesterday', endDate = 'yesterday') {
if (!analyticsDataClient) {
throw new Error('GA4 client not initialized. Call initializeGA4Client() first.');
}
try {
const [response] = await analyticsDataClient.runReport({
property: `properties/${propertyId}`,
dateRanges: [{ startDate, endDate }],
dimensions: [
{ name: 'eventName' },
{ name: 'date' },
{ name: 'sessionSource' },
{ name: 'sessionMedium' },
],
metrics: [
{ name: 'eventCount' },
{ name: 'activeUsers' },
],
// Only conversion events
dimensionFilter: {
filter: {
fieldName: 'eventName',
inListFilter: {
values: [
'form_submit',
'sign_up',
'generate_lead',
'purchase',
'contact',
],
},
},
},
});
return parseGA4ResponsparseGA4Response function · javascript · L154-L180 (27 LOC)functions/ga4Service.js
function parseGA4Response(response) {
if (!response || !response.rows) {
return [];
}
const events = [];
response.rows.forEach((row) => {
const event = {};
// Parse dimensions
row.dimensionValues.forEach((dimension, index) => {
const dimensionName = response.dimensionHeaders[index].name;
event[dimensionName] = dimension.value;
});
// Parse metrics
row.metricValues.forEach((metric, index) => {
const metricName = response.metricHeaders[index].name;
event[metricName] = parseFloat(metric.value) || 0;
});
events.push(event);
});
return events;
}getRealTimeMetrics function · javascript · L187-L209 (23 LOC)functions/ga4Service.js
async function getRealTimeMetrics(propertyId) {
if (!analyticsDataClient) {
throw new Error('GA4 client not initialized. Call initializeGA4Client() first.');
}
try {
const [response] = await analyticsDataClient.runRealtimeReport({
property: `properties/${propertyId}`,
dimensions: [
{ name: 'eventName' },
{ name: 'country' },
],
metrics: [
{ name: 'activeUsers' },
],
});
return parseGA4Response(response);
} catch (error) {
console.error('Error fetching real-time metrics:', error);
throw error;
}
}testConnection function · javascript · L216-L239 (24 LOC)functions/ga4Service.js
async function testConnection(propertyId) {
try {
const [response] = await analyticsDataClient.runReport({
property: `properties/${propertyId}`,
dateRanges: [{ startDate: 'yesterday', endDate: 'yesterday' }],
dimensions: [{ name: 'date' }],
metrics: [{ name: 'activeUsers' }],
});
return {
success: true,
propertyId,
rowCount: response.rowCount || 0,
message: `Successfully connected to GA4 property ${propertyId}`,
};
} catch (error) {
return {
success: false,
propertyId,
error: error.message,
message: 'Failed to connect to GA4',
};
}
}mapGA4Source function · javascript · L35-L84 (50 LOC)functions/ga4Transformer.js
function mapGA4Source(source, medium) {
const lowerSource = (source || '').toLowerCase();
const lowerMedium = (medium || '').toLowerCase();
// Google Ads
if (lowerSource.includes('google') && lowerMedium.includes('cpc')) {
return 'google_ads';
}
// Facebook Ads
if (lowerSource.includes('facebook') || lowerSource.includes('fb')) {
if (lowerMedium.includes('cpc') || lowerMedium.includes('paid')) {
return 'facebook_ads';
}
return 'facebook_organic';
}
// LinkedIn
if (lowerSource.includes('linkedin')) {
if (lowerMedium.includes('cpc') || lowerMedium.includes('paid')) {
return 'linkedin_ads';
}
return 'linkedin_organic';
}
// Organic search
if (lowerMedium === 'organic') {
if (lowerSource.includes('google')) return 'google_organic';
if (lowerSource.includes('bing')) return 'bing';
return 'organic_search';
}
// Email
if (lowerMedium === 'email' || lowerSource.includes('email')) {
return 'email';
}
getFunnelStage function · javascript · L89-L126 (38 LOC)functions/ga4Transformer.js
function getFunnelStage(eventType) {
const awarenessEvents = [
'landing_page_view',
'social_post',
'ad_impression',
'ad_click',
'content_engagement',
];
const followUpEvents = [
'email_open',
'email_click',
'form_submission',
'phone_call',
'meeting_scheduled',
'app_signup',
'initial_contact',
];
const convertedEvents = [
'deal_closed',
'contract_signed',
'payment_received',
];
const recommendationEvents = [
'referral_made',
'testimonial_given',
'repeat_purchase',
];
if (awarenessEvents.includes(eventType)) return 'awareness';
if (followUpEvents.includes(eventType)) return 'follow_up';
if (convertedEvents.includes(eventType)) return 'converted';
if (recommendationEvents.includes(eventType)) return 'recommendation';
return 'awareness'; // Default
}transformGA4Event function · javascript · L134-L178 (45 LOC)functions/ga4Transformer.js
function transformGA4Event(ga4Event, userId) {
// Map GA4 event name to engagement event type
const eventType = GA4_EVENT_MAP[ga4Event.eventName] || 'content_engagement';
// Map source/medium to engagement source
const source = mapGA4Source(ga4Event.sessionSource, ga4Event.sessionMedium);
// Determine funnel stage
const stage = getFunnelStage(eventType);
// Parse date (GA4 format: YYYYMMDD)
const dateStr = ga4Event.date || '';
const timestamp = dateStr.length === 8
? admin.firestore.Timestamp.fromDate(
new Date(
dateStr.substring(0, 4), // Year
parseInt(dateStr.substring(4, 6)) - 1, // Month (0-indexed)
dateStr.substring(6, 8) // Day
)
)
: admin.firestore.Timestamp.now();
// Build engagement event
const engagement = {
userId,
eventType,
source,
stage,
timestamp,
metadata: {
ga4_event_name: ga4Event.eventName,
ga4_source: ga4Event.sessionSource || 'unknown',
gatransformGA4Batch function · javascript · L186-L188 (3 LOC)functions/ga4Transformer.js
function transformGA4Batch(ga4Events, userId) {
return ga4Events.map((event) => transformGA4Event(event, userId));
}Provenance: Repobility (https://repobility.com) — every score reproducible from /scan/
deduplicateEngagements function · javascript · L195-L211 (17 LOC)functions/ga4Transformer.js
function deduplicateEngagements(engagements) {
const seen = new Set();
const unique = [];
engagements.forEach((engagement) => {
// Create unique key based on userId, eventType, source, and date
const dateStr = engagement.timestamp.toDate().toISOString().split('T')[0];
const key = `${engagement.userId}_${engagement.eventType}_${engagement.source}_${dateStr}`;
if (!seen.has(key)) {
seen.add(key);
unique.push(engagement);
}
});
return unique;
}aggregatePageViews function · javascript · L219-L249 (31 LOC)functions/ga4Transformer.js
function aggregatePageViews(engagements) {
const aggregated = [];
const pageViewMap = new Map();
engagements.forEach((engagement) => {
if (engagement.eventType === 'landing_page_view') {
const dateStr = engagement.timestamp.toDate().toISOString().split('T')[0];
const key = `${engagement.userId}_${engagement.source}_${dateStr}`;
if (pageViewMap.has(key)) {
// Increment event count
const existing = pageViewMap.get(key);
existing.metadata.event_count += engagement.metadata.event_count || 1;
existing.metadata.active_users = Math.max(
existing.metadata.active_users || 0,
engagement.metadata.active_users || 0
);
} else {
pageViewMap.set(key, engagement);
}
} else {
// Keep non-page-view events as-is
aggregated.push(engagement);
}
});
// Add aggregated page views
pageViewMap.forEach((engagement) => aggregated.push(engagement));
return aggregated;
}filterLowValueEvents function · javascript · L256-L273 (18 LOC)functions/ga4Transformer.js
function filterLowValueEvents(engagements) {
return engagements.filter((engagement) => {
// Keep conversion events always
if (engagement.stage === 'converted') return true;
// Keep follow-up events always
if (engagement.stage === 'follow_up') return true;
// For awareness events, filter out low engagement
if (engagement.eventType === 'content_engagement') {
const engagementRate = engagement.metadata.engagement_rate || 0;
return engagementRate > 0.1; // Only keep if >10% engagement rate
}
// Keep everything else
return true;
});
}getHubSpotClient function · javascript · L6-L13 (8 LOC)functions/hubspotIntegration.js
function getHubSpotClient() {
const apiKey = process.env.HUBSPOT_API_KEY;
if (!apiKey) {
throw new Error('HUBSPOT_API_KEY not set. Run: firebase functions:secrets:set HUBSPOT_API_KEY');
}
console.log('🔑 HubSpot API key found, length:', apiKey.length);
return new hubspot.Client({ accessToken: apiKey });
}createOrUpdateContact function · javascript · L25-L66 (42 LOC)functions/hubspotIntegration.js
async function createOrUpdateContact(contactData) {
const hubspotClient = getHubSpotClient();
const properties = {
email: contactData.email,
};
if (contactData.firstName) properties.firstname = contactData.firstName;
if (contactData.lastName) properties.lastname = contactData.lastName;
if (contactData.phone) properties.phone = contactData.phone;
// Add any custom properties
if (contactData.customProperties) {
Object.assign(properties, contactData.customProperties);
}
try {
// Try to create the contact
const response = await hubspotClient.crm.contacts.basicApi.create({
properties,
associations: []
});
console.log('✅ Contact created in HubSpot:', response.id);
return response;
} catch (error) {
// If contact already exists (409 conflict), update it
if (error.statusCode === 409) {
console.log('Contact already exists, updating...');
const existingContactId = error.body.message.match(/Existing ID: (\d+)/)?createDeal function · javascript · L79-L116 (38 LOC)functions/hubspotIntegration.js
async function createDeal(dealData) {
const hubspotClient = getHubSpotClient();
const properties = {
dealname: dealData.dealName,
};
if (dealData.dealStage) properties.dealstage = dealData.dealStage;
if (dealData.amount) properties.amount = dealData.amount.toString();
if (dealData.pipeline) properties.pipeline = dealData.pipeline;
if (dealData.closeDate) properties.closedate = dealData.closeDate;
// Add any custom properties
if (dealData.customProperties) {
Object.assign(properties, dealData.customProperties);
}
const associations = [];
// Associate with contact if provided
if (dealData.contactId) {
associations.push({
to: { id: dealData.contactId },
types: [{
associationCategory: 'HUBSPOT_DEFINED',
associationTypeId: 3 // Deal to Contact association
}]
});
}
const response = await hubspotClient.crm.deals.basicApi.create({
properties,
associations
});
console.log('✅ Deal created in HubSpocreatePropertyNote function · javascript · L126-L161 (36 LOC)functions/hubspotIntegration.js
async function createPropertyNote(noteData) {
const hubspotClient = getHubSpotClient();
const noteBody = `
📍 Property Analysis: ${noteData.propertyAddress || 'N/A'}
Strategy: ${noteData.strategy || 'N/A'}
Generated by CloudCalcs on ${new Date().toLocaleString()}
`;
const properties = {
hs_timestamp: Date.now().toString(),
hs_note_body: noteBody,
};
const associations = [];
if (noteData.contactId) {
associations.push({
to: { id: noteData.contactId },
types: [{
associationCategory: 'HUBSPOT_DEFINED',
associationTypeId: 202 // Note to Contact association
}]
});
}
const response = await hubspotClient.crm.objects.notes.basicApi.create({
properties,
associations
});
console.log('✅ Property note created in HubSpot:', response.id);
return response;
}findContactByEmail function · javascript · L168-L195 (28 LOC)functions/hubspotIntegration.js
async function findContactByEmail(email) {
const hubspotClient = getHubSpotClient();
try {
const response = await hubspotClient.crm.contacts.searchApi.doSearch({
filterGroups: [{
filters: [{
propertyName: 'email',
operator: 'EQ',
value: email
}]
}],
properties: ['email', 'firstname', 'lastname', 'phone'],
limit: 1
});
if (response.results && response.results.length > 0) {
console.log('✅ Contact found in HubSpot:', response.results[0].id);
return response.results[0];
}
console.log('ℹ️ Contact not found in HubSpot for email:', email);
return null;
} catch (error) {
console.error('Error searching for contact:', error);
throw error;
}
}Repobility analyzer · published findings · https://repobility.com
trackPropertyCalculation function · javascript · L208-L238 (31 LOC)functions/hubspotIntegration.js
async function trackPropertyCalculation(eventData) {
try {
// Find or create contact
let contact = await findContactByEmail(eventData.email);
if (!contact) {
contact = await createOrUpdateContact({
email: eventData.email,
firstName: eventData.firstName,
lastName: eventData.lastName,
phone: eventData.phone
});
}
// Create note with calculation details (only address and method)
const note = await createPropertyNote({
contactId: contact.id,
propertyAddress: eventData.address,
strategy: eventData.method
});
return {
success: true,
contact: contact,
note: note
};
} catch (error) {
console.error('Error tracking property calculation:', error);
throw error;
}
}ensureFlutterFlowCompatibility function · javascript · L87-L102 (16 LOC)functions/index.js
function ensureFlutterFlowCompatibility(obj) {
if (obj === null || obj === undefined) {
return {error: "Null object"};
}
// Convert to JSON and back to remove any problematic values
try {
const jsonString = JSON.stringify(obj, (key, value) => {
if (value === null || value === undefined) return "";
if (typeof value === "number" && (isNaN(value) || !isFinite(value))) return 0;
return value;
});
return JSON.parse(jsonString);
} catch (error) {
return {error: "JSON serialization failed", originalError: error.message};
}
}processBatch function · javascript · L107-L181 (75 LOC)functions/index.js
async function processBatch(batch, params, writeEvent, batchStartCount, totalProcessed, maxProperties) {
console.log(`🚀 Processing batch of ${batch.length} properties (total processed: ${totalProcessed}/${maxProperties})`);
writeEvent("status", {
message: `Processing batch of ${batch.length} properties...`,
totalProcessed: totalProcessed,
maxProperties: maxProperties,
timestamp: new Date().toISOString()
});
// Process all properties in the batch in parallel
const batchPromises = batch.map(async (item, index) => {
try {
console.log(`🏠 Processing property ${item.property.zpid} (${item.sequence}/${item.totalEstimated})`);
const results = await processProperty(item.property, params, item.sequence, item.totalEstimated);
console.log(`✅ Property ${item.property.zpid} completed, got ${results.length} results`);
return results;
} catch (error) {
console.error(`❌ Error processing property ${item.property.zpid}:`, {
error: processUserDripCampaigns function · javascript · L879-L900 (22 LOC)functions/index.js
async function processUserDripCampaigns(userId, db) {
let emailsSent = 0;
// Get all contacts for this user (unique emails)
const contactsMap = await getUserContacts(userId, db);
// Check each contact for drip campaign eligibility
for (const [email, lastEngagement] of contactsMap.entries()) {
// Check 7-day follow-up
if (shouldSend7DayFollowUp(lastEngagement)) {
await send7DayEmail(userId, email, lastEngagement, db);
emailsSent++;
}
// Check 30-day check-in
else if (shouldSend30DayCheckIn(lastEngagement)) {
await send30DayEmail(userId, email, lastEngagement, db);
emailsSent++;
}
}
return emailsSent;
}getUserContacts function · javascript · L905-L930 (26 LOC)functions/index.js
async function getUserContacts(userId, db) {
const snapshot = await db
.collection("engagements")
.where("userId", "==", userId)
.where("contactEmail", "!=", null)
.orderBy("contactEmail")
.orderBy("timestamp", "desc")
.get();
const contactsMap = new Map();
snapshot.docs.forEach((doc) => {
const data = doc.data();
const email = data.contactEmail;
// Only keep the most recent engagement per contact
if (email && !contactsMap.has(email)) {
contactsMap.set(email, {
...data,
id: doc.id,
});
}
});
return contactsMap;
}shouldSend7DayFollowUp function · javascript · L935-L964 (30 LOC)functions/index.js
function shouldSend7DayFollowUp(engagement) {
const now = new Date();
const engagementDate = engagement.timestamp.toDate();
const daysSince = Math.floor(
(now.getTime() - engagementDate.getTime()) / (1000 * 60 * 60 * 24)
);
// Send if:
// 1. Last engagement was 7 days ago
// 2. Last engagement was initial_contact or email_sent
// 3. Haven't sent 7-day follow-up yet
if (daysSince !== 7) {
return false;
}
if (
engagement.eventType !== "initial_contact" &&
engagement.eventType !== "email_sent"
) {
return false;
}
// Check if we've already sent 7-day follow-up
if (engagement.metadata && engagement.metadata.drip7DaySent) {
return false;
}
return true;
}shouldSend30DayCheckIn function · javascript · L969-L1001 (33 LOC)functions/index.js
function shouldSend30DayCheckIn(engagement) {
const now = new Date();
const engagementDate = engagement.timestamp.toDate();
const daysSince = Math.floor(
(now.getTime() - engagementDate.getTime()) / (1000 * 60 * 60 * 24)
);
// Send if:
// 1. Last engagement was 30 days ago
// 2. Last engagement was not converted or recommendation stage
// 3. Haven't sent 30-day check-in yet
if (daysSince !== 30) {
return false;
}
if (
engagement.eventType === "deal_closed" ||
engagement.eventType === "contract_signed" ||
engagement.eventType === "payment_received" ||
engagement.eventType === "referral_made" ||
engagement.eventType === "testimonial_given"
) {
return false;
}
// Check if we've already sent 30-day check-in
if (engagement.metadata && engagement.metadata.drip30DaySent) {
return false;
}
return true;
}send7DayEmail function · javascript · L1006-L1038 (33 LOC)functions/index.js
async function send7DayEmail(userId, email, engagement, db) {
console.log(`[Drip] Sending 7-day follow-up to ${email}`);
// Create email document in 'mail' collection
await db.collection("mail").add({
to: email,
template: {
name: "realdeal-followup-7day",
data: {
contactName: email.split("@")[0], // Extract name from email
},
},
});
// Mark as sent in engagement metadata
await db.collection("engagements").doc(engagement.id).update({
"metadata.drip7DaySent": true,
"metadata.drip7DaySentAt": admin.firestore.FieldValue.serverTimestamp(),
});
// Create engagement event
await db.collection("engagements").add({
eventType: "email_sent",
source: "email",
contactEmail: email,
userId,
timestamp: admin.firestore.FieldValue.serverTimestamp(),
metadata: {
dripCampaign: "7-day-followup",
templateId: "realdeal-followup-7day",
},
});
}Repobility · MCP-ready · https://repobility.com
send30DayEmail function · javascript · L1043-L1075 (33 LOC)functions/index.js
async function send30DayEmail(userId, email, engagement, db) {
console.log(`[Drip] Sending 30-day check-in to ${email}`);
// Create email document in 'mail' collection
await db.collection("mail").add({
to: email,
template: {
name: "realdeal-checkin-30day",
data: {
contactName: email.split("@")[0],
},
},
});
// Mark as sent in engagement metadata
await db.collection("engagements").doc(engagement.id).update({
"metadata.drip30DaySent": true,
"metadata.drip30DaySentAt": admin.firestore.FieldValue.serverTimestamp(),
});
// Create engagement event
await db.collection("engagements").add({
eventType: "email_sent",
source: "email",
contactEmail: email,
userId,
timestamp: admin.firestore.FieldValue.serverTimestamp(),
metadata: {
dripCampaign: "30-day-checkin",
templateId: "realdeal-checkin-30day",
},
});
}loadUserCalcParams function · javascript · L1086-L1118 (33 LOC)functions/index.js
async function loadUserCalcParams(db, email) {
const userSnap = await db.collection("UserData")
.where("email", "==", email).limit(1).get();
if (userSnap.empty) {
console.log(`[DealScanner] No user profile found for ${email}, using defaults`);
return {};
}
const u = userSnap.docs[0].data();
console.log(`[DealScanner] Loaded profile for ${email} (${u.display_name || "unknown"})`);
// Map user profile field names → strategyCalculator param names
const params = {};
if (u.financingRate) params.interestRate = u.financingRate;
if (u.salRate) params.salRate = u.salRate;
if (u.loanFeesRate) params.loanFeesRate = u.loanFeesRate;
if (u.permitsFees) params.permitsFees = u.permitsFees;
if (u.fixnflipDuration) params.fixFlipDuration = u.fixnflipDuration;
if (u.addOnDuration) params.addOnDuration = u.addOnDuration;
if (u.aduDuration) params.aduDuration = u.aduDuration;
if (u.newDuration) params.newBuildDuration = u.newDuration;
if (u.oneBdrmMarketValue)fetchZillowDataWithCache function · javascript · L25-L103 (79 LOC)functions/oaDataApi.js
async function fetchZillowDataWithCache(endpoint, config, maxRetries = 2) {
const docId = `oa_zillow_${endpoint}_${Buffer.from(JSON.stringify(config)).toString("base64")}`;
async function realFetch() {
let retries = 0;
while (retries <= maxRetries) {
try {
let url;
let params = {};
if (endpoint === "zestimate") {
// zpid is "oa_<id>" — extract address from the prop context
// For zestimate, we need address+city. The caller passes {zpid}.
// Since OA uses address-based lookup, we pass zpid as-is and
// the API will resolve it. If zpid starts with "oa_", use the
// listing search to find it.
if (config.address && config.city) {
url = `${OA_DATA_API_URL}/api/v1/valuation/estimate`;
params = { address: config.address, city: config.city, state: config.state || "CA" };
} else {
// Fallback: return empty data (no zpid-based lookup in OA)
realFetch function · javascript · L28-L100 (73 LOC)functions/oaDataApi.js
async function realFetch() {
let retries = 0;
while (retries <= maxRetries) {
try {
let url;
let params = {};
if (endpoint === "zestimate") {
// zpid is "oa_<id>" — extract address from the prop context
// For zestimate, we need address+city. The caller passes {zpid}.
// Since OA uses address-based lookup, we pass zpid as-is and
// the API will resolve it. If zpid starts with "oa_", use the
// listing search to find it.
if (config.address && config.city) {
url = `${OA_DATA_API_URL}/api/v1/valuation/estimate`;
params = { address: config.address, city: config.city, state: config.state || "CA" };
} else {
// Fallback: return empty data (no zpid-based lookup in OA)
return { status: 200, data: { value: 0 } };
}
} else if (endpoint === "propertyDetails") {
if (config.address && config.city) {
urlfetchRedfinDataWithCache function · javascript · L113-L150 (38 LOC)functions/oaDataApi.js
async function fetchRedfinDataWithCache(endpoint, config, maxRetries = 2) {
const docId = `oa_redfin_${endpoint}_${Buffer.from(JSON.stringify(config)).toString("base64")}`;
async function realFetch() {
let retries = 0;
while (retries <= maxRetries) {
try {
let url;
const params = { location: config.location, limit: 50 };
if (endpoint === "searchSold") {
url = `${OA_DATA_API_URL}/api/v1/comps/sold`;
} else if (endpoint === "searchForSale") {
url = `${OA_DATA_API_URL}/api/v1/comps/for-sale`;
} else {
console.warn(`[OA-API] Unknown Redfin endpoint: ${endpoint}`);
return { status: 200, data: { data: { homes: [] } } };
}
const response = await axios.get(url, { params, timeout: 10000 });
// OA returns {status, data: {data: {homes: [...]}}} — matches Redfin shape
return { status: 200, data: response.data.data || { data: { homes: [] } } };
} catch (err) {
realFetch function · javascript · L116-L147 (32 LOC)functions/oaDataApi.js
async function realFetch() {
let retries = 0;
while (retries <= maxRetries) {
try {
let url;
const params = { location: config.location, limit: 50 };
if (endpoint === "searchSold") {
url = `${OA_DATA_API_URL}/api/v1/comps/sold`;
} else if (endpoint === "searchForSale") {
url = `${OA_DATA_API_URL}/api/v1/comps/for-sale`;
} else {
console.warn(`[OA-API] Unknown Redfin endpoint: ${endpoint}`);
return { status: 200, data: { data: { homes: [] } } };
}
const response = await axios.get(url, { params, timeout: 10000 });
// OA returns {status, data: {data: {homes: [...]}}} — matches Redfin shape
return { status: 200, data: response.data.data || { data: { homes: [] } } };
} catch (err) {
if (err.response?.status === 429 || err.code === "ECONNREFUSED") {
await new Promise((r) => setTimeout(r, 1000 + retries * 500));
retries++;
fetchMarketSignal function · javascript · L156-L167 (12 LOC)functions/oaDataApi.js
async function fetchMarketSignal(zipCode) {
try {
const response = await axios.get(`${OA_DATA_API_URL}/api/v1/market/signal`, {
params: { zip_code: zipCode },
timeout: 5000,
});
return response.data;
} catch (err) {
console.warn(`[OA-API] Market signal error: ${err.message}`);
return { signal: "yellow", composite_score: 50, recommendation: "Data unavailable" };
}
}fetchDistressCheck function · javascript · L173-L189 (17 LOC)functions/oaDataApi.js
async function fetchDistressCheck(address, city) {
try {
const response = await axios.get(`${OA_DATA_API_URL}/api/v1/distress/check`, {
params: { address, city },
timeout: 5000,
});
return response.data;
} catch (err) {
console.warn(`[OA-API] Distress check error: ${err.message}`);
return {
has_distress: false,
is_foreclosure: false,
is_pre_foreclosure: false,
has_tax_delinquency: false,
};
}
}Repobility · code-quality intelligence · https://repobility.com
extractCompPrice function · javascript · L117-L120 (4 LOC)functions/propertyProcessor.js
function extractCompPrice(c) {
return c.data?.aboveTheFold?.addressSectionInfo?.priceInfo?.amount ||
(typeof c.price === "object" ? c.price?.value : c.price) || 0;
}extractCompSqft function · javascript · L121-L124 (4 LOC)functions/propertyProcessor.js
function extractCompSqft(c) {
return c.data?.aboveTheFold?.addressSectionInfo?.sqFt?.value ||
(typeof c.sqFt === "object" ? c.sqFt?.value : c.sqFt) || 0;
}extractLotSize function · javascript · L125-L131 (7 LOC)functions/propertyProcessor.js
function extractLotSize(c) {
// If lotSize is an object with value property, extract it
if (typeof c.lotSize === "object" && c.lotSize !== null) {
return c.lotSize.value || 0;
}
return c.lotSize || 0;
}