diff --git a/app/api/job/test-medipost-responses/route.ts b/app/api/job/test-medipost-responses/route.ts new file mode 100644 index 0000000..2cf8fa7 --- /dev/null +++ b/app/api/job/test-medipost-responses/route.ts @@ -0,0 +1,54 @@ +import { NextRequest, NextResponse } from "next/server"; +import { getAnalysisOrdersAdmin } from "~/lib/services/order.service"; +import { composeOrderTestResponseXML, sendPrivateMessageTestResponse } from "~/lib/services/medipostTest.service"; +import { retrieveOrder } from "@lib/data"; +import { getAccountAdmin } from "~/lib/services/account.service"; +import { getOrderedAnalysisElementsIds } from "~/lib/services/medipost.service"; +import loadEnv from "../handler/load-env"; +import validateApiKey from "../handler/validate-api-key"; + +export async function POST(request: NextRequest) { + loadEnv(); + + try { + validateApiKey(request); + } catch (e) { + return NextResponse.json({}, { status: 401, statusText: 'Unauthorized' }); + } + + const analysisOrders = await getAnalysisOrdersAdmin({ orderStatus: 'QUEUED' }); + + console.error(`Sending test responses for ${analysisOrders.length} analysis orders`); + for (const medreportOrder of analysisOrders) { + const medusaOrderId = medreportOrder.medusa_order_id; + const medusaOrder = await retrieveOrder(medusaOrderId) + + const account = await getAccountAdmin({ primaryOwnerUserId: medreportOrder.user_id }); + const orderedAnalysisElementsIds = await getOrderedAnalysisElementsIds({ medusaOrder }); + + console.info(`Sending test response for order=${medusaOrderId} with ${orderedAnalysisElementsIds.length} ordered analysis elements`); + const idsToSend = orderedAnalysisElementsIds; + const messageXml = await composeOrderTestResponseXML({ + person: { + idCode: account.personal_code!, + firstName: account.name ?? '', + lastName: account.last_name ?? '', + phone: account.phone ?? '', + }, + orderedAnalysisElementsIds: idsToSend.map(({ analysisElementId }) => analysisElementId), + orderedAnalysesIds: [], + orderId: medusaOrderId, + orderCreatedAt: new Date(medreportOrder.created_at), + }); + + console.info("SEND XML", messageXml); + + try { + await sendPrivateMessageTestResponse({ messageXml }); + } catch (error) { + console.error("Error sending private message test response: ", error); + } + } + + return NextResponse.json({ success: true }); +} diff --git a/app/home/(user)/(dashboard)/analysis-results/_components/analysis-level-bar.tsx b/app/home/(user)/(dashboard)/analysis-results/_components/analysis-level-bar.tsx index c092fe5..66ceeac 100644 --- a/app/home/(user)/(dashboard)/analysis-results/_components/analysis-level-bar.tsx +++ b/app/home/(user)/(dashboard)/analysis-results/_components/analysis-level-bar.tsx @@ -1,8 +1,9 @@ -import React from 'react'; +import React, { useMemo } from 'react'; import { ArrowDown } from 'lucide-react'; import { cn } from '@kit/ui/utils'; +import { UserAnalysisElement } from '@/packages/features/accounts/src/types/accounts'; export enum AnalysisResultLevel { VERY_LOW = 0, @@ -17,11 +18,13 @@ const Level = ({ color, isFirst = false, isLast = false, + arrowLocation, }: { isActive?: boolean; color: 'destructive' | 'success' | 'warning' | 'gray-200'; isFirst?: boolean; isLast?: boolean; + arrowLocation?: number; }) => { return (
{isActive && ( -
+
)} @@ -52,11 +58,33 @@ const AnalysisLevelBar = ({ normLowerIncluded = true, normUpperIncluded = true, level, + results, }: { normLowerIncluded?: boolean; normUpperIncluded?: boolean; level: AnalysisResultLevel; + results: UserAnalysisElement; }) => { + + const { norm_lower: lower, norm_upper: upper, response_value: value } = results; + const arrowLocation = useMemo(() => { + if (value < lower!) { + return 0; + } + + if (normLowerIncluded || normUpperIncluded) { + return 50; + } + + const calculated = ((value - lower!) / (upper! - lower!)) * 100; + + if (calculated > 100) { + return 100; + } + + return calculated; + }, [value, upper, lower]); + return (
{normLowerIncluded && ( @@ -73,8 +101,9 @@ const AnalysisLevelBar = ({ {normUpperIncluded && ( diff --git a/app/home/(user)/(dashboard)/analysis-results/_components/analysis.tsx b/app/home/(user)/(dashboard)/analysis-results/_components/analysis.tsx index 85d52e2..91af308 100644 --- a/app/home/(user)/(dashboard)/analysis-results/_components/analysis.tsx +++ b/app/home/(user)/(dashboard)/analysis-results/_components/analysis.tsx @@ -1,6 +1,6 @@ 'use client'; -import React, { useState } from 'react'; +import React, { useMemo, useState } from 'react'; import { UserAnalysisElement } from '@/packages/features/accounts/src/types/accounts'; import { format } from 'date-fns'; @@ -39,11 +39,12 @@ const Analysis = ({ const normUpper = results?.norm_upper || 0; const [showTooltip, setShowTooltip] = useState(false); - const isUnderNorm = value < normLower; - const getAnalysisResultLevel = () => { + const analysisResultLevel = useMemo(() => { if (!results) { return null; } + + const isUnderNorm = value < normLower; if (isUnderNorm) { switch (status) { case AnalysisStatus.MEDIUM: @@ -60,7 +61,7 @@ const Analysis = ({ default: return AnalysisResultLevel.NORMAL; } - }; + }, [results, value, normLower]); return (
@@ -99,9 +100,10 @@ const Analysis = ({
) : ( diff --git a/app/home/(user)/(dashboard)/analysis-results/page.tsx b/app/home/(user)/(dashboard)/analysis-results/page.tsx index d31cce5..27041df 100644 --- a/app/home/(user)/(dashboard)/analysis-results/page.tsx +++ b/app/home/(user)/(dashboard)/analysis-results/page.tsx @@ -82,6 +82,7 @@ async function AnalysisResultsPage() {
{analysisOrders.length > 0 && analysisElements.length > 0 ? analysisOrders.map((analysisOrder) => { + const analysisResponse = analysisResponses?.find((response) => response.analysis_order_id === analysisOrder.id); const analysisElementIds = getAnalysisElementIds([analysisOrder]); const analysisElementsForOrder = analysisElements.filter((element) => analysisElementIds.includes(element.id)); return ( @@ -98,7 +99,8 @@ async function AnalysisResultsPage() {
{analysisElementsForOrder.length > 0 ? analysisElementsForOrder.map((analysisElement) => { - const results = analysisResponseElements?.find((element) => element.analysis_element_original_id === analysisElement.analysis_id_original); + const results = analysisResponse?.elements.some((element) => element.analysis_element_original_id === analysisElement.analysis_id_original) + && analysisResponseElements?.find((element) => element.analysis_element_original_id === analysisElement.analysis_id_original); if (!results) { return ( diff --git a/lib/services/medipost.service.ts b/lib/services/medipost.service.ts index 4490c0b..8508663 100644 --- a/lib/services/medipost.service.ts +++ b/lib/services/medipost.service.ts @@ -218,12 +218,9 @@ export async function readPrivateMessageResponse({ privateMessage.messageId, ); const messageResponse = privateMessageContent?.Saadetis?.Vastus; - const medusaOrderId = privateMessageContent?.Saadetis?.Tellimus?.ValisTellimuseId; + const medusaOrderId = privateMessageContent?.Saadetis?.Tellimus?.ValisTellimuseId || messageResponse?.ValisTellimuseId; if (!messageResponse) { - if (medusaOrderId === 'order_01K2JSJXR5XVNRWEAGB199RCKP') { - console.info("messageResponse", JSON.stringify(privateMessageContent, null, 2)); - } throw new Error(`Private message response has no results yet for order=${medusaOrderId}`); } diff --git a/lib/services/medipostTest.service.ts b/lib/services/medipostTest.service.ts index 192db4c..1a3b0d9 100644 --- a/lib/services/medipostTest.service.ts +++ b/lib/services/medipostTest.service.ts @@ -90,7 +90,7 @@ export async function composeOrderTestResponseXML({ // 1 – Järjekorras, 2 – Ootel, 3 - Töös, 4 – Lõpetatud, // 5 – Tagasi lükatud, 6 – Tühistatud. const orderStatus = 4; - const orderNumber = 'TSU000001200'; + const orderNumber = orderId; const allAnalysisElementsForGroups = analysisElements?.filter((element) => { return analysisGroups.some((group) => group.id === element.analysis_groups.id); @@ -153,7 +153,7 @@ export async function composeOrderTestResponseXML({ const lower = getRandomInt(0, 100); const upper = getRandomInt(lower + 1, 500); - const result = getRandomInt(lower, upper); + const result = getRandomInt(lower - Math.floor(lower * 0.1), upper + Math.floor(upper * 0.1)); addedIds.add(relatedAnalysisElement.id); return (` @@ -175,7 +175,7 @@ export async function composeOrderTestResponseXML({ ${formatDate(new Date(), 'yyyy-MM-dd HH:mm:ss')} ${upper} ${lower} - 0 + ${result < lower ? 1 : (result > upper ? 1 : 0)} 1 diff --git a/lib/services/order.service.ts b/lib/services/order.service.ts index 3725e5c..998be03 100644 --- a/lib/services/order.service.ts +++ b/lib/services/order.service.ts @@ -108,7 +108,33 @@ export async function getAnalysisOrders({ }: { orderStatus?: Tables<{ schema: 'medreport' }, 'analysis_orders'>['status']; } = {}) { - const query = getSupabaseServerClient() + const client = getSupabaseServerClient(); + + const { + data: { user }, + } = await client.auth.getUser(); + if (!user) { + throw new Error('Unauthorized'); + } + + const query = client + .schema('medreport') + .from('analysis_orders') + .select('*') + .eq("user_id", user.id) + if (orderStatus) { + query.eq('status', orderStatus); + } + const orders = await query.order('created_at', { ascending: false }).throwOnError(); + return orders.data; +} + +export async function getAnalysisOrdersAdmin({ + orderStatus, +}: { + orderStatus?: Tables<{ schema: 'medreport' }, 'analysis_orders'>['status']; +} = {}) { + const query = getSupabaseServerAdminClient() .schema('medreport') .from('analysis_orders') .select('*') diff --git a/lib/types/medipost.ts b/lib/types/medipost.ts index b47d985..583fc33 100644 --- a/lib/types/medipost.ts +++ b/lib/types/medipost.ts @@ -261,7 +261,7 @@ export const AnalysisOrderStatus = { 6: 'CANCELLED', } as const; export const NormStatus: Record = { - 1: 'NORMAL', - 2: 'WARNING', - 3: 'REQUIRES_ATTENTION', + 0: 'NORMAL', + 1: 'WARNING', + 2: 'REQUIRES_ATTENTION', } as const; diff --git a/schedule-setup/setup_send_analysis_test_results_cron.sql b/schedule-setup/setup_send_analysis_test_results_cron.sql new file mode 100644 index 0000000..2562939 --- /dev/null +++ b/schedule-setup/setup_send_analysis_test_results_cron.sql @@ -0,0 +1,19 @@ +-- Enable required extensions for cron jobs and HTTP requests +create extension if not exists pg_cron; +create extension if not exists pg_net; + +-- Schedule the test-medipost-responses job to run every 15 minutes +select + cron.schedule( + 'send-test-medipost-responses-every-15-minutes', -- Unique job name + '*/15 * * * *', -- Cron schedule: every 15 minutes + $$ + select + net.http_post( + url := 'https://test.medreport.ee/api/job/test-medipost-responses', + headers := jsonb_build_object( + 'x-jobs-api-key', 'fd26ec26-70ed-11f0-9e95-431ac3b15a84' + ) + ) as request_id; + $$ + ); diff --git a/schedule-setup/setup_sync_analysis_results_cron.sql b/schedule-setup/setup_sync_analysis_results_cron.sql new file mode 100644 index 0000000..832f982 --- /dev/null +++ b/schedule-setup/setup_sync_analysis_results_cron.sql @@ -0,0 +1,19 @@ +-- Enable required extensions for cron jobs and HTTP requests +create extension if not exists pg_cron; +create extension if not exists pg_net; + +-- Schedule the sync-analysis-results job to run every 15 minutes +select + cron.schedule( + 'sync-analysis-results-every-15-minutes', -- Unique job name + '*/15 * * * *', -- Cron schedule: every 15 minutes + $$ + select + net.http_post( + url := 'https://test.medreport.ee/api/job/sync-analysis-results', + headers := jsonb_build_object( + 'x-jobs-api-key', 'fd26ec26-70ed-11f0-9e95-431ac3b15a84' + ) + ) as request_id; + $$ + );