270 lines
22 KiB
TypeScript
270 lines
22 KiB
TypeScript
import { randomUUID } from 'node:crypto'
|
|
import type { PoolClient } from 'pg'
|
|
|
|
type IdMap = Map<string, string>
|
|
|
|
function mapped(ids: IdMap, sourceId: string, label: string) {
|
|
const id = ids.get(sourceId)
|
|
if (!id) throw new Error(`Could not map ${label} ${sourceId}`)
|
|
return id
|
|
}
|
|
|
|
export async function clearBoard(client: PoolClient, boardId: string) {
|
|
await client.query('DELETE FROM osint.case_report_submissions WHERE board_id=$1', [boardId])
|
|
await client.query('DELETE FROM osint.case_reports WHERE board_id=$1', [boardId])
|
|
await client.query('DELETE FROM osint.board_views WHERE board_id=$1', [boardId])
|
|
await client.query('DELETE FROM osint.brief_concepts WHERE board_id=$1', [boardId])
|
|
await client.query('DELETE FROM osint.level_briefs WHERE board_id=$1', [boardId])
|
|
await client.query('DELETE FROM osint.level_goals WHERE board_id=$1', [boardId])
|
|
await client.query('DELETE FROM osint.evidence_match_rules WHERE board_id=$1', [boardId])
|
|
await client.query('DELETE FROM osint.metadata_fields WHERE board_id=$1', [boardId])
|
|
await client.query('DELETE FROM osint.exhibits WHERE board_id=$1', [boardId])
|
|
await client.query('UPDATE osint.boards SET revision=0,updated_at=NOW() WHERE id=$1', [boardId])
|
|
}
|
|
|
|
/** Clone a complete normalized board. The target board must be empty. */
|
|
export async function cloneBoard(client: PoolClient, sourceBoardId: string, targetBoardId: string) {
|
|
const exhibitIds: IdMap = new Map()
|
|
const regionIds: IdMap = new Map()
|
|
const fieldIds: IdMap = new Map()
|
|
|
|
const views = await client.query<{
|
|
id: string; view_type_id: string; placement_mode: string; dock_edge: string | null; xpos: number | null; ypos: number | null
|
|
width: number | null; height: number; z_index: number; visible: boolean; range_mode: string | null; range_start: string | null; range_end: string | null
|
|
}>(`SELECT v.id,v.view_type_id,v.placement_mode,v.dock_edge,v.xpos,v.ypos,v.width,v.height,v.z_index,v.visible,
|
|
t.range_mode,t.range_start::text,t.range_end::text FROM osint.board_views v
|
|
LEFT JOIN osint.timeline_views t ON t.view_id=v.id WHERE v.board_id=$1 ORDER BY v.z_index,v.created_at`, [sourceBoardId])
|
|
for (const row of views.rows) {
|
|
const viewId = randomUUID()
|
|
await client.query(`INSERT INTO osint.board_views
|
|
(id,board_id,view_type_id,origin_view_id,placement_mode,dock_edge,xpos,ypos,width,height,z_index,visible)
|
|
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12)`, [viewId,targetBoardId,row.view_type_id,row.id,row.placement_mode,row.dock_edge,
|
|
row.xpos,row.ypos,row.width,row.height,row.z_index,row.visible])
|
|
if (row.view_type_id === 'timeline') await client.query(
|
|
'INSERT INTO osint.timeline_views (view_id,range_mode,range_start,range_end) VALUES ($1,$2,$3,$4)',
|
|
[viewId,row.range_mode || 'auto',row.range_start,row.range_end])
|
|
}
|
|
|
|
const exhibits = await client.query<{
|
|
id: string; exhibit_type_id: string; xpos: number; ypos: number; width: number; height: number
|
|
rotation: number; z_index: number; hidden: boolean
|
|
}>(`SELECT id,exhibit_type_id,xpos,ypos,width,height,rotation,z_index,hidden
|
|
FROM osint.exhibits WHERE board_id=$1 ORDER BY created_at,id`, [sourceBoardId])
|
|
for (const row of exhibits.rows) {
|
|
const id = randomUUID(); exhibitIds.set(row.id, id)
|
|
await client.query(`INSERT INTO osint.exhibits
|
|
(id,board_id,exhibit_type_id,origin_exhibit_id,xpos,ypos,width,height,rotation,z_index,hidden)
|
|
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11)`,
|
|
[id, targetBoardId, row.exhibit_type_id, row.id, row.xpos, row.ypos, row.width, row.height, row.rotation, row.z_index, row.hidden])
|
|
}
|
|
|
|
const folders = await client.query<{ exhibit_id: string; title: string; label_text: string; is_open: boolean }>(
|
|
`SELECT f.* FROM osint.folder_exhibits f JOIN osint.exhibits e ON e.id=f.exhibit_id WHERE e.board_id=$1`, [sourceBoardId])
|
|
for (const row of folders.rows) await client.query(
|
|
'INSERT INTO osint.folder_exhibits (exhibit_id,title,label_text,is_open) VALUES ($1,$2,$3,$4)',
|
|
[mapped(exhibitIds, row.exhibit_id, 'folder'), row.title, row.label_text, row.is_open])
|
|
|
|
const documents = await client.query<{
|
|
exhibit_id: string; document_type_id: string; asset_id: string | null; title: string
|
|
published_at: Date | null; captured_at: Date | null; source_uri: string | null; citation_text:string
|
|
}>(`SELECT d.* FROM osint.document_exhibits d JOIN osint.exhibits e ON e.id=d.exhibit_id WHERE e.board_id=$1`, [sourceBoardId])
|
|
for (const row of documents.rows) await client.query(`INSERT INTO osint.document_exhibits
|
|
(exhibit_id,document_type_id,asset_id,title,published_at,captured_at,source_uri,citation_text) VALUES ($1,$2,$3,$4,$5,$6,$7,$8)`,
|
|
[mapped(exhibitIds, row.exhibit_id, 'document'), row.document_type_id, row.asset_id, row.title, row.published_at, row.captured_at, row.source_uri,row.citation_text])
|
|
|
|
const citations = await client.query<{ exhibit_id:string;display_number:number }>(
|
|
'SELECT exhibit_id,display_number FROM osint.exhibit_citations WHERE board_id=$1 ORDER BY display_number',[sourceBoardId])
|
|
for (const row of citations.rows) await client.query(
|
|
'INSERT INTO osint.exhibit_citations (board_id,exhibit_id,display_number) VALUES ($1,$2,$3)',
|
|
[targetBoardId,mapped(exhibitIds,row.exhibit_id,'cited exhibit'),row.display_number])
|
|
|
|
const documentRequirements = await client.query<{ document_exhibit_id: string; flag_key: string }>(
|
|
'SELECT document_exhibit_id,flag_key FROM osint.document_flag_requirements WHERE board_id=$1 ORDER BY document_exhibit_id,flag_key', [sourceBoardId])
|
|
for (const row of documentRequirements.rows) await client.query(
|
|
'INSERT INTO osint.document_flag_requirements (board_id,document_exhibit_id,flag_key) VALUES ($1,$2,$3)',
|
|
[targetBoardId, mapped(exhibitIds, row.document_exhibit_id, 'document reveal requirement'), row.flag_key])
|
|
|
|
const ruleIds: IdMap = new Map()
|
|
const matchRules = await client.query<{
|
|
id: string; name: string; source_label:string | null; source_uri:string | null; flag_key: string; matcher_version: string; minimum_anchor_matches: number; enabled: boolean
|
|
}>('SELECT id,name,source_label,source_uri,flag_key,matcher_version,minimum_anchor_matches,enabled FROM osint.evidence_match_rules WHERE board_id=$1 ORDER BY created_at,id', [sourceBoardId])
|
|
for (const row of matchRules.rows) {
|
|
const id = randomUUID(); ruleIds.set(row.id, id)
|
|
await client.query(`INSERT INTO osint.evidence_match_rules
|
|
(id,board_id,origin_rule_id,name,source_label,source_uri,flag_key,matcher_version,minimum_anchor_matches,enabled)
|
|
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)`, [id,targetBoardId,row.id,row.name,row.source_label,row.source_uri,row.flag_key,row.matcher_version,row.minimum_anchor_matches,row.enabled])
|
|
}
|
|
const matchAnchors = await client.query<{ rule_id: string; phrase_text: string; minimum_similarity: string; sort_order: number }>(
|
|
`SELECT a.rule_id,a.phrase_text,a.minimum_similarity::text,a.sort_order FROM osint.evidence_match_anchors a
|
|
JOIN osint.evidence_match_rules r ON r.id=a.rule_id WHERE r.board_id=$1 ORDER BY a.rule_id,a.sort_order,a.id`, [sourceBoardId])
|
|
for (const row of matchAnchors.rows) await client.query(`INSERT INTO osint.evidence_match_anchors
|
|
(id,rule_id,phrase_text,minimum_similarity,sort_order) VALUES ($1,$2,$3,$4,$5)`,
|
|
[randomUUID(), mapped(ruleIds, row.rule_id, 'evidence match rule'), row.phrase_text, row.minimum_similarity, row.sort_order])
|
|
|
|
const goalIds: IdMap = new Map()
|
|
const goals = await client.query<{
|
|
id: string; goal_key: string; title: string; instructions: string; completion_message: string; enabled: boolean
|
|
}>(`SELECT id,goal_key,title,instructions,completion_message,enabled FROM osint.level_goals
|
|
WHERE board_id=$1 ORDER BY created_at,id`, [sourceBoardId])
|
|
for (const row of goals.rows) {
|
|
const id = randomUUID(); goalIds.set(row.id, id)
|
|
await client.query(`INSERT INTO osint.level_goals
|
|
(id,board_id,origin_goal_id,goal_key,title,instructions,completion_message,enabled)
|
|
VALUES ($1,$2,$3,$4,$5,$6,$7,$8)`,
|
|
[id,targetBoardId,row.id,row.goal_key,row.title,row.instructions,row.completion_message,row.enabled])
|
|
}
|
|
const goalRequirements = await client.query<{ goal_id: string; flag_key: string }>(
|
|
`SELECT requirement.goal_id,requirement.flag_key FROM osint.level_goal_flag_requirements requirement
|
|
JOIN osint.level_goals goal ON goal.id=requirement.goal_id
|
|
WHERE goal.board_id=$1 ORDER BY requirement.goal_id,requirement.flag_key`, [sourceBoardId])
|
|
for (const row of goalRequirements.rows) await client.query(
|
|
'INSERT INTO osint.level_goal_flag_requirements (board_id,goal_id,flag_key) VALUES ($1,$2,$3)',
|
|
[targetBoardId, mapped(goalIds, row.goal_id, 'level goal'), row.flag_key])
|
|
|
|
const semanticRules = await client.query<{
|
|
id: string; goal_id: string; name: string; target_subject: string; related_subject: string | null; assertion_text: string
|
|
success_flag_key: string; related_flag_key: string | null; minimum_confidence: string; evaluator_version: string; enabled: boolean
|
|
}>(`SELECT id,goal_id,name,target_subject,related_subject,assertion_text,success_flag_key,related_flag_key,
|
|
minimum_confidence::text,evaluator_version,enabled FROM osint.evidence_semantic_rules
|
|
WHERE board_id=$1 ORDER BY created_at,id`, [sourceBoardId])
|
|
for (const row of semanticRules.rows) await client.query(`INSERT INTO osint.evidence_semantic_rules
|
|
(id,board_id,origin_rule_id,goal_id,name,target_subject,related_subject,assertion_text,success_flag_key,
|
|
related_flag_key,minimum_confidence,evaluator_version,enabled)
|
|
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13)`,
|
|
[randomUUID(),targetBoardId,row.id,mapped(goalIds,row.goal_id,'semantic evidence goal'),row.name,row.target_subject,row.related_subject,
|
|
row.assertion_text,row.success_flag_key,row.related_flag_key,row.minimum_confidence,row.evaluator_version,row.enabled])
|
|
|
|
const images = await client.query<{ exhibit_id: string; pixel_width: number | null; pixel_height: number | null; alt_text: string }>(
|
|
`SELECT i.* FROM osint.image_documents i JOIN osint.exhibits e ON e.id=i.exhibit_id WHERE e.board_id=$1`, [sourceBoardId])
|
|
for (const row of images.rows) await client.query(
|
|
'INSERT INTO osint.image_documents (exhibit_id,pixel_width,pixel_height,alt_text) VALUES ($1,$2,$3,$4)',
|
|
[mapped(exhibitIds, row.exhibit_id, 'image'), row.pixel_width, row.pixel_height, row.alt_text])
|
|
|
|
const notes = await client.query<{ exhibit_id: string; title: string; note_text: string }>(
|
|
`SELECT n.* FROM osint.note_exhibits n JOIN osint.exhibits e ON e.id=n.exhibit_id WHERE e.board_id=$1`, [sourceBoardId])
|
|
for (const row of notes.rows) await client.query('INSERT INTO osint.note_exhibits (exhibit_id,title,note_text) VALUES ($1,$2,$3)',
|
|
[mapped(exhibitIds, row.exhibit_id, 'note'), row.title, row.note_text])
|
|
|
|
const claims = await client.query<{ exhibit_id:string;statement:string }>(
|
|
`SELECT claim.exhibit_id,claim.statement FROM osint.claim_exhibits claim
|
|
JOIN osint.exhibits exhibit ON exhibit.id=claim.exhibit_id WHERE exhibit.board_id=$1`,[sourceBoardId])
|
|
for (const row of claims.rows) await client.query('INSERT INTO osint.claim_exhibits (exhibit_id,statement) VALUES ($1,$2)',
|
|
[mapped(exhibitIds,row.exhibit_id,'claim'),row.statement])
|
|
|
|
const events = await client.query<{ exhibit_id: string; title: string; narrative_text: string; occurred_at: Date | null }>(
|
|
`SELECT ev.* FROM osint.event_exhibits ev JOIN osint.exhibits e ON e.id=ev.exhibit_id WHERE e.board_id=$1`, [sourceBoardId])
|
|
for (const row of events.rows) await client.query(
|
|
'INSERT INTO osint.event_exhibits (exhibit_id,title,narrative_text,occurred_at) VALUES ($1,$2,$3,$4)',
|
|
[mapped(exhibitIds, row.exhibit_id, 'event'), row.title, row.narrative_text, row.occurred_at])
|
|
|
|
const parties = await client.query<{ exhibit_id: string; party_kind: string; display_name: string; summary: string }>(
|
|
`SELECT p.* FROM osint.party_exhibits p JOIN osint.exhibits e ON e.id=p.exhibit_id WHERE e.board_id=$1`, [sourceBoardId])
|
|
for (const row of parties.rows) await client.query(
|
|
'INSERT INTO osint.party_exhibits (exhibit_id,party_kind,display_name,summary) VALUES ($1,$2,$3,$4)',
|
|
[mapped(exhibitIds, row.exhibit_id, 'party'), row.party_kind, row.display_name, row.summary])
|
|
const people = await client.query<{ exhibit_id: string; given_name: string | null; family_name: string | null }>(
|
|
`SELECT p.* FROM osint.person_parties p JOIN osint.exhibits e ON e.id=p.exhibit_id WHERE e.board_id=$1`, [sourceBoardId])
|
|
for (const row of people.rows) await client.query('INSERT INTO osint.person_parties (exhibit_id,given_name,family_name) VALUES ($1,$2,$3)',
|
|
[mapped(exhibitIds, row.exhibit_id, 'person'), row.given_name, row.family_name])
|
|
const organizations = await client.query<{ exhibit_id: string; organization_kind: string }>(
|
|
`SELECT o.* FROM osint.organization_parties o JOIN osint.exhibits e ON e.id=o.exhibit_id WHERE e.board_id=$1`, [sourceBoardId])
|
|
for (const row of organizations.rows) await client.query('INSERT INTO osint.organization_parties (exhibit_id,organization_kind) VALUES ($1,$2)',
|
|
[mapped(exhibitIds, row.exhibit_id, 'organization'), row.organization_kind])
|
|
const aliases = await client.query<{ party_exhibit_id: string; alias: string; sort_order: number }>(
|
|
`SELECT a.party_exhibit_id,a.alias,a.sort_order FROM osint.party_aliases a JOIN osint.exhibits e ON e.id=a.party_exhibit_id
|
|
WHERE e.board_id=$1 ORDER BY a.sort_order`, [sourceBoardId])
|
|
for (const row of aliases.rows) await client.query('INSERT INTO osint.party_aliases (id,party_exhibit_id,alias,sort_order) VALUES ($1,$2,$3,$4)',
|
|
[randomUUID(), mapped(exhibitIds, row.party_exhibit_id, 'party alias'), row.alias, row.sort_order])
|
|
|
|
const blocks = await client.query<{ document_exhibit_id: string; sort_order: number; content: string }>(
|
|
`SELECT b.document_exhibit_id,b.sort_order,b.content FROM osint.document_content_blocks b
|
|
JOIN osint.exhibits e ON e.id=b.document_exhibit_id WHERE e.board_id=$1 ORDER BY b.sort_order`, [sourceBoardId])
|
|
for (const row of blocks.rows) await client.query(
|
|
'INSERT INTO osint.document_content_blocks (id,document_exhibit_id,sort_order,content) VALUES ($1,$2,$3,$4)',
|
|
[randomUUID(), mapped(exhibitIds, row.document_exhibit_id, 'content document'), row.sort_order, row.content])
|
|
|
|
const regions = await client.query<{
|
|
id: string; document_exhibit_id: string; region_key: string; label: string; excerpt: string; occurred_at: Date | null; sort_order: number
|
|
}>(`SELECT r.* FROM osint.document_regions r JOIN osint.exhibits e ON e.id=r.document_exhibit_id
|
|
WHERE e.board_id=$1 ORDER BY r.sort_order`, [sourceBoardId])
|
|
for (const row of regions.rows) {
|
|
const id = randomUUID(); regionIds.set(row.id, id)
|
|
await client.query(`INSERT INTO osint.document_regions
|
|
(id,document_exhibit_id,region_key,label,excerpt,occurred_at,sort_order) VALUES ($1,$2,$3,$4,$5,$6,$7)`,
|
|
[id, mapped(exhibitIds, row.document_exhibit_id, 'region document'), row.region_key, row.label, row.excerpt, row.occurred_at, row.sort_order])
|
|
}
|
|
|
|
const fields = await client.query<{ id: string; field_key: string; label: string; value_type: string }>(
|
|
'SELECT id,field_key,label,value_type FROM osint.metadata_fields WHERE board_id=$1 ORDER BY field_key', [sourceBoardId])
|
|
for (const row of fields.rows) {
|
|
const id = randomUUID(); fieldIds.set(row.id, id)
|
|
await client.query('INSERT INTO osint.metadata_fields (id,board_id,field_key,label,value_type) VALUES ($1,$2,$3,$4,$5)',
|
|
[id, targetBoardId, row.field_key, row.label, row.value_type])
|
|
}
|
|
for (const [table, cast] of [
|
|
['exhibit_metadata_text_values', 'text'], ['exhibit_metadata_timestamp_values', 'timestamptz'],
|
|
['exhibit_metadata_number_values', 'numeric'], ['exhibit_metadata_boolean_values', 'boolean'],
|
|
] as const) {
|
|
const values = await client.query<{ exhibit_id: string; field_id: string; value: unknown }>(
|
|
`SELECT v.exhibit_id,v.field_id,v.value::${cast} AS value FROM osint.${table} v
|
|
JOIN osint.exhibits e ON e.id=v.exhibit_id WHERE e.board_id=$1`, [sourceBoardId])
|
|
for (const row of values.rows) await client.query(`INSERT INTO osint.${table} (exhibit_id,field_id,value) VALUES ($1,$2,$3)`,
|
|
[mapped(exhibitIds, row.exhibit_id, 'metadata exhibit'), mapped(fieldIds, row.field_id, 'metadata field'), row.value])
|
|
}
|
|
|
|
const memberships = await client.query<{ folder_exhibit_id: string; child_exhibit_id: string; sort_order: number }>(
|
|
'SELECT folder_exhibit_id,child_exhibit_id,sort_order FROM osint.folder_memberships WHERE board_id=$1', [sourceBoardId])
|
|
for (const row of memberships.rows) await client.query(`INSERT INTO osint.folder_memberships
|
|
(board_id,folder_exhibit_id,child_exhibit_id,sort_order) VALUES ($1,$2,$3,$4)`,
|
|
[targetBoardId, mapped(exhibitIds, row.folder_exhibit_id, 'membership folder'), mapped(exhibitIds, row.child_exhibit_id, 'membership child'), row.sort_order])
|
|
|
|
const eventEvidence = await client.query<{ event_exhibit_id: string; evidence_exhibit_id: string; sort_order: number; note: string | null }>(
|
|
'SELECT event_exhibit_id,evidence_exhibit_id,sort_order,note FROM osint.event_evidence WHERE board_id=$1', [sourceBoardId])
|
|
for (const row of eventEvidence.rows) await client.query(`INSERT INTO osint.event_evidence
|
|
(board_id,event_exhibit_id,evidence_exhibit_id,sort_order,note) VALUES ($1,$2,$3,$4,$5)`,
|
|
[targetBoardId, mapped(exhibitIds, row.event_exhibit_id, 'event'), mapped(exhibitIds, row.evidence_exhibit_id, 'event evidence'), row.sort_order, row.note])
|
|
|
|
const partyEvidence = await client.query<{ party_exhibit_id: string; evidence_exhibit_id: string; sort_order: number; note: string | null }>(
|
|
'SELECT party_exhibit_id,evidence_exhibit_id,sort_order,note FROM osint.party_evidence WHERE board_id=$1', [sourceBoardId])
|
|
for (const row of partyEvidence.rows) await client.query(`INSERT INTO osint.party_evidence
|
|
(board_id,party_exhibit_id,evidence_exhibit_id,sort_order,note) VALUES ($1,$2,$3,$4,$5)`,
|
|
[targetBoardId, mapped(exhibitIds, row.party_exhibit_id, 'party'), mapped(exhibitIds, row.evidence_exhibit_id, 'party evidence'), row.sort_order, row.note])
|
|
const partyRelationships = await client.query<{ relationship_type_id: string; from_party_exhibit_id: string; to_party_exhibit_id: string; note: string | null }>(
|
|
'SELECT relationship_type_id,from_party_exhibit_id,to_party_exhibit_id,note FROM osint.party_relationships WHERE board_id=$1', [sourceBoardId])
|
|
for (const row of partyRelationships.rows) await client.query(`INSERT INTO osint.party_relationships
|
|
(id,board_id,relationship_type_id,from_party_exhibit_id,to_party_exhibit_id,note) VALUES ($1,$2,$3,$4,$5,$6)`,
|
|
[randomUUID(), targetBoardId, row.relationship_type_id, mapped(exhibitIds, row.from_party_exhibit_id, 'related party'),
|
|
mapped(exhibitIds, row.to_party_exhibit_id, 'related party'), row.note])
|
|
|
|
const connections = await client.query<{ connection_type_id: string; from_exhibit_id: string; to_exhibit_id: string; label: string | null; tightness: number; tag_style: string; tag_position_percent: number; tag_lateral_offset: number }>(
|
|
'SELECT connection_type_id,from_exhibit_id,to_exhibit_id,label,tightness,tag_style,tag_position_percent,tag_lateral_offset FROM osint.exhibit_connections WHERE board_id=$1', [sourceBoardId])
|
|
for (const row of connections.rows) await client.query(`INSERT INTO osint.exhibit_connections
|
|
(id,board_id,connection_type_id,from_exhibit_id,to_exhibit_id,label,tightness,tag_style,tag_position_percent,tag_lateral_offset) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10)`,
|
|
[randomUUID(), targetBoardId, row.connection_type_id, mapped(exhibitIds, row.from_exhibit_id, 'connection source'), mapped(exhibitIds, row.to_exhibit_id, 'connection target'), row.label, row.tightness, row.tag_style, row.tag_position_percent, row.tag_lateral_offset])
|
|
|
|
const sources = await client.query<{ exhibit_id: string; source_document_exhibit_id: string; source_region_id: string | null }>(
|
|
`SELECT s.exhibit_id,s.source_document_exhibit_id,s.source_region_id FROM osint.exhibit_sources s
|
|
JOIN osint.exhibits e ON e.id=s.exhibit_id WHERE e.board_id=$1`, [sourceBoardId])
|
|
for (const row of sources.rows) await client.query(`INSERT INTO osint.exhibit_sources
|
|
(exhibit_id,source_document_exhibit_id,source_region_id) VALUES ($1,$2,$3)`,
|
|
[mapped(exhibitIds, row.exhibit_id, 'sourced exhibit'), mapped(exhibitIds, row.source_document_exhibit_id, 'source document'),
|
|
row.source_region_id ? mapped(regionIds, row.source_region_id, 'source region') : null])
|
|
|
|
const brief = await client.query<{ body: string }>('SELECT body FROM osint.level_briefs WHERE board_id=$1', [sourceBoardId])
|
|
if (brief.rows[0]) await client.query('INSERT INTO osint.level_briefs (board_id,body) VALUES ($1,$2)', [targetBoardId, brief.rows[0].body])
|
|
const concepts = await client.query<{
|
|
id: string; label: string; context_text: string; sort_order: number; expected_party_kind: string | null; resolved_party_exhibit_id: string | null
|
|
}>('SELECT id,label,context_text,sort_order,expected_party_kind,resolved_party_exhibit_id FROM osint.brief_concepts WHERE board_id=$1 ORDER BY sort_order,id', [sourceBoardId])
|
|
for (const row of concepts.rows) await client.query(`INSERT INTO osint.brief_concepts
|
|
(id,board_id,origin_concept_id,label,context_text,sort_order,expected_party_kind,resolved_party_exhibit_id) VALUES ($1,$2,$3,$4,$5,$6,$7,$8)`,
|
|
[randomUUID(), targetBoardId, row.id, row.label, row.context_text, row.sort_order, row.expected_party_kind,
|
|
row.resolved_party_exhibit_id ? mapped(exhibitIds, row.resolved_party_exhibit_id, 'resolved party') : null])
|
|
|
|
const report = (await client.query<{ title:string;required_for_completion:boolean }>(
|
|
'SELECT title,required_for_completion FROM osint.case_reports WHERE board_id=$1',[sourceBoardId])).rows[0]
|
|
if (report) await client.query(`INSERT INTO osint.case_reports (board_id,title,investigator_name,required_for_completion)
|
|
VALUES ($1,$2,'',$3)`,[targetBoardId,report.title,report.required_for_completion])
|
|
|
|
return exhibitIds
|
|
}
|