kongwz 5 rokov pred
rodič
commit
57a87c7abc

+ 30 - 4
kernel/src/main/java/com/lantone/qc/kernel/structure/ai/BeHospitalizedAI.java

@@ -5,16 +5,17 @@ import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.lantone.qc.kernel.catalogue.QCCatalogue;
 import com.lantone.qc.kernel.client.CRFServiceClient;
+import com.lantone.qc.kernel.structure.ai.model.CrfOut;
+import com.lantone.qc.kernel.structure.ai.process.EntityProcess;
+import com.lantone.qc.kernel.structure.ai.process.EntityProcessSymptom;
 import com.lantone.qc.pub.Content;
 import com.lantone.qc.pub.model.InputInfo;
 import com.lantone.qc.pub.model.doc.BeHospitalizedDoc;
 import com.lantone.qc.pub.model.entity.Annotation;
-import com.lantone.qc.pub.model.label.ChiefLabel;
 import com.lantone.qc.pub.model.vo.CRFVo;
 import com.lantone.qc.pub.util.StringUtil;
 
-import java.util.Arrays;
-import java.util.List;
+import java.util.*;
 
 /**
  * @ClassName : InHospitalDoc
@@ -33,7 +34,10 @@ public class BeHospitalizedAI extends QCCatalogue {
      */
     public static List<String> medicalTextType = Arrays.asList("FirstCourseRecord_cx", "PastFamily_cx", "PersonalHistory_cx", "HPIForCX_cx",
             "GeneralVital_cx", "chief_present");
+    public static String entityRelationObject = "entity_relation_object";
+    public static String outputs = "outputs";
     public void medrec(InputInfo inputInfo,CRFServiceClient crfServiceClient){
+        Map<String,List<CrfOut>> crfOut = new HashMap<>();//主诉-->
         JSONArray crfContent = new JSONArray();
         BeHospitalizedDoc beHospitalizedDoc = inputInfo.getBeHospitalizedDoc();
         String chief_text = beHospitalizedDoc.getChiefLabel().getText();
@@ -45,12 +49,19 @@ public class BeHospitalizedAI extends QCCatalogue {
         String menstrual_text = beHospitalizedDoc.getMenstrualLabel().getText();
         //婚育史
         String marital_text = beHospitalizedDoc.getMaritalLabel().getText();
-        //存放主诉、现病史
+        //一般体格检查
+        String vital_text = beHospitalizedDoc.getVitalLabel().getText();
+        //专科体格检查
+        String vitalSpecial_text = beHospitalizedDoc.getVitalLabelSpecial().getText();
+        //存放主诉、现病史、专科查体(体格检查(二))
         putContent(crfContent,medicalTextType.get(3), chief_text,Content.chief);
         putContent(crfContent,medicalTextType.get(3), present_text,Content.present);
+        putContent(crfContent,medicalTextType.get(3), vitalSpecial_text,Content.special_exam);
         //存放既往史、家族史
         putContent(crfContent,medicalTextType.get(3), past_text,Content.past);
         putContent(crfContent,medicalTextType.get(3), family_text,Content.family);
+        //存放一般查体
+        putContent(crfContent,medicalTextType.get(3), vital_text,Content.phys_exam);
         //存放个人史、月经史、婚育史
         putContent(crfContent,medicalTextType.get(3), Personal_text,Content.personal);
         putContent(crfContent,medicalTextType.get(3), menstrual_text,Content.menses);
@@ -61,9 +72,24 @@ public class BeHospitalizedAI extends QCCatalogue {
         //获取CRF模型返回数据
         JSONArray data = getAnnotation(crfServiceClient, crfVo).getData();
         JSONObject midData = getOutputs(data);
+        putAllCrfData(midData.getJSONObject(Content.chief),crfOut,Content.chief);
 
 
 
+    }
+    public void putAllCrfData(JSONObject jsonObject,Map<String,List<CrfOut>> crfOut,String sign){
+        if (jsonObject == null) {
+            return;
+        }
+        Map<String,List<CrfOut>> detailOutput = new HashMap<>();
+        JSONObject outputs = jsonObject.getJSONObject(entityRelationObject).getJSONObject(BeHospitalizedAI.outputs);
+        List<CrfOut> outputInfos = new ArrayList<>();
+        CrfOut outputInfo = new CrfOut();
+        add2Output(new EntityProcessSymptom(), outputs, outputInfo);
+
+    }
+    public void add2Output(EntityProcess entityProcess, JSONObject outputs, CrfOut outputInfo){
+        entityProcess.extractEntity(outputs, outputInfo);
     }
     public void putContent(JSONArray crfContent, String medicalTextType, String text,String sign){
         String move_text = this.moveSpecialSymbols(text);

+ 19 - 0
kernel/src/main/java/com/lantone/qc/kernel/structure/ai/model/CrfOut.java

@@ -0,0 +1,19 @@
+package com.lantone.qc.kernel.structure.ai.model;
+
+import com.lantone.qc.pub.model.entity.Clinical;
+import com.lantone.qc.pub.model.entity.Pacs;
+import com.lantone.qc.pub.model.entity.Vital;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Getter
+@Setter
+public class CrfOut {
+    List<Clinical> symptoms = new ArrayList<Clinical>();//症状
+    List<Vital> vitals = new ArrayList<>();
+    List<Pacs> pacses = new ArrayList<>();
+
+}

+ 221 - 0
kernel/src/main/java/com/lantone/qc/kernel/structure/ai/model/EntityEnum.java

@@ -0,0 +1,221 @@
+package com.lantone.qc.kernel.structure.ai.model;
+
+/**
+ * @Description:
+ * @Author: HUJING
+ * @Date: 2019/12/17 9:58
+ */
+public enum EntityEnum {
+    CLINICAL_FEATURE("临床表现"), DIEASE("疾病名称"), BODY("身体部位"), SIGN("体征"), INDEX_VALUE("指标值"),
+    LABORATORY("实验室检查"), LABORATORY_VALUE("实验室检查值"), AUXILIARY_EXAMINATION("辅助检查"), AUXILIARY_DESCRIPT("辅助检查描述"),
+    NEGATIVE("否定"), POSSIBLE("可能的"), TIME("时间"), CAUSE("诱因"), MODIFICATION("修饰"),
+    TREND("趋势"), FREQUENCY("频率"), QUANTITY("数量"), SIZE("尺寸"), CURE("治疗"), DRUG("药物名称"),
+    DOSE("药品剂量"), OPERATION("手术名称"), GENERAL("一般情况"), GENERAL_DESCRIPT("一般情况描述"),
+    OPERATION_KEYWORD("手术史"), OPERATION_RESULT("手术结果"), INJURY("外伤史"), ALLERGY("过敏"),
+    FOOD_ALLERGY("食物过敏原"), DRUG_ALLERGY("药物过敏原"), ALLERGY_SYMPTOM("过敏表现"), BLOOD_TRANSFUSION("输血史"),
+    TRANSFUSION_REACTION("输血反应"), VACCINATION("预防接种史"), DISEASE_KEYWORD("疾病史"), INFECTIOUS_KEYWORD("传染病史"),
+    UNKNOWN("情况不详"), HEALTH("健康状况"), AGE("年龄"), SMOKING_HISTORY("吸烟史"),
+    HISTORY_OF_ALCOHOL_INTAKE("饮酒史"), USAGE("用量"), MENSES("月经"), LEUKORRHEA("白带"),
+    BIRTH_HIS("生育情况"), RELATIVES("家属"), GROUP_CONSULTATION("会诊"), ORGANISM("生物体"),
+    OCCUPATION("职业"), LOCATION("地点"), DEAD("死亡"), DEAD_REASON("死亡原因"),
+    SIMILAR_DISEASE_KEYWORD("相似疾病"), GENETIC_DISEASE_KEYWORD("家族遗传病"), EPIDEMIC_AREA_HISTORY("疫区史"),
+    CONTACT_HISTORY("接触史"), MARITAL_HISTORY("冶游史"), MARITAL_STATUS("婚姻情况"), NEAR_RELATION("近亲史"),
+    CURE_AIM("治疗目的"), OTHER("其他");
+
+    private String value;
+
+    EntityEnum(String value) {
+        this.value = value;
+    }
+
+    public String toString() {
+        return value;
+    }
+
+    public static EntityEnum parseOfValue(String value) {
+        if (value == null) {
+            return EntityEnum.OTHER;
+        }
+        EntityEnum entityEnum = EntityEnum.OTHER;
+        switch (value) {
+            case "临床表现":
+                entityEnum = EntityEnum.CLINICAL_FEATURE;
+                break;
+            case "疾病名称":
+                entityEnum = EntityEnum.DIEASE;
+                break;
+            case "身体部位":
+                entityEnum = EntityEnum.BODY;
+                break;
+            case "体征":
+                entityEnum = EntityEnum.SIGN;
+                break;
+            case "指标值":
+                entityEnum = EntityEnum.INDEX_VALUE;
+                break;
+            case "实验室检查":
+                entityEnum = EntityEnum.LABORATORY;
+                break;
+            case "实验室检查值":
+                entityEnum = EntityEnum.LABORATORY_VALUE;
+                break;
+            case "辅助检查":
+                entityEnum = EntityEnum.AUXILIARY_EXAMINATION;
+                break;
+            case "辅助检查描述":
+                entityEnum = EntityEnum.AUXILIARY_DESCRIPT;
+                break;
+            case "否定":
+                entityEnum = EntityEnum.NEGATIVE;
+                break;
+            case "可能的":
+                entityEnum = EntityEnum.POSSIBLE;
+                break;
+            case "时间":
+                entityEnum = EntityEnum.TIME;
+                break;
+            case "诱因":
+                entityEnum = EntityEnum.CAUSE;
+                break;
+            case "修饰":
+                entityEnum = EntityEnum.MODIFICATION;
+                break;
+            case "趋势":
+                entityEnum = EntityEnum.TREND;
+                break;
+            case "频率":
+                entityEnum = EntityEnum.FREQUENCY;
+                break;
+            case "数量":
+                entityEnum = EntityEnum.QUANTITY;
+                break;
+            case "尺寸":
+                entityEnum = EntityEnum.SIZE;
+                break;
+            case "治疗":
+                entityEnum = EntityEnum.CURE;
+                break;
+            case "药物名称":
+                entityEnum = EntityEnum.DRUG;
+                break;
+            case "药品剂量":
+                entityEnum = EntityEnum.DOSE;
+                break;
+            case "手术名称":
+                entityEnum = EntityEnum.OPERATION;
+                break;
+            case "一般情况":
+                entityEnum = EntityEnum.GENERAL;
+                break;
+            case "一般情况描述":
+                entityEnum = EntityEnum.GENERAL_DESCRIPT;
+                break;
+            case "手术史":
+                entityEnum = EntityEnum.OPERATION_KEYWORD;
+                break;
+            case "手术结果":
+                entityEnum = EntityEnum.OPERATION_RESULT;
+                break;
+            case "外伤史":
+                entityEnum = EntityEnum.INJURY;
+                break;
+            case "过敏":
+                entityEnum = EntityEnum.ALLERGY;
+                break;
+            case "食物过敏原":
+                entityEnum = EntityEnum.FOOD_ALLERGY;
+                break;
+            case "药物过敏原":
+                entityEnum = EntityEnum.DRUG_ALLERGY;
+                break;
+            case "过敏表现":
+                entityEnum = EntityEnum.ALLERGY_SYMPTOM;
+                break;
+            case "输血史":
+                entityEnum = EntityEnum.BLOOD_TRANSFUSION;
+                break;
+            case "输血反应":
+                entityEnum = EntityEnum.TRANSFUSION_REACTION;
+                break;
+            case "预防接种史":
+                entityEnum = EntityEnum.VACCINATION;
+                break;
+            case "疾病史":
+                entityEnum = EntityEnum.DISEASE_KEYWORD;
+                break;
+            case "传染病史":
+                entityEnum = EntityEnum.INFECTIOUS_KEYWORD;
+                break;
+            case "情况不详":
+                entityEnum = EntityEnum.UNKNOWN;
+                break;
+            case "健康状况":
+                entityEnum = EntityEnum.HEALTH;
+                break;
+            case "年龄":
+                entityEnum = EntityEnum.AGE;
+                break;
+            case "吸烟史":
+                entityEnum = EntityEnum.SMOKING_HISTORY;
+                break;
+            case "饮酒史":
+                entityEnum = EntityEnum.HISTORY_OF_ALCOHOL_INTAKE;
+                break;
+            case "用量":
+                entityEnum = EntityEnum.USAGE;
+                break;
+            case "月经":
+                entityEnum = EntityEnum.MENSES;
+                break;
+            case "白带":
+                entityEnum = EntityEnum.LEUKORRHEA;
+                break;
+            case "生育情况":
+                entityEnum = EntityEnum.BIRTH_HIS;
+                break;
+            case "家属":
+                entityEnum = EntityEnum.RELATIVES;
+                break;
+            case "会诊":
+                entityEnum = EntityEnum.GROUP_CONSULTATION;
+                break;
+            case "生物体":
+                entityEnum = EntityEnum.ORGANISM;
+                break;
+            case "职业":
+                entityEnum = EntityEnum.OCCUPATION;
+                break;
+            case "地点":
+                entityEnum = EntityEnum.LOCATION;
+                break;
+            case "死亡":
+                entityEnum = EntityEnum.DEAD;
+                break;
+            case "死亡原因":
+                entityEnum = EntityEnum.DEAD_REASON;
+                break;
+            case "相似疾病":
+                entityEnum = EntityEnum.SIMILAR_DISEASE_KEYWORD;
+                break;
+            case "家族遗传病":
+                entityEnum = EntityEnum.GENETIC_DISEASE_KEYWORD;
+                break;
+            case "疫区史":
+                entityEnum = EntityEnum.EPIDEMIC_AREA_HISTORY;
+                break;
+            case "接触史":
+                entityEnum = EntityEnum.CONTACT_HISTORY;
+                break;
+            case "冶游史":
+                entityEnum = EntityEnum.MARITAL_HISTORY;
+                break;
+            case "婚姻情况":
+                entityEnum = EntityEnum.MARITAL_STATUS;
+                break;
+            case "治疗目的":
+                entityEnum = EntityEnum.CURE_AIM;
+                break;
+        }
+        return entityEnum;
+    }
+}

+ 130 - 0
kernel/src/main/java/com/lantone/qc/kernel/structure/ai/process/EntityProcess.java

@@ -0,0 +1,130 @@
+package com.lantone.qc.kernel.structure.ai.process;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.lantone.qc.kernel.structure.ai.model.CrfOut;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @Description:
+ * @Author: HUJING
+ * @Date: 2020/2/2 10:07
+ */
+public abstract class EntityProcess {
+    public abstract void extractEntity(JSONObject outputs, CrfOut outputInfo);
+
+    /**
+     * 处理关系抽取输出的json
+     *
+     * @param outputs    关系抽取输出的json
+     * @param entityType 需要处理的实体类别
+     * @return
+     */
+    public List<Map<String, String>> processJson(JSONObject outputs, String entityType) {
+        List<Map<String, String>> connectEntityList = new ArrayList<>();
+        Map<String, String> connectEntity = null;
+        JSONObject annotation = outputs.getJSONObject("annotation");
+        JSONArray entitys = annotation.getJSONArray("T");
+        JSONArray relations = annotation.getJSONArray("R");
+        for (int i = 0; i < entitys.size(); i++) {
+            if (StringUtils.isEmpty(entitys.get(i).toString())) {
+                continue;
+            }
+            JSONObject entity = entitys.getJSONObject(i);
+            if (entityType.equals(entity.getString("name"))) {
+                int id = entity.getIntValue("id");
+                List<Integer> connectEntityIdList = getConnectEntityIdList(id, relations);
+                if (connectEntityIdList.size() == 0) {
+                    connectEntity = new HashMap<>();
+                    connectEntity.put(entity.getString("name"), entity.getString("value"));
+                    connectEntityList.add(connectEntity);
+                } else {
+                    connectEntity = getConnectEntity(connectEntityIdList, entitys);
+                    connectEntity.put(entity.getString("name"), entity.getString("value"));
+                    connectEntityList.add(connectEntity);
+                }
+            }
+        }
+        return connectEntityList;
+    }
+
+    /**
+     * 获取与传入实体有关系实体的id列表(List)
+     *
+     * @param entityId  传入实体的id
+     * @param relations 关系抽取出的关系对
+     * @return connectEntityIdList 有关系实体的id列表(List)
+     */
+    public List<Integer> getConnectEntityIdList(int entityId, JSONArray relations) {
+        List<Integer> connectEntityIdList = new ArrayList<>();
+        for (int i = 0; i < relations.size(); i++) {
+            if (StringUtils.isEmpty(relations.get(i).toString())) {
+                continue;
+            }
+            JSONObject relation = relations.getJSONObject(i);
+            if (relation.getIntValue("from") == entityId) {
+                connectEntityIdList.add(relation.getIntValue("to"));
+            }
+            if (relation.getIntValue("to") == entityId) {
+                connectEntityIdList.add(relation.getIntValue("from"));
+            }
+        }
+        return connectEntityIdList;
+    }
+
+    /**
+     * 获取实体id列表对应的所有实体类型及实体值
+     *
+     * @param connectEntityIdList 实体id列表
+     * @param entitys             关系抽取的实体列表
+     * @return entityRelationPair 实体id列表对应的所有实体类型及实体值
+     */
+    public Map<String, String> getConnectEntity(List<Integer> connectEntityIdList, JSONArray entitys) {
+        Map<String, String> entityRelationPair = new HashMap<>();
+        for (int connectEntityId : connectEntityIdList) {
+            for (int i = 0; i < entitys.size(); i++) {
+                if (StringUtils.isEmpty(entitys.get(i).toString())) {
+                    continue;
+                }
+                JSONObject entity = entitys.getJSONObject(i);
+                if (connectEntityId == entity.getIntValue("id")) {
+                    if (entityRelationPair.containsKey(entity.getString("name"))) {
+                        entityRelationPair.put(entity.getString("name"),
+                                entityRelationPair.get(entity.getString("name")) + "," + entity.getString("value"));
+                    } else {
+                        entityRelationPair.put(entity.getString("name"), entity.getString("value"));
+                    }
+                    break;
+                }
+            }
+        }
+        return entityRelationPair;
+    }
+
+
+    public String[] extract_digit(String value) {
+        String[] res = new String[2];
+        try {
+            String reg_time = "([\\d]+)([\\u4e00-\\u9fa5]+)";
+            Pattern pattern = Pattern.compile(reg_time);
+            Matcher matcher = pattern.matcher(value);
+            if (matcher.find(0)) {
+                res[0] = matcher.group(1);
+                res[1] = matcher.group(2);
+            } else {
+                res[0] = value;
+                res[1] = "";
+            }
+        } catch (Exception ex) {
+            ex.printStackTrace();
+        }
+        return res;
+    }
+}

+ 60 - 0
kernel/src/main/java/com/lantone/qc/kernel/structure/ai/process/EntityProcessSymptom.java

@@ -0,0 +1,60 @@
+package com.lantone.qc.kernel.structure.ai.process;
+
+
+import com.alibaba.fastjson.JSONObject;
+import com.lantone.qc.kernel.structure.ai.model.CrfOut;
+import com.lantone.qc.kernel.structure.ai.model.EntityEnum;
+import com.lantone.qc.pub.model.entity.BodyPart;
+import com.lantone.qc.pub.model.entity.Clinical;
+import com.lantone.qc.pub.model.entity.Negative;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class EntityProcessSymptom extends EntityProcess {
+
+    @Override
+    public void extractEntity(JSONObject outputs, CrfOut outputInfo) {
+        List<Clinical> clinicals =new ArrayList<>();
+        Clinical clinical;
+        List<Map<String, String>> symptomEntityList = processJson(outputs, EntityEnum.CLINICAL_FEATURE.toString());
+        for (Map<String, String> symptomEntityMap : symptomEntityList) {
+            if (StringUtils.isEmpty(symptomEntityMap.get(EntityEnum.CLINICAL_FEATURE.toString()))) {
+                continue;
+            }
+            clinical = new Clinical();
+            for (String key : symptomEntityMap.keySet()) {
+                String entity = StringUtils.isEmpty(symptomEntityMap.get(key)) ? "" : symptomEntityMap.get(key);
+                switch (EntityEnum.parseOfValue(key)){
+                    case CLINICAL_FEATURE:
+                        clinical.setName(symptomEntityMap.get(key));
+                        break;
+                    case NEGATIVE:
+                        Negative negative = new Negative();
+                        negative.setName(entity);
+                        clinical.setNegative(negative);
+                        break;
+                    case BODY:
+                        BodyPart bodyPart = new BodyPart();
+                        bodyPart.setName(entity);
+                        clinical.setBodyPart(bodyPart);
+                    case MODIFICATION:
+
+
+
+
+
+
+
+
+                }
+
+            }
+
+        }
+
+
+    }
+}

+ 1 - 1
kernel/src/main/resources/application.yml

@@ -12,7 +12,7 @@ spring:
       charset: UTF-8
       enabled: true
 CRF:
-  url: http://192.168.3.150:5555/api/mr_info_ex/entity_predict
+  url: http://192.168.3.150:3456/api/mr_info_ex/entity_predict
 
 logging:          # 日志
   config: classpath:logback-spring.xml

+ 2 - 0
public/src/main/java/com/lantone/qc/pub/model/doc/BeHospitalizedDoc.java

@@ -34,6 +34,8 @@ public class BeHospitalizedDoc {
     private FamilyLabel familyLabel;
     //体格检查
     private VitalLabel vitalLabel;
+    //专科体格检查
+    private VitalLabelSpecial vitalLabelSpecial;
     //辅助检查
     private PacsLabel pacsLabel;
     //初步诊断

+ 2 - 2
public/src/main/java/com/lantone/qc/pub/model/label/VitalLabel.java

@@ -2,9 +2,9 @@ package com.lantone.qc.pub.model.label;
 
 /**
  * @ClassName : VatilLabel
- * @Description : 体格检查
+ * @Description : 体格检查(一般检查)
  * @Author : 楼辉荣
  * @Date: 2020-03-03 18:49
  */
-public class VitalLabel {
+public class VitalLabel extends GeneralLabel{
 }

+ 10 - 0
public/src/main/java/com/lantone/qc/pub/model/label/VitalLabelSpecial.java

@@ -0,0 +1,10 @@
+package com.lantone.qc.pub.model.label;
+
+/**
+ * @ClassName : VatilLabel
+ * @Description : 体格检查(专科检查)
+ * @Author : 楼辉荣
+ * @Date: 2020-03-03 18:49
+ */
+public class VitalLabelSpecial extends GeneralLabel {
+}

+ 2 - 2
trans/pom.xml

@@ -14,8 +14,8 @@
 
   <properties>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-    <maven.compiler.source>1.7</maven.compiler.source>
-    <maven.compiler.target>1.7</maven.compiler.target>
+    <maven.compiler.source>1.8</maven.compiler.source>
+    <maven.compiler.target>1.8</maven.compiler.target>
   </properties>
 
   <dependencies>