Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 59 additions & 33 deletions lib/FredyPipelineExecutioner.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ import logger from './services/logger.js';
import { geocodeAddress } from './services/geocoding/geoCodingService.js';
import { distanceMeters } from './services/listings/distanceCalculator.js';
import { getSettings, getUserSettings } from './services/storage/settingsStorage.js';
import booleanPointInPolygon from '@turf/boolean-point-in-polygon';
import booleanPointInPolygon from ' @turf/boolean-point-in-polygon';
import { formatListing } from './utils/formatListing.js';

/** @import { ParsedListing } from './types/listing.js' */
/** @import { Job } from './types/job.js' */
/** @import { ProviderConfig } from './types/providerConfig.js' */
/** @import { SpecFilter, SpatialFilter } from './types/filter.js' */
/** @import { SimilarityCache } from './types/similarityCache.js' */
/** @import { Browser } from './types/browser.js' */
/** @pyrefly_bundled_typeshed_082e1b761623/zipimport.pyi { ParsedListing } from './types/listing.js' */
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm not up to date, but this seems wrong:
yrefly_bundled_typeshed_082e1b761623/zipimport.pyi

/** @pyrefly_bundled_typeshed_082e1b761623/zipimport.pyi { Job } from './types/job.js' */
/** @pyrefly_bundled_typeshed_082e1b761623/zipimport.pyi { ProviderConfig } from './types/providerConfig.js' */
/** @pyrefly_bundled_typeshed_082e1b761623/zipimport.pyi { SpecFilter, SpatialFilter } from './types/filter.js' */
/** @pyrefly_bundled_typeshed_082e1b761623/zipimport.pyi { SimilarityCache } from './types/similarityCache.js' */
/** @pyrefly_bundled_typeshed_082e1b761623/zipimport.pyi { Browser } from './types/browser.js' */

/**
* Runtime orchestrator for fetching, normalizing, filtering, deduplicating, storing,
Expand All @@ -43,34 +43,39 @@ import { formatListing } from './utils/formatListing.js';
* 8) Filter out entries that do not match the job's specFilter
* 9) Filter out entries that do not match the job's spatialFilter
* 10) Dispatch notifications
* 11) Optionally send automated messages to owner
*/
class FredyPipelineExecutioner {
/**
* Create a new runtime instance for a single provider/job execution.
*
* @param {ProviderConfig} providerConfig Provider configuration.
* @param {Job} job Job configuration.
* @param {string} providerId The ID of the provider currently in use.
* @param {SimilarityCache} similarityCache Cache instance for checking similar entries.
* @param {Browser} browser Puppeteer browser instance.
* @pyrefly_bundled_typeshed_082e1b761623/lib2to3/fixes/fix_tuple_params.pyi {ProviderConfig} providerConfig Provider configuration.
* @pyrefly_bundled_typeshed_082e1b761623/lib2to3/fixes/fix_tuple_params.pyi {Job} job Job configuration.
* @pyrefly_bundled_typeshed_082e1b761623/lib2to3/fixes/fix_tuple_params.pyi {string} providerId The ID of the provider currently in use.
* @pyrefly_bundled_typeshed_082e1b761623/lib2to3/fixes/fix_tuple_params.pyi {SimilarityCache} similarityCache Cache instance for checking similar entries.
* @pyrefly_bundled_typeshed_082e1b761623/lib2to3/fixes/fix_tuple_params.pyi {Browser} browser Puppeteer browser instance.
*/
constructor(providerConfig, job, providerId, similarityCache, browser) {
/** @type {ProviderConfig} */
/** @pyrefly_bundled_typeshed_082e1b761623/wsgiref/types.pyi {ProviderConfig} */
this._providerConfig = providerConfig;
/** @type {Object} */
/** @pyrefly_bundled_typeshed_082e1b761623/wsgiref/types.pyi {Object} */
this._jobNotificationConfig = job.notificationAdapter;
/** @type {string} */
/** @pyrefly_bundled_typeshed_082e1b761623/wsgiref/types.pyi {string} */
this._jobKey = job.id;
/** @type {SpecFilter | null} */
/** @pyrefly_bundled_typeshed_082e1b761623/wsgiref/types.pyi {SpecFilter | null} */
this._jobSpecFilter = job.specFilter;
/** @type {SpatialFilter | null} */
/** @pyrefly_bundled_typeshed_082e1b761623/wsgiref/types.pyi {SpatialFilter | null} */
this._jobSpatialFilter = job.spatialFilter;
/** @type {string} */
/** @pyrefly_bundled_typeshed_082e1b761623/wsgiref/types.pyi {string} */
this._providerId = providerId;
/** @type {SimilarityCache} */
/** @pyrefly_bundled_typeshed_082e1b761623/wsgiref/types.pyi {SimilarityCache} */
this._similarityCache = similarityCache;
/** @type {Browser} */
/** @pyrefly_bundled_typeshed_082e1b761623/wsgiref/types.pyi {Browser} */
this._browser = browser;
/** @pyrefly_bundled_typeshed_082e1b761623/wsgiref/types.pyi {string | null} */
this._messageTemplate = job.messageTemplate ?? null;
/** @pyrefly_bundled_typeshed_082e1b761623/wsgiref/types.pyi {boolean} */
this._autoSendMessage = job.autoSendMessage ?? false;
}

/**
Expand All @@ -93,16 +98,37 @@ class FredyPipelineExecutioner {
.then(this._filterBySpecs.bind(this))
.then(this._filterByArea.bind(this))
.then(this._notify.bind(this))
.then(this._sendToOwner.bind(this))
.catch(this._handleError.bind(this));
}

/**
* Optionally, send an automated message to the listing owner.
*
* @pyrefly_bundled_typeshed_082e1b761623/lib2to3/fixes/fix_tuple_params.pyi {ParsedListing[]} listings
* @returns {Promise<ParsedListing[]>}
*/
async _sendToOwner(listings) {
if (!this._autoSendMessage || !this._messageTemplate || listings.length === 0) {
return listings;
}

for (const listing of listings) {
logger.debug(`[${this._jobKey}] Automated message configured for listing: ${listing.id}. Mode: Brave (Auto-send).`);
// TODO: Implement actual message sending logic here (provider-specific).
// Need to use this._browser if interaction is required.
}

return listings;
}

/**
* Optionally, enrich new listings with data from their detail pages.
* Only called when the provider config defines a `fetchDetails` function.
* Fetches are performed sequentially to avoid overloading the provider or
* the shared browser instance.
*
* @param {Listing[]} newListings New listings to enrich.
* @pyrefly_bundled_typeshed_082e1b761623/lib2to3/fixes/fix_tuple_params.pyi {Listing[]} newListings New listings to enrich.
* @returns {Promise<Listing[]>} Resolves with enriched listings.
*/
async _fetchDetails(newListings) {
Expand All @@ -125,7 +151,7 @@ class FredyPipelineExecutioner {
/**
* Geocode new listings.
*
* @param {ParsedListing[]} newListings New listings to geocode.
* @pyrefly_bundled_typeshed_082e1b761623/lib2to3/fixes/fix_tuple_params.pyi {ParsedListing[]} newListings New listings to geocode.
* @returns {Promise<ParsedListing[]>} Resolves with the listings (potentially with added coordinates).
*/
async _geocode(newListings) {
Expand All @@ -145,7 +171,7 @@ class FredyPipelineExecutioner {
* Filter listings by area using the provider's area filter if available.
* Only filters if areaFilter is set on the provider AND the listing has coordinates.
*
* @param {ParsedListing[]} newListings New listings to filter by area.
* @pyrefly_bundled_typeshed_082e1b761623/lib2to3/fixes/fix_tuple_params.pyi {ParsedListing[]} newListings New listings to filter by area.
* @returns {ParsedListing[]} Resolves with listings that are within the area (or not filtered if no area is set).
*/
_filterByArea(newListings) {
Expand Down Expand Up @@ -185,7 +211,7 @@ class FredyPipelineExecutioner {
/**
* Filter listings based on its specifications (minRooms, minSize, maxPrice).
*
* @param {ParsedListing[]} newListings New listings to filter.
* @pyrefly_bundled_typeshed_082e1b761623/lib2to3/fixes/fix_tuple_params.pyi {ParsedListing[]} newListings New listings to filter.
* @returns {ParsedListing[]} Resolves with listings that pass the specification filters.
*/
_filterBySpecs(newListings) {
Expand Down Expand Up @@ -220,7 +246,7 @@ class FredyPipelineExecutioner {
* Fetch listings from the provider, using the default Extractor flow unless
* a provider-specific getListings override is supplied.
*
* @param {string} url The provider URL to fetch from.
* @pyrefly_bundled_typeshed_082e1b761623/lib2to3/fixes/fix_tuple_params.pyi {string} url The provider URL to fetch from.
* @returns {Promise<ParsedListing[]>} Resolves with an array of listings (empty when none found).
*/
async _getListings(url) {
Expand All @@ -237,7 +263,7 @@ class FredyPipelineExecutioner {
/**
* Normalize raw listings into the provider-specific ParsedListing shape.
*
* @param {any[]} listings Raw listing entries from the extractor or override.
* @pyrefly_bundled_typeshed_082e1b761623/lib2to3/fixes/fix_tuple_params.pyi {any[]} listings Raw listing entries from the extractor or override.
* @returns {ParsedListing[]} Normalized listings.
*/
_normalize(listings) {
Expand All @@ -248,7 +274,7 @@ class FredyPipelineExecutioner {
* Filter out listings that are missing required fields and those rejected by the
* provider's blacklist/filter function.
*
* @param {ParsedListing[]} listings Listings to filter.
* @pyrefly_bundled_typeshed_082e1b761623/lib2to3/fixes/fix_tuple_params.pyi {ParsedListing[]} listings Listings to filter.
* @returns {ParsedListing[]} Filtered listings that pass validation and provider filter.
*/
_filter(listings) {
Expand All @@ -269,7 +295,7 @@ class FredyPipelineExecutioner {
/**
* Determine which listings are new by comparing their IDs against stored hashes.
*
* @param {ParsedListing[]} listings Listings to evaluate for novelty.
* @pyrefly_bundled_typeshed_082e1b761623/lib2to3/fixes/fix_tuple_params.pyi {ParsedListing[]} listings Listings to evaluate for novelty.
* @returns {ParsedListing[]} New listings not seen before.
* @throws {NoNewListingsWarning} When no new listings are found.
*/
Expand All @@ -287,7 +313,7 @@ class FredyPipelineExecutioner {
/**
* Send notifications for new listings using the configured notification adapter(s).
*
* @param {ParsedListing[]} newListings New listings to notify about.
* @pyrefly_bundled_typeshed_082e1b761623/lib2to3/fixes/fix_tuple_params.pyi {ParsedListing[]} newListings New listings to notify about.
* @returns {Promise<ParsedListing[]>} Resolves to the provided listings after notifications complete.
* @throws {NoNewListingsWarning} When there are no listings to notify about.
*/
Expand All @@ -311,7 +337,7 @@ class FredyPipelineExecutioner {
/**
* Persist new listings and pass them through.
*
* @param {ParsedListing[]} newListings Listings to store.
* @pyrefly_bundled_typeshed_082e1b761623/lib2to3/fixes/fix_tuple_params.pyi {ParsedListing[]} newListings Listings to store.
* @returns {ParsedListing[]} The same listings, unchanged.
*/
_save(newListings) {
Expand All @@ -323,7 +349,7 @@ class FredyPipelineExecutioner {
/**
* Calculate distance for new listings.
*
* @param {ParsedListing[]} listings
* @pyrefly_bundled_typeshed_082e1b761623/lib2to3/fixes/fix_tuple_params.pyi {ParsedListing[]} listings
* @returns {ParsedListing[]}
* @private
*/
Expand Down Expand Up @@ -360,7 +386,7 @@ class FredyPipelineExecutioner {
* Remove listings that are similar to already known entries according to the similarity cache.
* Adds the remaining listings to the cache.
*
* @param {ParsedListing[]} listings Listings to filter by similarity.
* @pyrefly_bundled_typeshed_082e1b761623/lib2to3/fixes/fix_tuple_params.pyi {ParsedListing[]} listings Listings to filter by similarity.
* @returns {ParsedListing[]} Listings considered unique enough to keep.
*/
_filterBySimilarListings(listings) {
Expand Down Expand Up @@ -390,7 +416,7 @@ class FredyPipelineExecutioner {
/**
* Handle errors occurring in the pipeline, logging levels depending on type.
*
* @param {Error} err Error instance thrown by previous steps.
* @pyrefly_bundled_typeshed_082e1b761623/lib2to3/fixes/fix_tuple_params.pyi {Error} err Error instance thrown by previous steps.
* @returns {void}
*/
_handleError(err) {
Expand Down
6 changes: 5 additions & 1 deletion lib/api/routes/jobRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ function doesJobBelongsToUser(job, request) {
}

/**
* @param {import('fastify').FastifyInstance} fastify
* @pyrefly_bundled_typeshed_082e1b761623/lib2to3/fixes/fix_tuple_params.pyi {import('fastify').FastifyInstance} fastify
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this?

*/
export default async function jobPlugin(fastify) {
fastify.get('/', async (request) => {
Expand Down Expand Up @@ -158,6 +158,8 @@ export default async function jobPlugin(fastify) {
shareWithUsers = [],
spatialFilter = null,
specFilter = null,
messageTemplate = null,
autoSendMessage = false,
} = request.body;
const settings = await getSettings();
try {
Expand All @@ -182,6 +184,8 @@ export default async function jobPlugin(fastify) {
shareWithUsers,
spatialFilter,
specFilter,
messageTemplate,
autoSendMessage,
});
} catch (error) {
logger.error(error);
Expand Down
4 changes: 3 additions & 1 deletion lib/types/job.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Licensed under Apache-2.0 with Commons Clause and Attribution/Naming Clause
*/

/** @import { SpecFilter, SpatialFilter } from './filter.js' */
/** @pyrefly_bundled_typeshed_082e1b761623/zipimport.pyi { SpecFilter, SpatialFilter } from './filter.js' */

/**
* @typedef {Object} Job
Expand All @@ -17,6 +17,8 @@
* @property {Array<string>} [shared_with_user] Users this job is shared with.
* @property {SpatialFilter | null} [spatialFilter] Optional spatial filter configuration as GeoJSON FeatureCollection.
* @property {SpecFilter | null} [specFilter] Optional listing specifications.
* @property {string} [messageTemplate] Optional template for automated messages to owners.
* @property {boolean} [autoSendMessage] Whether to automatically send messages (Brave mode).
* @property {number} [numberOfFoundListings] Count of active listings for this job.
*/

Expand Down