Skip to content
Open
Show file tree
Hide file tree
Changes from 6 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
16 changes: 15 additions & 1 deletion app/airflow/dags/libs/queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@


find_existing_concepts_query = """
-- If the date_event is "death_date", mark this as a death table.
Comment thread
AndrewThien marked this conversation as resolved.
Outdated
UPDATE mapping_scanreporttable
SET death_table = TRUE
WHERE id = %(table_id)s
AND date_event_id IS NOT NULL
AND EXISTS (
SELECT 1
FROM mapping_scanreportfield f
WHERE f.id = mapping_scanreporttable.date_event_id
AND LOWER(TRIM(f.name)) = 'death_date'
);

INSERT INTO temp_existing_concepts_%(table_id)s (
object_id, sr_concept_id, source_concept_id, content_type_id
)
Expand Down Expand Up @@ -139,7 +151,9 @@
-- Because concepts may or may not have the standard_concept_id, in general. And we prefer to use the standard_concept_id, if it exists.
WHERE target_concept.concept_id = COALESCE(temp_existing_concepts.standard_concept_id, temp_existing_concepts.source_concept_id);

-- When the scan report table is a death table, override dest_table_id to the death OMOP table so rules show destination table: death, field: cause_concept_id
-- If the scan report table has the "death_table" flag set to TRUE, set the destination table to OMOP's "death" table.
-- This ensures that mapping rules will use "death" as the destination table and, for example, will select "cause_concept_id" as the mapped field.
-- Note: The "death_table" flag may have been set automatically if a date_event was mapped to the "death_date" field earlier in the workflow.
UPDATE temp_existing_concepts_%(table_id)s temp_existing_concepts
SET dest_table_id = (SELECT id FROM mapping_omoptable WHERE "table" = 'death' LIMIT 1)
WHERE (SELECT death_table FROM mapping_scanreporttable WHERE id = %(table_id)s) = TRUE;
Expand Down
1 change: 1 addition & 0 deletions app/next-client-app/api/scanreports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ export async function getScanReportTable(
permissions: [],
jobs: [],
trigger_reuse: true,
death_table: false,
};
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { ColumnDef } from "@tanstack/react-table";
import { DataTableColumnHeader } from "@/components/data-table/DataTableColumnHeader";
import { EditButton } from "@/components/scanreports/EditButton";
import { Tooltips } from "@/components/core/Tooltips";
import JobDialog from "@/components/jobs/JobDialog";
import { FindGeneralStatus, DivideJobs } from "@/components/jobs/JobUtils";
import Link from "next/link";
Expand Down Expand Up @@ -89,6 +90,28 @@ export const columns: ColumnDef<ScanReportTable>[] = [
);
}
},
{
id: "note",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="Note" />
),
cell: ({ row }) => {
const { death_table } = row.original;
return (
<div>
{death_table && (
<h3 className="flex">
{" "}
Death table
<Tooltips
content="This table is marked as a Death table. In the Carrot data standard, death data is typically provided in a separate file (e.g. death.csv) for mapping to the OMOP Death table."
Comment thread
AndrewThien marked this conversation as resolved.
/>
</h3>
)}
</div>
);
}
},
{
id: "edit",
header: ({ column }) => <DataTableColumnHeader column={column} title="" />,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,9 @@ export default async function ScanReportsValue(props: ScanReportsValueProps) {
id={id}
tableId={tableId}
fieldId={fieldId}
tableName={table.name}
tableName={
table.death_table ? `${table.name} (Death table)` : table.name
}
fieldName={field.name}
variant="field"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ export default async function ScanReportsEditField(props: ScanReportsEditFieldPr
id={id}
tableId={tableId}
fieldId={fieldId}
tableName={table.name}
tableName={
table.death_table ? `${table.name} (Death table)` : table.name
}
fieldName={field.name}
variant="fieldUpdate"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,11 @@ export default async function ScanReportsField(props: ScanReportsFieldProps) {
<TableBreadcrumbs
id={id}
tableId={tableId}
tableName={tableName.name}
tableName={
tableName.death_table
? `${tableName.name} (Death table)`
: tableName.name
}
variant="table"
/>
<div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export default async function UpdateTable(props: UpdateTableProps) {
<div>
<Link href={`/scanreports/${id}/tables/${tableId}`}>
<Button variant={"secondary"} className="mb-3">
Update Table: {table.name}
Update Table: {table.name} {table.death_table && "(Death table)"}
</Button>
</Link>
{(table.date_event === null || table.person_id === null) && (
Expand Down
2 changes: 1 addition & 1 deletion app/next-client-app/components/core/Tooltips.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,4 @@ export function Tooltips({
</Tooltip>
</TooltipProvider>
);
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,34 @@
"use client";

import { useState } from "react";
import { Button } from "@/components/ui/button";
import { updateScanReportTable } from "@/api/scanreports";
import { Save } from "lucide-react";
import { Check, Save, X } from "lucide-react";
import { toast } from "sonner";
import { FormDataFilter } from "../form-components/FormikUtils";
import { Formik } from "formik";
import { FormField, FormItem, FormLabel, FormControl, FormMessage, FormDescription } from "@/components/ui/form";
import { FormikSelect } from "../form-components/FormikSelect";
import { useRouter } from "next/navigation";
import { Checkbox } from "../ui/checkbox";
import { Switch } from "@/components/ui/switch";
import { Label } from "@/components/ui/label";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { Tooltips } from "@/components/core/Tooltips";
import { enableReuseTriggerOption } from "@/constants";

interface FormData {
personId: number | null;
dateEvent: number | null;
triggerReuse: boolean;
death_table: boolean;
}

export function ScanReportTableUpdateForm({
Expand All @@ -32,6 +45,7 @@ export function ScanReportTableUpdateForm({
dateEvent: ScanReportField;
}) {
const router = useRouter();
const [isDialogOpen, setIsDialogOpen] = useState(false);
const canUpdate =
permissions.includes("CanEdit") || permissions.includes("CanAdmin");

Expand All @@ -45,6 +59,7 @@ export function ScanReportTableUpdateForm({
person_id: data.personId !== 0 ? data.personId : null,
date_event: data.dateEvent !== 0 ? data.dateEvent : null,
trigger_reuse: data.triggerReuse,
death_table: data.death_table,
};

const response = await updateScanReportTable(
Expand All @@ -68,6 +83,7 @@ export function ScanReportTableUpdateForm({
dateEvent: initialDateEvent[0].value,
personId: initialPersonId[0].value,
triggerReuse: scanreportTable.trigger_reuse,
death_table: Boolean(scanreportTable.death_table),
}}
onSubmit={(data) => {
handleSubmit(data);
Expand Down Expand Up @@ -109,6 +125,83 @@ export function ScanReportTableUpdateForm({
</FormControl>
</FormItem>

<FormItem>
<div className="flex flex-wrap items-center gap-2">
<FormLabel className="mb-0">
Does this table contain only death data for the OMOP CDM Death
table?
</FormLabel>
<Tooltips
content="In the Carrot data standard, death data is provided in a separate file (e.g. death.csv). Mark Yes if this table contains that death data to be mapped to the OMOP Death table."
/>
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle>Please Confirm Your Choice</DialogTitle>
<DialogDescription>
Are you sure you want to set this table as a Death table?
Doing so will result in the following:
<ul className="text-gray-500 list-disc pl-4 py-2">
<li>
Mapping Rules that are created either manually or
automatically (built from OMOP vocabulary or Reused)
will have Destination table as{" "}
<span className="font-bold">Death</span>.
</li>
<li>
All concepts in this table will be recognised as{" "}
<span className="font-bold">Cause of Death</span> in
OMOP CDM.
</li>
<li>
Destination of Date Event will be{" "}
<span className="font-bold">Death date</span> field in
OMOP CDM.
</li>
</ul>
<p className="text-muted-foreground text-pretty">
You can turn off this setting later. Mapping rules will
be refreshed when you save.
</p>
</DialogDescription>
</DialogHeader>
<DialogFooter className="flex gap-3">
<Button
type="button"
onClick={() => setIsDialogOpen(false)}
variant="outline"
>
Cancel <X className="size-4 ml-2" />
</Button>
<Button
type="button"
onClick={() => {
setFieldValue("death_table", true);
setIsDialogOpen(false);
}}
>
Confirm <Check className="size-4 ml-2" />
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
<Switch
checked={values.death_table}
onCheckedChange={(checked) => {
if (checked) {
setIsDialogOpen(true);
} else {
setFieldValue("death_table", false);
}
}}
disabled={!canUpdate}
/>
<Label className="text-lg">
{values.death_table === true ? "YES" : "NO"}
</Label>
</div>
</FormItem>

{enableReuseTriggerOption === "true" && (
<FormField name="triggerReuse">
{({ field }) => (
Expand Down
1 change: 1 addition & 0 deletions app/next-client-app/types/scanreport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ interface ScanReportTable {
permissions: Permission[];
jobs: Job[];
trigger_reuse: boolean;
death_table?: boolean;
}

interface ScanReportField {
Expand Down
Loading