# Как импортировать лабораторные результаты в запись FHIR

> Учебное пособие: импортируйте лабораторный отчет в формате CSV и HL7 v2 ORU^R01 в FHIR R4 Observations с кодами LOINC и референсными диапазонами, обрабатывайте некартированные маркеры и повторно отправленные отчеты, а также отслеживайте каждый результат в его лаборатории.

Source: https://developers.anpheros.com/ru/guides/import-lab-results-fhir

**Это учебное пособие рассматривает лабораторный отчет в виде файла CSV и сообщения HL7 v2 ORU^R01, импортирует его в запись HL7 FHIR R4 пациента по одному запросу для каждого, и показывает, что вы получаете в ответ: ресурсы `Observation` лаборатории с кодами LOINC, референсными диапазонами и происхождением.** Оно также охватывает две проблемы, с которыми сталкивается каждая интеграция лаборатории: маркеры, которые не могут быть автоматически сопоставлены, и отчеты, которые отправляются дважды. Все работает в бесплатной песочнице с синтетическим пациентом.

## Почему результаты лабораторных исследований сложно хранить

Лабораторный отчет выглядит просто: маркер, значение, единица измерения, референсный диапазон, дата. На практике каждая лаборатория называет маркеры по-своему ("Hemoglobina glicozilata", "HbA1c", "Glycated haemoglobin"), использует запятые или точки для десятичных знаков, записывает диапазоны как `4,0-5,6`, `<5` или `>3.5` и повторно отправляет один и тот же отчет при возникновении сбоев. При хранении в виде свободного текста эти результаты невозможно сравнивать с течением времени, строить графики, передавать другому приложению или с уверенностью передавать модели искусственного интеллекта.

Запись FHIR хранит каждый результат как `Observation` с кодом LOINC, единицей UCUM, значением, референсным диапазоном и датой, а также сохраняет информацию о том, кто отправил результат, в ресурсе `Provenance`. [Коннектор для лабораторных исследований](https://developers.anpheros.com/ru/guides/lab-connector) выполняет это преобразование для вас.

```
 lab report (CSV, HL7 v2, JSON)
        │  POST /v1/patients/{id}/labs/import
        ▼
 ┌──────────────────────────────────────────────┐
 │ parse rows → map marker to LOINC → parse     │
 │ value, unit, range → stable id per row       │
 └──────────────────────────────────────────────┘
        │
        ▼
 Observation (category laboratory) + Provenance (author_type import, source system, report id)
        │
        ├─► the patient's app and every app they consented to
        └─► webhooks: resource.created per Observation
```

## Перед началом работы

Вам потребуется sandbox-ключ (`sk_test_…`) с [platform.anpheros.com/dashboard](https://platform.anpheros.com/dashboard/) (**Получить sandbox-ключ**). В примерах используется `curl`; [SDK](https://developers.anpheros.com/ru/guides/sdks) имеют те же вызовы (`labs.importCsv`, `labs.importHl7`, `labs.importItems`).

```bash
export BASE=https://platform.anpheros.com
export KEY=sk_test_…
# Elena Ionescu, a synthetic patient with type 2 diabetes
export PID=$(curl -s "$BASE/fhir/R4/Patient?identifier=https://anpheros.com/fhir/sid/sandbox-persona|p02" \
  -H "Authorization: Bearer $KEY" | jq -r '.entry[0].resource.id')
```

## Шаг 1: импорт CSV-отчета

Вот как румынская лаборатория может экспортировать отчет: точки с запятой, десятичные запятые, румынские названия столбцов и даты, как в `DD.MM.YYYY`. Коннектор распознает румынские и английские названия столбцов.

```bash
cat > report.csv <<'EOF'
Analiza;Rezultat;UM;Interval de referinta;Data
Hemoglobina glicozilata (HbA1c);6,9;%;4,0-5,6;01.10.2026
Glicemie;131;mg/dL;70-100;01.10.2026
TSH;2,1;mUI/L;0,4-4,0;01.10.2026
Marker foarte rar;12;U/L;0-20;01.10.2026
EOF

curl -X POST "$BASE/v1/patients/$PID/labs/import" -H "Authorization: Bearer $KEY" \
     -H "Content-Type: text/csv" -H "X-Anpheros-Source-System: labx" \
     -H "Idempotency-Key: report-2026-10-01-elena" --data-binary @report.csv
```

Ответ (`201`):

```json
{"created": 4, "skipped": 0, "unmapped": ["Marker foarte rar"], "warnings": [],
 "observations": [
  {"id": "ff0bbb44-…", "marker": "Hemoglobina glicozilata (HbA1c)", "code": "4548-4", "system": "http://loinc.org", "value": 6.9, "unit": "%", "date": "2026-10-01"},
  {"id": "ebe5522f-…", "marker": "Glicemie", "code": "2345-7", "system": "http://loinc.org", "value": 131.0, "unit": "mg/dL", "date": "2026-10-01"},
  {"id": "bace44a8-…", "marker": "TSH", "code": "3016-3", "system": "http://loinc.org", "value": 2.1, "unit": "mUI/L", "date": "2026-10-01"},
  {"id": "26fd2270-…", "marker": "Marker foarte rar", "code": "marker_foarte_rar", "system": "urn:anpheros:lab-code", "value": 12.0, "unit": "U/L", "date": "2026-10-01"}]}
```

HbA1c, глюкоза и TSH были сопоставлены с LOINC по их названиям. Десятичные запятые стали числами, а диапазоны стали `referenceRange`. Одна строка вернулась в `unmapped`. Она по-прежнему хранится под собственным кодом лаборатории (`urn:anpheros:lab-code`) с точным текстом из отчета, поэтому ничего не теряется.

## Шаг 2: Обработка несовпавших маркеров

Некоторые названия требуют принятия решения. "Glicemie" (уровень глюкозы в крови) — хороший пример: LOINC имеет один код для глюкозы в сыворотке или плазме (`2345-7`, венозный образец, взятый в лаборатории) и другой для глюкозы в цельной крови (`2339-0`, например, при проколе пальца). В лабораторном отчете простое "Glicemie" сопоставляется с сывороткой или плазмой. Название, содержащее "capilar", "capillary" или "sânge integral", сопоставляется с цельной кровью:

```json
{"created": 1, "skipped": 0, "unmapped": [], "warnings": [],
 "observations": [{"id": "00480a6d-…", "marker": "Glicemie capilara", "code": "2339-0", "system": "http://loinc.org", "value": 140.0, "unit": "mg/dL", "date": "2026-10-02"}]}
```

Для маркеров, которые коннектор не распознает, найдите код:

```bash
curl "$BASE/v1/terminology/loinc?q=glucose" -H "Authorization: Bearer $KEY"
```

```json
{"data": [
  {"system": "http://loinc.org", "code": "2339-0", "display": "Glucose [Mass/volume] in Blood", "display_ro": "Glicemie (sânge)", "category": "laboratory", "unit": "mg/dL"},
  {"system": "http://loinc.org", "code": "2345-7", "display": "Glucose [Mass/volume] in Serum or Plasma", "display_ro": "Glicemie (ser)", "category": "laboratory", "unit": "mg/dL"}],
 "total": 2}
```

Затем отправьте код в столбце `LOINC`. Код в отчете всегда имеет приоритет над названием. Сохраняйте это сопоставление для каждой лаборатории на своей стороне и добавляйте этот столбец в каждый отчет от нее:

```
Analiza;Rezultat;UM;Interval de referinta;Data;LOINC
Glicemie;99;mg/dL;70-100;04.10.2026;2339-0
```

Маркеры, которые LOINC вообще не охватывает, например, вымышленный "Marker foarte rar", могут оставаться под кодом лаборатории. Они отображаются пациенту и включаются в запись. Их нельзя сравнивать с результатами других лабораторий.

## Шаг 3: отправка одного и того же отчета дважды

Лаборатории повторно отправляют отчеты. Каждая строка получает стабильный идентификатор, построенный из идентификатора отчета, кода, даты и значения, поэтому второй импорт того же отчета ничего не создает:

```json
{"created": 0, "skipped": 4, "unmapped": ["Marker foarte rar"], "warnings": [], "observations": []}
```

Строки, которые изменились, например, исправленное значение, импортируются как новые результаты. Заголовок `Idempotency-Key` добавляет вторую гарантию: сетевая повторная попытка с тем же ключом возвращает первый ответ без изменений.

## Шаг 4: импорт сообщения HL7 v2 ORU^R01

Большинство лабораторных информационных систем используют HL7 v2. Отправьте сообщение как есть (текст ER7, сегменты разделены CR или LF):

```
MSH|^~\&|LABX|LAB1|ANPHEROS|EHR|202610010930||ORU^R01|MSG00077|P|2.5.1
PID|1||12345||Ionescu^Elena||19850903|F
OBR|1||R-77|24331-1^Lipid panel^LN|||202610010800
OBX|1|NM|2093-3^Cholesterol [Mass/volume] in Serum or Plasma^LN||212|mg/dL|<200|H|||F|||202610010800
OBX|2|NM|2085-9^HDL Cholesterol^LN||52|mg/dL|>40|N|||F|||202610010800
```

```bash
curl -X POST "$BASE/v1/patients/$PID/labs/import" -H "Authorization: Bearer $KEY" \
     -H "Content-Type: x-application/hl7-v2+er7" --data-binary @oru.hl7
```

```json
{"created": 2, "skipped": 0, "unmapped": [], "warnings": [],
 "observations": [
  {"id": "b562045b-…", "marker": "Cholesterol [Mass/volume] in Serum or Plasma", "code": "2093-3", "system": "http://loinc.org", "value": 212.0, "unit": "mg/dL", "date": "2026-10-01T08:00:00Z"},
  {"id": "9b3274e4-…", "marker": "HDL Cholesterol", "code": "2085-9", "system": "http://loinc.org", "value": 52.0, "unit": "mg/dL", "date": "2026-10-01T08:00:00Z"}]}
```

`OBX-3` с системой кодирования `LN` используется напрямую как LOINC. `OBX-7` становится референсным диапазоном, а `OBX-8` (`H`, `N`) — интерпретацией. `OBR-3` (`R-77`) сохраняется как идентификатор отчета в provenance. Пациент — это тот, кто указан в URL: сегмент `PID` не используется для поиска пациента, поэтому неправильный `PID` не может прикрепить результаты к другому человеку.

Уже есть разобранные результаты? Отправьте JSON: `{"date": "2026-10-01", "origin": "report-77", "items": [{"marker": "Ferritin", "value": 38, "unit": "ng/mL", "ref_low": 15, "ref_high": 150}]}`. Ферритин сопоставляется с LOINC `2276-4`.

## Шаг 5: чтение результатов в формате FHIR

```bash
curl "$BASE/fhir/R4/Observation?patient=$PID&code=http://loinc.org|4548-4&_sort=-date&_count=1" \
     -H "Authorization: Bearer $KEY"
```

Импортированный HbA1c, как он хранится:

```json
{
  "resourceType": "Observation",
  "id": "62b1b0c0-…",
  "meta": {"versionId": "1", "lastUpdated": "2026-10-12T20:05:57.011Z"},
  "identifier": [{"system": "urn:anpheros:lab-import", "value": "c52d3cd6449291d34f6bef1516d0ac05"}],
  "status": "final",
  "category": [{"coding": [{"system": "http://terminology.hl7.org/CodeSystem/observation-category", "code": "laboratory"}]}],
  "code": {"coding": [{"system": "http://loinc.org", "code": "4548-4", "display": "Hemoglobin A1c/Hemoglobin.total in Blood"}],
           "text": "Hemoglobina glicozilata (HbA1c)"},
  "subject": {"reference": "Patient/…"},
  "effectiveDateTime": "2026-10-01",
  "valueQuantity": {"value": 6.9, "unit": "%"},
  "referenceRange": [{"low": {"value": 4.0, "unit": "%"}, "high": {"value": 5.6, "unit": "%"}}]
}
```

LOINC display — это стандартное английское название. `code.text` сохраняет собственную формулировку лаборатории, поэтому пациент по-прежнему видит то, что было в бумажном отчете. Дополнительную информацию о ресурсах и поиске см. в руководстве по [платформе FHIR](https://developers.anpheros.com/ru/guides/fhir) и [кодовым системам FHIR](https://developers.anpheros.com/ru/guides/fhir-code-systems).

## Шаг 6: проверка отправителя

```bash
curl "$BASE/v1/patients/$PID/provenance/Observation/62b1b0c0-…" -H "Authorization: Bearer $KEY"
```

```json
{"data": [{"version": 1, "activity": "CREATE", "recorded": "2026-10-12T20:05:57.011000Z",
           "author_type": "import", "author": "proj_…", "source_system": "labx", "origin_id": null,
           "fhir": "Provenance/f15c4fee-…"}], "total": 1}
```

`author_type: import` и `source_system: labx` остаются со значением. Когда ИИ-приложение позже прочитает этот результат через [context API](https://developers.anpheros.com/ru/guides/llm-healthcare-data), он будет помечен как `[import/labx]`, чтобы модель могла отличить лабораторный результат от значения, введенного пациентом.

## Что происходит дальше

- Пациент видит результаты в своем приложении. Любое приложение, на доступ к которому он дал согласие, может их прочитать в пределах своих областей действия ([модель согласия и доступа](https://developers.anpheros.com/ru/guides/consent)).
- Для каждого Observation срабатывает [вебхук](https://developers.anpheros.com/ru/guides/webhooks) `resource.created`, чтобы панель управления клиники или сводка ИИ могли обновляться без опроса.
- `POST /fhir/R4/Observation/$validate?profile=eu-lab` сообщает, как результат соотносится с профилем лабораторного отчета HL7 Europe.
- До 500 строк на запрос; ограничения перечислены в разделе [Ограничения и лимиты скорости](https://developers.anpheros.com/ru/guides/limits).

## Часто задаваемые вопросы

### Какие форматы лабораторных отчетов можно импортировать в FHIR?
CSV с заголовком (с разделителями-запятыми, точками с запятой или табуляцией, допустима десятичная запятая), сообщения HL7 v2 ORU^R01 и JSON. Отчеты в формате PDF могут храниться как документы; извлеченные из них значения ссылаются на документ.

### Что происходит с маркерами без кода LOINC?
Они хранятся под собственным кодом лаборатории с точным текстом из отчета и перечислены в `unmapped`. Добавьте столбец LOINC для этой лаборатории, и они будут сопоставлены. Код в отчете всегда имеет приоритет над названием.

### Безопасно ли повторно отправлять отчет?
Да. Каждая строка имеет стабильный идентификатор, построенный из отчета, кода, даты и значения, поэтому уже импортированные строки пропускаются; измененные строки импортируются как новые результаты.

### Может ли лаборатория импортировать результаты для любого пациента?
Нет. Лаборатория импортирует данные для пациентов, созданных в ее проекте, или чьи записи были предоставлены ей человеком через OAuth. Другие пациенты для нее не существуют (`404`).

## Связанные материалы

- [API лабораторных результатов](https://developers.anpheros.com/ru/guides/lab-results-api): HL7 v2 и FHIR рядом, и что должен содержать результат
- [Коннектор для лабораторий](https://developers.anpheros.com/ru/guides/lab-connector): каждое имя столбца, поле HL7 и поле ответа
- [Интероперабельность медицинских данных](https://developers.anpheros.com/ru/guides/interoperability): почему коды и происхождение делают обмен данными пригодным для использования
- [Медицинские интеграции](https://developers.anpheros.com/ru/guides/healthcare-integrations): клиники, лаборатории, устройства и сторонние приложения
- [API медицинских данных](https://developers.anpheros.com/ru/guides/medical-data-api): запись отдельных наблюдений, состояний и медикаментов
- [Как создать приложение для ИИ в сфере здравоохранения с бэкендом FHIR](https://developers.anpheros.com/ru/guides/build-ai-healthcare-app): использование этих результатов в ответе ИИ
