41 lines
1.6 KiB
JavaScript
41 lines
1.6 KiB
JavaScript
/// <reference path="../pb_data/types.d.ts" />
|
|
//
|
|
// Adds intentional-cancellation support to scrape_runs — distinct from
|
|
// a job merely surviving a frontend disconnect (CLAUDE.md §18/§21,
|
|
// jobs.py's run_research_job already runs to completion regardless of
|
|
// the dashboard's polling state, by design). This migration adds the
|
|
// PM-facing "stop this analysis" affordance:
|
|
//
|
|
// cancel_requested bool set true by POST /research/cancel/<job_id>,
|
|
// checked by run_research_job() between
|
|
// competitors — see jobs.py's per-competitor
|
|
// loop for why this is a between-competitor
|
|
// checkpoint rather than an interrupt of a
|
|
// scrape/extraction already in flight
|
|
// status: 'cancelled' new select option, distinct from 'failed'
|
|
// so the dashboard can show "Cancelled by
|
|
// you" rather than an error state
|
|
|
|
migrate((app) => {
|
|
const collection = app.findCollectionByNameOrId('scrape_runs');
|
|
|
|
collection.fields.add(new Field({
|
|
type: 'bool',
|
|
name: 'cancel_requested',
|
|
}));
|
|
|
|
const statusField = collection.fields.getByName('status');
|
|
statusField.values = [...statusField.values, 'cancelled'];
|
|
|
|
return app.save(collection);
|
|
}, (app) => {
|
|
const collection = app.findCollectionByNameOrId('scrape_runs');
|
|
|
|
collection.fields.removeByName('cancel_requested');
|
|
|
|
const statusField = collection.fields.getByName('status');
|
|
statusField.values = statusField.values.filter((v) => v !== 'cancelled');
|
|
|
|
return app.save(collection);
|
|
});
|