Лучший способ обработки / чтения этих файлов (медицинская форма претензии HCFA) - PullRequest
4 голосов
/ 19 января 2012

Я ищу несколько предложений о лучших подходах к обработке сценария с чтением файла в C #; конкретный сценарий - это то, с чем большинство людей не были бы знакомы, если бы вы не занимались здравоохранением, поэтому я сначала дам краткое объяснение.

Я работаю в плане медицинского обслуживания, и мы получаем жалобы от врачей несколькими способами (EDI, бумага и т. Д.). Бумажная форма для стандартных медицинских требований - это форма "HCFA" или "CMS 1500". Некоторые из наших врачей по контракту используют программное обеспечение, которое позволяет генерировать свои претензии и сохранять их в «макете» HCFA, но в текстовом файле (так что вы можете подумать, что это бумажная форма, но без фона / коробок / и т.д. ). Я приложил изображение фиктивного файла претензий, в котором показано, как это будет выглядеть.

Информация о претензии в настоящее время извлекается из текстовых файлов и преобразуется в XML. Весь процесс работает нормально, но я бы хотел сделать его лучше и проще в обслуживании. Существует одна серьезная проблема, которая относится к сценарию: каждый кабинет врача может предоставить нам эти текстовые файлы в несколько разных форматах. Это означает, что у доктора A может быть имя пациента в строке 10, начиная с символа 3, в то время как доктор B может отправить файл, имя которого начинается в строке 11 с символа 4, и так далее. Да, то, что мы должны делать, - это применение стандартной схемы, которой должны придерживаться все врачи, которые желают представить таким образом. Однако руководство заявило, что мы (разработчики) должны сами использовать разные возможности и что мы не можем просить их сделать что-то особенное, поскольку они хотят поддерживать хорошие отношения.

В настоящее время существует «таблица сопоставления», в которой по одному ряду для каждого кабинета врача. В таблице есть столбцы для каждого поля (например, имя пациента, идентификационный номер участника, дата рождения и т. Д.). Каждый из них получает значение на основе первого файла, который мы получили от доктора (мы вручную настроили карту). Таким образом, столбец PATIENT_NAME может быть определен в таблице сопоставления как «10,3,25», что означает, что имя начинается со строки 10, с символа 3, и может иметь длину до 25 символов. Это был болезненный процесс, как с точки зрения (а) создания карты для каждого врача - это утомительно, так и (б) ремонтопригодности, так как иногда они внезапно меняют свой макет, а затем нам приходится заново отображать все для этого врача .

Файл читается построчно, и каждая строка добавляется к

 List<string>

Как только это будет сделано, мы сделаем следующее, где мы получим данные карты, прочитаем список строк файла и получим значения полей (напомним, что каждое сопоставленное поле имеет значение типа «10,3,25» ( без кавычек)):

ClaimMap M = ClaimMap.GetMapForDoctor(17);

List<HCFA_Claim> ClaimSet = new List<HCFA_Claim>();

foreach (List<string> cl in Claims) //Claims is List<List<string>>, where we have a List<string> for each claim in the text file (it can have more than one, and the file is split up into separate claims earlier in the process)   
{
     HCFA_Claim c = new HCFA_Claim();
    c.Patient = new Patient();
    c.Patient.FullName = cl[Int32.Parse(M.Name.Split(',')[0]) - 1].Substring(Int32.Parse(M.Name.Split(',')[1]) - 1, Int32.Parse(M.Name.Split(',')[2])).Trim();
        //...and so on...       
     ClaimSet.Add(c);
}                

Извините, что это так долго ... но я чувствовал, что некоторые предпосылки / объяснения были необходимы. Есть ли лучшие / более творческие способы сделать что-то подобное?

Ответы [ 4 ]

2 голосов
/ 19 января 2012

Учитывая отсутствие стандартизации, я думаю, что ваше текущее решение, хотя и не идеальное, может быть лучшим, что вы можете сделать. Учитывая эту ситуацию, я бы, по крайней мере, изолировать проблемы, например, чтение файла, разбор файла, преобразование файла в стандартный xml, отображение таблицы доступа и т. д. к простым компонентам, использующим очевидные шаблоны, например, DI, стратегии, фабрики, репозитории и т. Д., Где это необходимо, чтобы отделить систему от основной зависимости от таблицы отображения и текущих алгоритмов синтаксического анализа.

1 голос
/ 19 января 2012

Вам нужно работать по принципу СУХОЙ (не повторяйте себя) путем разделения проблем .Например, код, который вы опубликовали, явно знает:

  1. как анализировать карту заявок и
  2. как использовать карту заявок для анализа списка заявок.

Таким образом, есть как минимум две обязанности, непосредственно отнесенные к этому одному методу.Я бы порекомендовал изменить класс ClaimMap, чтобы он больше отражал то, что он фактически должен представлять:

public class ClaimMap
{
    public ClaimMapField Name{get;set;}
    ...
}
public class ClaimMapField
{
    public int StartingLine{get;set;}
    // I would have the parser subtract one when creating this, to make it 0-based.
    public int StartingCharacter{get;set;}
    public int MaxLength{get;set;}
}

Обратите внимание, что ClaimMapField представляет в коде то, что вы потратили много времени на объяснение на английском языке.Это уменьшает необходимость в длительной документации.Теперь все вызовы M.Name.Split можно объединить в один метод, который знает, как создать ClaimMapFields из исходного текстового файла.Если вам когда-либо потребуется изменить способ представления ваших ClaimMaps в текстовом файле, вам нужно всего лишь изменить одну точку в коде.

Теперь ваш код может выглядеть примерно так:

c.Patient.FullName = cl[map.Name.StartingLine].Substring(map.Name.StartingCharacter, map.Name.MaxLength).Trim();
c.Patient.Address = cl[map.Address.StartingLine].Substring(map.Address.StartingCharacter, map.Address.MaxLength).Trim();
...

Но подождите, это еще не все!Каждый раз, когда вы видите повторение в вашем коде, это запах кода .Почему бы не извлечь метод здесь:

public string ParseMapField(ClaimMapField field, List<string> claim)
{
    return claim[field.StartingLine].Substring(field.StartingCharacter, field.MaxLength).Trim();
}

Теперь ваш код может выглядеть примерно так:

HCFA_Claim c = new HCFA_Claim
    {
        Patient = new Patient
            {
                FullName = ParseMapField(map.Name, cl),
                Address = ParseMapField(map.Address, cl),
            }
    };

Разбив код на более мелкие логические части, вы можете увидеть, как каждыйЧасть становится очень легко понять и проверить визуально.Вы значительно снижаете риск ошибок копирования / вставки, а при наличии ошибки или нового требования вам обычно приходится менять только одно место в коде вместо каждой строки.

1 голос
/ 19 января 2012

Если вы получаете только неструктурированный текст, вам нужно проанализировать его. Если текстовое содержимое изменяется, вы должны исправить свой синтаксический анализатор. Там нет никакого способа обойти это. Вероятно, вы могли бы найти стороннее приложение для визуального анализа, где вы выделяете нужную строку текста, и оно выполняет всю подстроку за вас, но все еще неструктурированный текст == parsing == хрупкий Визуальный синтаксический анализатор, по крайней мере, облегчит просмотр ошибок / измененных макетов и их исправление.

Что касается синтаксического анализа, я не уверен в построчном подходе. Что если что-то, что вы ищете, занимает несколько строк? Вы можете объединить все это в одну строку и использовать IndexOf для подстроки с разными индексами для каждой части данных, которую вы ищете.

Вы всегда можете использовать RegEx вместо Substring, если вы знаете, как это сделать.

0 голосов
/ 19 января 2012

Хотя базовый подход, который вы используете, кажется подходящим для вашей ситуации, безусловно, есть способы, которыми вы можете очистить код, чтобы его было легче читать и обслуживать. Разделяя все функции, которые вы выполняете в основном цикле, вы можете изменить это:

 c.Patient.FullName = cl[Int32.Parse(M.Name.Split(',')[0]) - 1].Substring(Int32.Parse(M.Name.Split(',')[1]) - 1, Int32.Parse(M.Name.Split(',')[2])).Trim();

примерно так:

var parser = new FormParser(cl, M);
c.PatientFullName = FormParser.GetName();
c.PatientAddress = FormParser.GetAddress();
// etc

Итак, в своем новом классе FormParser вы передаете List, представляющий вашу форму, и карту утверждений для провайдера в конструктор. Затем у вас есть получатель для каждого свойства в форме. Внутри этого геттера вы выполняете логику парсинга / подстроки, как сейчас. Как я уже сказал, вы на самом деле не меняете метод, с помощью которого вы это делаете, но, безусловно, его будет легче читать и поддерживать, и это может снизить общий уровень стресса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...