├── README.md ├── answer_search.py ├── build_medicalgraph.py ├── chatbot_graph.py ├── img ├── chat1.png ├── chat2.png ├── graph_summary.png ├── kg_route.png └── qa_route.png ├── prepare_data ├── build_data.py ├── data_spider.py └── max_cut.py ├── question_classifier.py ├── question_parser.py ├── requirements.txt ├── run.py ├── static ├── css │ ├── _chat.scss │ ├── bootstrap.min.css │ ├── loginnew.css │ ├── style.css │ ├── style.css.map │ └── style.scss ├── fonts │ └── Artifika-Regular.ttf ├── img │ ├── bg12.jpg │ ├── bg14.jpg │ ├── bg15.jpg │ ├── bg16.jpg │ ├── bg33.jpg │ ├── btn3.gif │ ├── btn3.png │ ├── btn32.png │ ├── kuang1.png │ ├── kuang101.png │ ├── r1.png │ ├── r11.png │ └── r2.png └── js │ ├── bootstrap.min.js │ ├── jquery-1.12.3.min.js │ ├── jquery-3.1.1.min.js │ ├── jquery1102.min.js │ ├── loginnew.js │ └── main.js └── templates ├── README.md ├── debug.log └── index.html /README.md: -------------------------------------------------------------------------------- 1 | # 项目介绍 2 | 知识图谱是目前自然语言处理的一个热门方向。关于知识图谱概念性的介绍就不在此赘述。目前知识图谱在各个领域全面开花,如教育、医疗、司法、金融等。本项目立足医药领域,以垂直型医药网站为数据来源,以疾病为核心,构建起一个包含7类规模为4.4万的知识实体,11类规模约30万实体关系的知识图谱。 3 | 本项目将包括以下两部分的内容: 4 | 1) 基于垂直网站数据的医药知识图谱构建 5 | 2) 基于医药知识图谱的自动问答 6 | 7 | # 项目最终效果 8 | 以下两图是实际问答运行过程中的截图: 9 | ![image](/img/chat1.png) 10 | 11 | ![image](img/chat2.png) 12 | 13 | # 项目运行方式 14 | 1、配置要求:要求配置neo4j数据库及相应的python依赖包。neo4j数据库用户名密码记住,并修改相应文件。 15 | 2、知识图谱数据导入:python build_medicalgraph.py,导入的数据较多,估计需要几个小时。 16 | 3、启动问答:python chat_graph.py 17 | 18 | # 以下介绍详细方案 19 | # 一、医疗知识图谱构建 20 | # 1.1 业务驱动的知识图谱构建框架 21 | ![image](img/kg_route.png) 22 | 23 | # 1.2 脚本目录 24 | prepare_data/datasoider.py:网络资讯采集脚本 25 | prepare_data/datasoider.py:网络资讯采集脚本 26 | prepare_data/max_cut.py:基于词典的最大向前/向后切分脚本 27 | build_medicalgraph.py:知识图谱入库脚本    28 | 29 | # 1.3 医药领域知识图谱规模 30 | 1.3.1 neo4j图数据库存储规模 31 | ![image](img/graph_summary.png) 32 | 33 | 1.3.2 知识图谱实体类型 34 | 35 | | 实体类型 | 中文含义 | 实体数量 |举例 | 36 | | :--- | :---: | :---: | :--- | 37 | | Check | 诊断检查项目 | 3,353| 支气管造影;关节镜检查| 38 | | Department | 医疗科目 | 54 | 整形美容科;烧伤科| 39 | | Disease | 疾病 | 8,807 | 血栓闭塞性脉管炎;胸降主动脉动脉瘤| 40 | | Drug | 药品 | 3,828 | 京万红痔疮膏;布林佐胺滴眼液| 41 | | Food | 食物 | 4,870 | 番茄冲菜牛肉丸汤;竹笋炖羊肉| 42 | | Producer | 在售药品 | 17,201 | 通药制药青霉素V钾片;青阳醋酸地塞米松片| 43 | | Symptom | 疾病症状 | 5,998 | 乳腺组织肥厚;脑实质深部出血| 44 | | Total | 总计 | 44,111 | 约4.4万实体量级| 45 | 46 | 47 | 1.3.3 知识图谱实体关系类型 48 | 49 | | 实体关系类型 | 中文含义 | 关系数量 | 举例| 50 | | :--- | :---: | :---: | :--- | 51 | | belongs_to | 属于 | 8,844| <妇科,属于,妇产科>| 52 | | common_drug | 疾病常用药品 | 14,649 | <阳强,常用,甲磺酸酚妥拉明分散片>| 53 | | do_eat |疾病宜吃食物 | 22,238| <胸椎骨折,宜吃,黑鱼>| 54 | | drugs_of | 药品在售药品 | 17,315| <青霉素V钾片,在售,通药制药青霉素V钾片>| 55 | | need_check | 疾病所需检查 | 39,422| <单侧肺气肿,所需检查,支气管造影>| 56 | | no_eat | 疾病忌吃食物 | 22,247| <唇病,忌吃,杏仁>| 57 | | recommand_drug | 疾病推荐药品 | 59,467 | <混合痔,推荐用药,京万红痔疮膏>| 58 | | recommand_eat | 疾病推荐食谱 | 40,221 | <鞘膜积液,推荐食谱,番茄冲菜牛肉丸汤>| 59 | | has_symptom | 疾病症状 | 5,998 | <早期乳腺癌,疾病症状,乳腺组织肥厚>| 60 | | acompany_with | 疾病并发疾病 | 12,029 | <下肢交通静脉瓣膜关闭不全,并发疾病,血栓闭塞性脉管炎>| 61 | | Total | 总计 | 294,149 | 约30万关系量级| 62 | 63 | 1.3.4 知识图谱属性类型 64 | 65 | | 属性类型 | 中文含义 | 举例 | 66 | | :--- | :---: | :---: | 67 | | name | 疾病名称 | 喘息样支气管炎 | 68 | | desc | 疾病简介 | 又称哮喘性支气管炎... | 69 | | cause | 疾病病因 | 常见的有合胞病毒等...| 70 | | prevent | 预防措施 | 注意家族与患儿自身过敏史... | 71 | | cure_lasttime | 治疗周期 | 6-12个月 | 72 | | cure_way | 治疗方式 | "药物治疗","支持性治疗" | 73 | | cured_prob | 治愈概率 | 95% | 74 | | easy_get | 疾病易感人群 | 无特定的人群 | 75 | 76 | 77 | # 二、基于医疗知识图谱的自动问答 78 | # 2.1 技术架构 79 | ![image](img/qa_route.png) 80 | 81 | # 2.2 脚本结构 82 | question_classifier.py:问句类型分类脚本 83 | question_parser.py:问句解析脚本 84 | chatbot_graph.py:问答程序脚本 85 | 86 | # usage 87 | python run.py 127.0.0.1:1234 88 | 89 | # 2.3 支持问答类型 90 | 91 | | 问句类型 | 中文含义 | 问句举例 | 92 | | :--- | :---: | :---: | 93 | | disease_symptom | 疾病症状| 乳腺癌的症状有哪些? | 94 | | symptom_disease | 已知症状找可能疾病 | 最近老流鼻涕怎么办? | 95 | | disease_cause | 疾病病因 | 为什么有的人会失眠?| 96 | | disease_acompany | 疾病的并发症 | 失眠有哪些并发症? | 97 | | disease_not_food | 疾病需要忌口的食物 | 失眠的人不要吃啥? | 98 | | disease_do_food | 疾病建议吃什么食物 | 耳鸣了吃点啥? | 99 | | food_not_disease | 什么病最好不要吃某事物 | 哪些人最好不好吃蜂蜜? | 100 | | food_do_disease | 食物对什么病有好处| 鹅肉有什么好处? | 101 | | disease_drug | 啥病要吃啥药 | 肝病要吃啥药? | 102 | | drug_disease | 药品能治啥病 | 板蓝根颗粒能治啥病? | 103 | | disease_check | 疾病需要做什么检查 | 脑膜炎怎么才能查出来?| 104 | | check_disease | 检查能查什么病 | 全血细胞计数能查出啥来? | 105 | | disease_prevent | 预防措施| 怎样才能预防肾虚? | 106 | | disease_lasttime | 治疗周期 | 感冒要多久才能好? | 107 | | disease_cureway | 治疗方式 | 高血压要怎么治? | 108 | | disease_cureprob | 治愈概率 | 白血病能治好吗? | 109 | | disease_easyget | 疾病易感人群 | 什么人容易得高血压? | 110 | | disease_desc | 疾病描述 | 糖尿病 | 111 | 112 | # 问答结果展示 113 | 114 | 用户:乳腺癌的症状有哪些? 115 | 小勇: 乳腺癌的症状包括:乳腺癌的远处转移;胸痛;乳头溢液;乳头破碎;肝肿大;泌乳障碍;乳头内陷;乳房肿块;剧痛 116 | ****************************************************************************************** 117 | 用户:最近老流鼻涕怎么办? 118 | 小勇: 症状流鼻涕可能染上的疾病有:枯草热;副流行性感冒;急性上呼吸道感染;硫化氢中毒;小儿衣原体肺炎;风寒感冒;慢性额窦炎;鼻源性头痛;人禽流行性感冒;小儿流行性喘憋性肺炎;病毒性感冒;慢性鼻炎;风热犯肺;感冒;顿呛;小儿急性上呼吸道感染;嗜酸细胞增多性非变态反应性鼻炎;干酪性鼻窦炎;下呼吸道感染;麻疹 119 | ****************************************************************************************** 120 | 用户:为什么有的人会失眠? 121 | 小勇: 失眠可能的成因有:躯体疾病和服用药物可以影响睡眠,如消化不良,头痛,背痛,关节炎,心脏病,糖尿病,哮喘,鼻窦炎,溃疡病,或服用某些影响中枢神经的药物。 122 | 由于生活方式引起睡眠问题也很常见,如饮用咖啡或茶叶,晚间饮酒,睡前进食或晚饭较晚造成满腹食物尚未消化,大量吸烟,睡前剧烈的体力活动,睡前过度的精神活动,夜班工作,白天小睡,上床时间不规律,起床时间不规律。 123 | 可能的原因有压力很大,过度忧虑,紧张或焦虑,悲伤或抑郁,生气,容易出现睡眠问题。 124 | 吵闹的睡眠环境,睡眠环境过于明亮,污染,过度拥挤。 125 | ****************************************************************************************** 126 | 用户:失眠有哪些并发症? 127 | 小勇: 失眠的症状包括:心肾不交;神经性耳鸣;咽鼓管异常开放症;偏执狂;十二指肠胃反流及胆汁反流性胃炎;腋臭;黧黑斑;巨细胞动脉炎;Stargardt病;抑郁症;腔隙性脑梗死;甲状腺功能亢进伴发的精神障碍;紧张性头痛;胃下垂;心血虚;迷路震荡;口腔结核性溃疡;痰饮;游走性结节性脂膜炎;小儿脑震荡 128 | ****************************************************************************************** 129 | 用户:失眠的人不要吃啥? 130 | 小勇: 失眠忌食的食物包括有:油条;河蚌;猪油(板油);淡菜(鲜) 131 | ****************************************************************************************** 132 | 用户:耳鸣了吃点啥? 133 | 小勇: 耳鸣宜食的食物包括有:南瓜子仁;鸡翅;芝麻;腰果 134 | 推荐食谱包括有:紫菜芙蓉汤;羊肉汤面;油豆腐油菜;紫菜鸡蛋莲草汤;乌药羊肉汤;可乐鸡翅;栗子鸡翅;冬菇油菜心 135 | ****************************************************************************************** 136 | 用户:哪些人最好不好吃蜂蜜? 137 | 小勇: 患有散发性脑炎伴发的精神障碍;情感性心境障碍;蝎螫伤;四肢淋巴水肿;农药中毒所致的精神障碍;肝错构瘤;细菌性肺炎;急性高原病;小儿颅后窝室管膜瘤;柯萨奇病毒疹;眼眶静脉性血管瘤;乙脑伴发的精神障碍;晚期产后出血;吸入性肺炎;腓总神经损伤;铍及其化合物引起的皮肤病;猝死型冠心病;彼得异常;过敏性急性小管间质性肾炎;小儿腹胀的人最好不要吃蜂蜜 138 | ****************************************************************************************** 139 | 用户:鹅肉有什么好处? 140 | 小勇: 患有子宫内膜厚;呼吸疾病;肛肠病;闭经;丧偶后适应性障碍;宫颈外翻;巨球蛋白血症;急性颌下腺炎;锥体外系损害;腺样体炎;咳嗽;错构瘤;牙科病;子宫内膜炎;闭锁综合征;结膜炎;恶性淋巴瘤;足外翻;神经炎;病理性近视的人建议多试试鹅肉 141 | ****************************************************************************************** 142 | 用户:肝病要吃啥药? 143 | 小勇: 肝病宜食的食物包括有:鹅肉;鸡肉;鸡肝;鸡腿 144 | 推荐食谱包括有:小米红糖粥;小米蛋奶粥;扁豆小米粥;黄豆小米粥;人参小米粥;小米粉粥;鲜菇小米粥;芝麻小米粥 145 | 肝病通常的使用的药品包括:恩替卡韦分散片;维生素C片;二十五味松石丸;拉米夫定胶囊;阿德福韦酯片 146 | ****************************************************************************************** 147 | 用户:板蓝根颗粒能治啥病? 148 | 小勇: 板蓝根颗粒主治的疾病有流行性腮腺炎;喉痹;喉炎;咽部异感症;急性单纯性咽炎;腮腺隙感染;过敏性咽炎;咽囊炎;急性鼻咽炎;喉水肿;慢性化脓性腮腺炎;慢性咽炎;急性喉炎;咽异感症;鼻咽炎;锁喉痈;小儿咽喉炎;喉返神经损伤;化脓性腮腺炎;喉血管瘤,可以试试 149 | ****************************************************************************************** 150 | 用户:脑膜炎怎么才能查出来? 151 | 小勇: 脑膜炎通常可以通过以下方式检查出来:脑脊液钠;尿常规;Fisher手指试验;颈项强直;脑脊液细菌培养;尿谷氨酰胺;脑脊液钾;脑脊液天门冬氨酸氨基转移酶;脑脊液病原体检查;硝酸盐还原试验 152 | ****************************************************************************************** 153 | 用户:怎样才能预防肾虚? 154 | 小勇: 肾虚可能的成因有:1、多因房劳过度,或少年频繁手淫。2、思虑忧郁,损伤心脾,则病及阳明冲脉。3、恐惧伤肾,恐则伤肾。4、肝主筋,阴器为宗筋之汇,若情志不遂,忧思郁怒,肝失疏泄条达,则宗筋所聚无能。5、湿热下注,宗筋弛纵。 155 | 肾虚是肾脏精气阴阳不足所产生的诸如精神疲乏、头晕耳鸣、健忘脱发、腰脊酸痛、遗精阳痿、男子不育、女子不孕、更年期综合征等多种病证的一个综合概念。关于肾虚形成的原因,可归结为两个方面,一为先天禀赋不足,二为后天因素引起。 156 | 从引起肾虚的先天因素来看,首先是先天禀赋薄弱。《灵枢.寿天刚柔》篇说:“人之生也,有刚有柔,有弱有强。”由于父母体弱多病,精血亏虚时怀孕;或酒后房事怀孕;或年过五十精气力量大减之时怀孕;或男女双方年龄不够,身体发育不完全结婚,也就是早婚时怀孕,或生育过多,精血过度耗损;或妊娠期中失于调养,胎气不足等等都可导致肾的精气亏虚成为肾虚证形成的重要原因;其次,如果肾藏精功能失常就会导致性功能异常,生殖功能下降,影响生殖能力,便会引起下一代形体虚衰,或先天畸形、痴呆、缺陷、男子出现精少不育、早泄,女子出现闭经不孕、小产、习惯性流产等等。 157 | 肾虚的预防措施包括:肾虚日常预防 158 | 在预防方面,因起病与恣情纵欲有关的,应清心寡欲,戒除手淫;如与全身衰弱、营养不良或身心过劳有关的,应适当增加营养或注意劳逸结合,节制性欲。 159 | 1、性生活要适度,不勉强,不放纵。 160 | 2、饮食方面:无力疲乏时多吃含铁、蛋白质的食物,如木耳、大枣、乌鸡等;消化不良者多喝酸奶,吃山楂;平日护肾要多吃韭菜、海参、人参、乌鸡、家鸽等。 161 | 3、经常进行腰部活动,这些运动可以健运命门,补肾纳气。还可多做一些刺激脚心的按摩,中医认为,脚心的涌泉穴是浊气下降的地方,经常按摩涌泉穴,可益精补肾、强身健体、防止早衰,并能舒肝明目,清喉定心,促进睡眠,增进食欲。 162 | 4、充足的睡眠也是恢复精气神的重要保障,工作再紧张,家里的烦心事再多,到了该睡觉的时候也要按时休息。 163 | 健康教育 164 | 1、过度苦寒、冰凉的食物易伤肾,如芦荟、苦瓜、雪糕、鹅肉、啤酒进食过多都伤肾,应该多食黑色素含量高和温补性中药如黑米黑豆等。 165 | 2、男性接触过多的洗涤剂也伤肾,家庭应少用洗涤剂清洗餐具及蔬果,以免洗涤剂残留物被过多摄入。 166 | 3、适当运动可延缓衰老,但强度不宜太大,应选能力所及的运动项目,以促进血液循环,可改善血淤、气损等情况。散步、慢跑、快步走,或在鹅卵石上赤足适当行走,都会促进血液循环,对肾虚有辅助治疗作用。 167 | 4、保持良好的作息习惯,尽量避免熬夜。 168 | 5、积极参加户外运动,放松心情。 169 | 6、不要给自己太大的压力,学会合理减压。 170 | ****************************************************************************************** 171 | 用户:感冒要多久才能好? 172 | 小勇: 感冒治疗可能持续的周期为:7-14天 173 | ****************************************************************************************** 174 | 用户:高血压要怎么治? 175 | 小勇: 高血压可以尝试如下治疗:药物治疗;手术治疗;支持性治疗 176 | ****************************************************************************************** 177 | 用户:白血病能治好吗? 178 | 小勇: 白血病治愈的概率为(仅供参考):50%-70% 179 | ****************************************************************************************** 180 | 用户:什么人容易得高血压? 181 | 小勇: 高血压的易感人群包括:有高血压家族史,不良的生活习惯,缺乏运动的人群 182 | ****************************************************************************************** 183 | 用户:糖尿病 184 | 小勇: 糖尿病,熟悉一下:糖尿病是一种比较常见的内分泌代谢性疾病。该病发病原因主要是由于胰岛素分泌不足,以及胰升高血糖素不适当地分泌过多所引起。多见于40岁以上喜食甜食而肥胖的病人,城市多于农村,常有家族史,故与遗传有关。少数病人与病毒感染和自身免疫反应有关。主要表现为烦渴、多饮、多尿、多食、乏力、消瘦等症状。生命的常见病,伴发高血压、冠心病、高脂血症等,严重时危及生命。 185 | 中医学认为,肝主疏泄,关系人体接收机的升降与调畅,肝气郁滞则气机升降输布紊乱,肝失疏泄则血糖等精微物质不能随清阳之气输布于周身而郁滞于血中,出现高血糖或精微物质的输布紊乱,反见血糖升高,进一步导致血脂、蛋白等其它精微物质紊乱,引起其他合并症,治疗以疏肝调气为主,顺肝条达之性以恢复其生理功能,肝气条达,气机调畅,精微得以输布,糖被利用而血糖自然下降。 186 | 另外,因糖尿病的发生和饮食有关,饮食控制的好坏直接影响着治疗的效果。再就是配合运动,注意调摄情志,再适当的配合中药治疗会取得良好的治疗效果。  187 | ****************************************************************************************** 188 | 用户:全血细胞计数能查出啥来 189 | 小勇: 通常可以通过全血细胞计数检查出来的疾病有成人类风湿性关节炎性巩膜炎;外阴-阴道-牙龈综合征;电击伤;老年收缩期高血压;小儿肝硬化;异常血红蛋白病;痴呆综合征;高血压病伴发的精神障碍;睾丸淋巴瘤;叶酸缺乏所致贫血;眼球内炎;不稳定血红蛋白病;类癌综合征;老年痴呆;急性淋巴管炎;宫颈妊娠;蚕食性角膜溃疡;低增生性急性白血病;交感性眼炎;原发性免疫缺陷病 190 | 191 | #前端在线问答展示 192 | ![image](img/QA.png) 193 | 194 | # 总结 195 | 1、本项目完成了从无到有,以垂直网站为数据来源,构建起以疾病为中心的医疗知识图谱,实体规模4.4万,实体关系规模30万。并基于此,搭建起了一个可以回答18类问题的自动问答小系统,总共耗时3天。其中,数据采集与整理1天,知识图谱构建与入库0.5天,问答系统组件1.5天。总的来说,还是比较快速。 196 | 2、本项目以业务驱动,构建医疗知识图谱,知识schema设计基于所采集的结构化数据生成(对网页结构化数据进行xpath解析)。 197 | 3、本项目以neo4j作为存储,并基于传统规则的方式完成了知识问答,并最终以cypher查询语句作为问答搜索sql,支持了问答服务。 198 | 4、本项目可以快速部署,数据已经放在data/medical.json当中,本项目的数据,如侵犯相关单位权益,请联系我删除。本数据请勿商用,以免引起不必要的纠纷。在本项目中的部署上,可以遵循项目运行步骤,完成数据库搭建,并提供搜索服务。 199 | 5、本项目还有不足:关于疾病的起因、预防等,实际返回的是一大段文字,这里其实可以引入事件抽取的概念,进一步将原因结构化表示出来。这个可以后面进行尝试。 200 | 201 | 202 | 203 | 204 | 205 | -------------------------------------------------------------------------------- /answer_search.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # coding: utf-8 3 | # File: answer_search.py 4 | 5 | 6 | from py2neo import Graph 7 | 8 | class AnswerSearcher: 9 | def __init__(self): 10 | self.g = Graph( 11 | host="127.0.0.1", 12 | http_port=7474, 13 | user="neo4j", 14 | password="125525") 15 | self.num_limit = 20 16 | 17 | '''执行cypher查询,并返回相应结果''' 18 | def search_main(self, sqls): 19 | final_answers = [] 20 | for sql_ in sqls: 21 | question_type = sql_['question_type'] 22 | queries = sql_['sql'] 23 | answers = [] 24 | for query in queries: 25 | ress = self.g.run(query).data() 26 | answers += ress 27 | final_answer = self.answer_prettify(question_type, answers) 28 | if final_answer: 29 | final_answers.append(final_answer) 30 | return final_answers 31 | 32 | '''根据对应的qustion_type,调用相应的回复模板''' 33 | def answer_prettify(self, question_type, answers): 34 | final_answer = [] 35 | if not answers: 36 | return '' 37 | if question_type == 'disease_symptom': 38 | desc = [i['n.name'] for i in answers] 39 | subject = answers[0]['m.name'] 40 | final_answer = '{0}的症状包括:{1}'.format(subject, ';'.join(list(set(desc))[:self.num_limit])) 41 | 42 | elif question_type == 'symptom_disease': 43 | desc = [i['m.name'] for i in answers] 44 | subject = answers[0]['n.name'] 45 | final_answer = '症状{0}可能染上的疾病有:{1}'.format(subject, ';'.join(list(set(desc))[:self.num_limit])) 46 | 47 | elif question_type == 'disease_cause': 48 | desc = [i['m.cause'] for i in answers] 49 | subject = answers[0]['m.name'] 50 | final_answer = '{0}可能的成因有:{1}'.format(subject, ';'.join(list(set(desc))[:self.num_limit])) 51 | 52 | elif question_type == 'disease_prevent': 53 | desc = [i['m.prevent'] for i in answers] 54 | subject = answers[0]['m.name'] 55 | final_answer = '{0}的预防措施包括:{1}'.format(subject, ';'.join(list(set(desc))[:self.num_limit])) 56 | 57 | elif question_type == 'disease_lasttime': 58 | desc = [i['m.cure_lasttime'] for i in answers] 59 | subject = answers[0]['m.name'] 60 | final_answer = '{0}治疗可能持续的周期为:{1}'.format(subject, ';'.join(list(set(desc))[:self.num_limit])) 61 | 62 | elif question_type == 'disease_cureway': 63 | desc = [';'.join(i['m.cure_way']) for i in answers] 64 | subject = answers[0]['m.name'] 65 | final_answer = '{0}可以尝试如下治疗:{1}'.format(subject, ';'.join(list(set(desc))[:self.num_limit])) 66 | 67 | elif question_type == 'disease_cureprob': 68 | desc = [i['m.cured_prob'] for i in answers] 69 | subject = answers[0]['m.name'] 70 | final_answer = '{0}治愈的概率为(仅供参考):{1}'.format(subject, ';'.join(list(set(desc))[:self.num_limit])) 71 | 72 | elif question_type == 'disease_easyget': 73 | desc = [i['m.easy_get'] for i in answers] 74 | subject = answers[0]['m.name'] 75 | 76 | final_answer = '{0}的易感人群包括:{1}'.format(subject, ';'.join(list(set(desc))[:self.num_limit])) 77 | 78 | elif question_type == 'disease_desc': 79 | desc = [i['m.desc'] for i in answers] 80 | subject = answers[0]['m.name'] 81 | final_answer = '{0},熟悉一下:{1}'.format(subject, ';'.join(list(set(desc))[:self.num_limit])) 82 | 83 | elif question_type == 'disease_acompany': 84 | desc1 = [i['n.name'] for i in answers] 85 | desc2 = [i['m.name'] for i in answers] 86 | subject = answers[0]['m.name'] 87 | desc = [i for i in desc1 + desc2 if i != subject] 88 | final_answer = '{0}的症状包括:{1}'.format(subject, ';'.join(list(set(desc))[:self.num_limit])) 89 | 90 | elif question_type == 'disease_not_food': 91 | desc = [i['n.name'] for i in answers] 92 | subject = answers[0]['m.name'] 93 | final_answer = '{0}忌食的食物包括有:{1}'.format(subject, ';'.join(list(set(desc))[:self.num_limit])) 94 | 95 | elif question_type == 'disease_do_food': 96 | do_desc = [i['n.name'] for i in answers if i['r.name'] == '宜吃'] 97 | recommand_desc = [i['n.name'] for i in answers if i['r.name'] == '推荐食谱'] 98 | subject = answers[0]['m.name'] 99 | final_answer = '{0}宜食的食物包括有:{1}\n推荐食谱包括有:{2}'.format(subject, ';'.join(list(set(do_desc))[:self.num_limit]), ';'.join(list(set(recommand_desc))[:self.num_limit])) 100 | 101 | elif question_type == 'food_not_disease': 102 | desc = [i['m.name'] for i in answers] 103 | subject = answers[0]['n.name'] 104 | final_answer = '患有{0}的人最好不要吃{1}'.format(';'.join(list(set(desc))[:self.num_limit]), subject) 105 | 106 | elif question_type == 'food_do_disease': 107 | desc = [i['m.name'] for i in answers] 108 | subject = answers[0]['n.name'] 109 | final_answer = '患有{0}的人建议多试试{1}'.format(';'.join(list(set(desc))[:self.num_limit]), subject) 110 | 111 | elif question_type == 'disease_drug': 112 | desc = [i['n.name'] for i in answers] 113 | subject = answers[0]['m.name'] 114 | final_answer = '{0}通常的使用的药品包括:{1}'.format(subject, ';'.join(list(set(desc))[:self.num_limit])) 115 | 116 | elif question_type == 'drug_disease': 117 | desc = [i['m.name'] for i in answers] 118 | subject = answers[0]['n.name'] 119 | final_answer = '{0}主治的疾病有{1},可以试试'.format(subject, ';'.join(list(set(desc))[:self.num_limit])) 120 | 121 | elif question_type == 'disease_check': 122 | desc = [i['n.name'] for i in answers] 123 | subject = answers[0]['m.name'] 124 | final_answer = '{0}通常可以通过以下方式检查出来:{1}'.format(subject, ';'.join(list(set(desc))[:self.num_limit])) 125 | 126 | elif question_type == 'check_disease': 127 | desc = [i['m.name'] for i in answers] 128 | subject = answers[0]['n.name'] 129 | final_answer = '通常可以通过{0}检查出来的疾病有{1}'.format(subject, ';'.join(list(set(desc))[:self.num_limit])) 130 | 131 | return final_answer 132 | 133 | 134 | if __name__ == '__main__': 135 | searcher = AnswerSearcher() -------------------------------------------------------------------------------- /build_medicalgraph.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # coding: utf-8 3 | # File: MedicalGraph.py 4 | # Author: lhy 5 | # Date: 18-10-3 6 | 7 | import os 8 | import json 9 | from py2neo import Graph,Node 10 | 11 | class MedicalGraph: 12 | def __init__(self): 13 | cur_dir = '/'.join(os.path.abspath(__file__).split('/')[:-1]) 14 | self.data_path = os.path.join(cur_dir, 'data/medical.json') 15 | self.g = Graph( 16 | host="127.0.0.1", # neo4j 搭载服务器的ip地址,ifconfig可获取到 17 | http_port=7474, # neo4j 服务器监听的端口号,默认7687,UI默认7474 18 | user="neo4j", # 数据库user name,如果没有更改过,应该是neo4j 19 | password="125525") 20 | 21 | '''读取文件''' 22 | def read_nodes(self): 23 | # 共7类节点 24 | drugs = [] # 药品 25 | foods = [] # 食物 26 | checks = [] # 检查 27 | departments = [] #科室 28 | producers = [] #药品大类 29 | diseases = [] #疾病 30 | symptoms = []#症状 31 | 32 | disease_infos = []#疾病信息 33 | 34 | # 构建节点实体关系 35 | rels_department = [] # 科室-科室关系 36 | rels_noteat = [] # 疾病-忌吃食物关系 37 | rels_doeat = [] # 疾病-宜吃食物关系 38 | rels_recommandeat = [] # 疾病-推荐吃食物关系 39 | rels_commonddrug = [] # 疾病-通用药品关系 40 | rels_recommanddrug = [] # 疾病-热门药品关系 41 | rels_check = [] # 疾病-检查关系 42 | rels_drug_producer = [] # 厂商-药物关系 43 | 44 | rels_symptom = [] #疾病症状关系 45 | rels_acompany = [] # 疾病并发关系 46 | rels_category = [] # 疾病与科室之间的关系 47 | 48 | 49 | count = 0 50 | for data in open(self.data_path): 51 | disease_dict = {} 52 | count += 1 53 | print(count) 54 | data_json = json.loads(data) 55 | disease = data_json['name'] 56 | disease_dict['name'] = disease 57 | diseases.append(disease) 58 | disease_dict['desc'] = '' 59 | disease_dict['prevent'] = '' 60 | disease_dict['cause'] = '' 61 | disease_dict['easy_get'] = '' 62 | disease_dict['cure_department'] = '' 63 | disease_dict['cure_way'] = '' 64 | disease_dict['cure_lasttime'] = '' 65 | disease_dict['symptom'] = '' 66 | disease_dict['cured_prob'] = '' 67 | 68 | if 'symptom' in data_json: 69 | symptoms += data_json['symptom'] 70 | for symptom in data_json['symptom']: 71 | rels_symptom.append([disease, symptom]) 72 | 73 | if 'acompany' in data_json: 74 | for acompany in data_json['acompany']: 75 | rels_acompany.append([disease, acompany]) 76 | 77 | if 'desc' in data_json: 78 | disease_dict['desc'] = data_json['desc'] 79 | 80 | if 'prevent' in data_json: 81 | disease_dict['prevent'] = data_json['prevent'] 82 | 83 | if 'cause' in data_json: 84 | disease_dict['cause'] = data_json['cause'] 85 | 86 | if 'get_prob' in data_json: 87 | disease_dict['get_prob'] = data_json['get_prob'] 88 | 89 | if 'easy_get' in data_json: 90 | disease_dict['easy_get'] = data_json['easy_get'] 91 | 92 | if 'cure_department' in data_json: 93 | cure_department = data_json['cure_department'] 94 | if len(cure_department) == 1: 95 | rels_category.append([disease, cure_department[0]]) 96 | if len(cure_department) == 2: 97 | #cure_department在json中有两种形式,除了添加cure_department属性到disease_dict实体字典里和departments实体列表里。还需要提取关系,如果只有一个科室则直接提取疾病—科室关系(rels_category), 98 | # 如果有两个科室,还需要提取科室—科室关系(rels_department) 99 | big = cure_department[0] 100 | small = cure_department[1] 101 | rels_department.append([small, big]) 102 | rels_category.append([disease, small]) 103 | 104 | disease_dict['cure_department'] = cure_department 105 | departments += cure_department 106 | 107 | if 'cure_way' in data_json: 108 | disease_dict['cure_way'] = data_json['cure_way'] 109 | 110 | if 'cure_lasttime' in data_json: 111 | disease_dict['cure_lasttime'] = data_json['cure_lasttime'] 112 | 113 | if 'cured_prob' in data_json: 114 | disease_dict['cured_prob'] = data_json['cured_prob'] 115 | 116 | if 'common_drug' in data_json: 117 | common_drug = data_json['common_drug'] 118 | for drug in common_drug: 119 | rels_commonddrug.append([disease, drug]) 120 | drugs += common_drug 121 | 122 | if 'recommand_drug' in data_json: 123 | recommand_drug = data_json['recommand_drug'] 124 | drugs += recommand_drug 125 | for drug in recommand_drug: 126 | rels_recommanddrug.append([disease, drug]) 127 | 128 | if 'not_eat' in data_json: 129 | not_eat = data_json['not_eat'] 130 | for _not in not_eat: 131 | rels_noteat.append([disease, _not]) 132 | 133 | foods += not_eat 134 | do_eat = data_json['do_eat'] 135 | for _do in do_eat: 136 | rels_doeat.append([disease, _do]) 137 | 138 | foods += do_eat 139 | recommand_eat = data_json['recommand_eat'] 140 | 141 | for _recommand in recommand_eat: 142 | rels_recommandeat.append([disease, _recommand]) 143 | foods += recommand_eat 144 | 145 | if 'check' in data_json: 146 | check = data_json['check'] 147 | for _check in check: 148 | rels_check.append([disease, _check]) 149 | checks += check 150 | if 'drug_detail' in data_json: 151 | drug_detail = data_json['drug_detail'] 152 | producer = [i.split('(')[0] for i in drug_detail]#厂商 153 | #drug_details的形式为"drug_detail" : [ "惠普森穿心莲内酯片(穿心莲内酯片)", "北京同仁堂百咳静糖浆(百咳静糖浆)"], 154 | # 即包括药品名和生产厂商,因为字符串和括号的原因,提取药品—在售药品的关系的方式略有不同 155 | rels_drug_producer += [[i.split('(')[0], i.split('(')[-1].replace(')', '')] for i in drug_detail] 156 | producers += producer 157 | disease_infos.append(disease_dict) 158 | return set(drugs), set(foods), set(checks), set(departments), set(producers), set(symptoms), set(diseases), disease_infos,\ 159 | rels_check, rels_recommandeat, rels_noteat, rels_doeat, rels_department, rels_commonddrug, rels_drug_producer, rels_recommanddrug,\ 160 | rels_symptom, rels_acompany, rels_category 161 | 162 | '''建立节点:知识图谱中主要包含两类节点,一类为中心疾病节点,包含各种疾病属性;一类为普通实体节点,即药品、食物等,不包含属性,''' 163 | '''创建普通节点(不含属性)''' 164 | def create_node(self, label, nodes): 165 | count = 0 166 | for node_name in nodes: 167 | node = Node(label, name=node_name) 168 | self.g.create(node) 169 | count += 1 170 | print(count, len(nodes)) 171 | return 172 | 173 | '''创建知识图谱中心疾病的节点''' 174 | def create_diseases_nodes(self, disease_infos): 175 | count = 0 176 | for disease_dict in disease_infos: 177 | node = Node("Disease", name=disease_dict['name'], desc=disease_dict['desc'], 178 | prevent=disease_dict['prevent'] ,cause=disease_dict['cause'], 179 | easy_get=disease_dict['easy_get'],cure_lasttime=disease_dict['cure_lasttime'], 180 | cure_department=disease_dict['cure_department'] 181 | ,cure_way=disease_dict['cure_way'] , cured_prob=disease_dict['cured_prob']) 182 | self.g.create(node) 183 | count += 1 184 | print(count) 185 | return 186 | 187 | '''创建知识图谱实体节点类型schema''' 188 | def create_graphnodes(self): 189 | Drugs, Foods, Checks, Departments, Producers, Symptoms, Diseases, disease_infos,rels_check, rels_recommandeat, rels_noteat, rels_doeat, rels_department, rels_commonddrug, rels_drug_producer, rels_recommanddrug,rels_symptom, rels_acompany, rels_category = self.read_nodes() 190 | self.create_diseases_nodes(disease_infos) 191 | self.create_node('Drug', Drugs) 192 | print(len(Drugs)) 193 | self.create_node('Food', Foods) 194 | print(len(Foods)) 195 | self.create_node('Check', Checks) 196 | print(len(Checks)) 197 | self.create_node('Department', Departments) 198 | print(len(Departments)) 199 | self.create_node('Producer', Producers) 200 | print(len(Producers)) 201 | self.create_node('Symptom', Symptoms) 202 | return 203 | 204 | 205 | '''创建实体关系边''' 206 | def create_graphrels(self): 207 | Drugs, Foods, Checks, Departments, Producers, Symptoms, Diseases, disease_infos, rels_check, rels_recommandeat, rels_noteat, rels_doeat, rels_department, rels_commonddrug, rels_drug_producer, rels_recommanddrug,rels_symptom, rels_acompany, rels_category = self.read_nodes() 208 | self.create_relationship('Disease', 'Food', rels_recommandeat, 'recommand_eat', '推荐食谱') 209 | self.create_relationship('Disease', 'Food', rels_noteat, 'no_eat', '忌吃') 210 | self.create_relationship('Disease', 'Food', rels_doeat, 'do_eat', '宜吃') 211 | self.create_relationship('Department', 'Department', rels_department, 'belongs_to', '属于') 212 | self.create_relationship('Disease', 'Drug', rels_commonddrug, 'common_drug', '常用药品') 213 | self.create_relationship('Producer', 'Drug', rels_drug_producer, 'drugs_of', '生产药品') 214 | self.create_relationship('Disease', 'Drug', rels_recommanddrug, 'recommand_drug', '好评药品') 215 | self.create_relationship('Disease', 'Check', rels_check, 'need_check', '诊断检查') 216 | self.create_relationship('Disease', 'Symptom', rels_symptom, 'has_symptom', '症状') 217 | self.create_relationship('Disease', 'Disease', rels_acompany, 'acompany_with', '并发症') 218 | self.create_relationship('Disease', 'Department', rels_category, 'belongs_to', '所属科室') 219 | 220 | '''创建实体关联边''' 221 | def create_relationship(self, start_node, end_node, edges, rel_type, rel_name): 222 | count = 0 223 | # 去重处理 224 | set_edges = [] 225 | for edge in edges: 226 | set_edges.append('###'.join(edge)) 227 | all = len(set(set_edges)) 228 | for edge in set(set_edges): 229 | edge = edge.split('###') 230 | p = edge[0] 231 | q = edge[1] 232 | query = "match(p:%s),(q:%s) where p.name='%s'and q.name='%s' create (p)-[rel:%s{name:'%s'}]->(q)" % ( 233 | start_node, end_node, p, q, rel_type, rel_name) 234 | try: 235 | self.g.run(query) 236 | count += 1 237 | print(rel_type, count, all) 238 | except Exception as e: 239 | print(e) 240 | return 241 | 242 | '''导出数据''' 243 | def export_data(self): 244 | Drugs, Foods, Checks, Departments, Producers, Symptoms, Diseases, disease_infos, rels_check, rels_recommandeat, rels_noteat, rels_doeat, rels_department, rels_commonddrug, rels_drug_producer, rels_recommanddrug, rels_symptom, rels_acompany, rels_category = self.read_nodes() 245 | f_drug = open('drug.txt', 'w+') 246 | f_food = open('food.txt', 'w+') 247 | f_check = open('check.txt', 'w+') 248 | f_department = open('department.txt', 'w+') 249 | f_producer = open('producer.txt', 'w+') 250 | f_symptom = open('symptoms.txt', 'w+') 251 | f_disease = open('disease.txt', 'w+') 252 | 253 | f_drug.write('\n'.join(list(Drugs))) 254 | f_food.write('\n'.join(list(Foods))) 255 | f_check.write('\n'.join(list(Checks))) 256 | f_department.write('\n'.join(list(Departments))) 257 | f_producer.write('\n'.join(list(Producers))) 258 | f_symptom.write('\n'.join(list(Symptoms))) 259 | f_disease.write('\n'.join(list(Diseases))) 260 | 261 | f_drug.close() 262 | f_food.close() 263 | f_check.close() 264 | f_department.close() 265 | f_producer.close() 266 | f_symptom.close() 267 | f_disease.close() 268 | 269 | return 270 | 271 | 272 | 273 | if __name__ == '__main__': 274 | handler = MedicalGraph() 275 | handler.create_graphnodes() 276 | handler.create_graphrels() 277 | # handler.export_data() 278 | -------------------------------------------------------------------------------- /chatbot_graph.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # coding: utf-8 3 | # File: chatbot_graph.py 4 | 5 | 6 | from question_classifier import * 7 | from question_parser import * 8 | from answer_search import * 9 | 10 | '''问答类''' 11 | class ChatBotGraph: 12 | def __init__(self): 13 | self.classifier = QuestionClassifier() 14 | self.parser = QuestionPaser() 15 | self.searcher = AnswerSearcher() 16 | 17 | def chat_main(self, sent): 18 | answer = '您好,我是小倪医药智能助理,希望可以帮到您。如果没答上来,可联系https://nynCoder.github.io/。祝您身体棒棒!' 19 | res_classify = self.classifier.classify(sent) 20 | if not res_classify: 21 | return answer 22 | res_sql = self.parser.parser_main(res_classify) 23 | final_answers = self.searcher.search_main(res_sql) 24 | if not final_answers and "谢谢" not in sent: 25 | return answer 26 | elif "谢谢" in sent: 27 | return "不客气哒,有问题随时找我哦!" 28 | else: 29 | return '\n'.join(final_answers) 30 | 31 | if __name__ == '__main__': 32 | handler = ChatBotGraph() 33 | while 1: 34 | question = input('用户:') 35 | answer = handler.chat_main(question) 36 | print('小倪:', answer) 37 | 38 | -------------------------------------------------------------------------------- /img/chat1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nynCoder/Medical-QA/c448008af8b94a9cc1a7d7ccbd0dee29de3f9cb9/img/chat1.png -------------------------------------------------------------------------------- /img/chat2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nynCoder/Medical-QA/c448008af8b94a9cc1a7d7ccbd0dee29de3f9cb9/img/chat2.png -------------------------------------------------------------------------------- /img/graph_summary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nynCoder/Medical-QA/c448008af8b94a9cc1a7d7ccbd0dee29de3f9cb9/img/graph_summary.png -------------------------------------------------------------------------------- /img/kg_route.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nynCoder/Medical-QA/c448008af8b94a9cc1a7d7ccbd0dee29de3f9cb9/img/kg_route.png -------------------------------------------------------------------------------- /img/qa_route.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nynCoder/Medical-QA/c448008af8b94a9cc1a7d7ccbd0dee29de3f9cb9/img/qa_route.png -------------------------------------------------------------------------------- /prepare_data/build_data.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # coding: utf-8 3 | # File: build_data.py 4 | # Author: lhy 5 | # Date: 18-10-3 6 | import pymongo 7 | from lxml import etree 8 | import os 9 | from max_cut import * 10 | 11 | class MedicalGraph: 12 | def __init__(self): 13 | self.conn = pymongo.MongoClient() 14 | cur_dir = '/'.join(os.path.abspath(__file__).split('/')[:-1]) 15 | self.db = self.conn['medical'] 16 | self.col = self.db['data'] 17 | first_words = [i.strip() for i in open(os.path.join(cur_dir, 'first_name.txt'))] 18 | alphabets = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y', 'z'] 19 | nums = ['1','2','3','4','5','6','7','8','9','0'] 20 | self.stop_words = first_words + alphabets + nums 21 | self.key_dict = { 22 | '医保疾病' : 'yibao_status', 23 | "患病比例" : "get_prob", 24 | "易感人群" : "easy_get", 25 | "传染方式" : "get_way", 26 | "就诊科室" : "cure_department", 27 | "治疗方式" : "cure_way", 28 | "治疗周期" : "cure_lasttime", 29 | "治愈率" : "cured_prob", 30 | '药品明细': 'drug_detail', 31 | '药品推荐': 'recommand_drug', 32 | '推荐': 'recommand_eat', 33 | '忌食': 'not_eat', 34 | '宜食': 'do_eat', 35 | '症状': 'symptom', 36 | '检查': 'check', 37 | '成因': 'cause', 38 | '预防措施': 'prevent', 39 | '所属类别': 'category', 40 | '简介': 'desc', 41 | '名称': 'name', 42 | '常用药品' : 'common_drug', 43 | '治疗费用': 'cost_money', 44 | '并发症': 'acompany' 45 | } 46 | self.cuter = CutWords() 47 | 48 | def collect_medical(self): 49 | cates = [] 50 | inspects = [] 51 | count = 0 52 | for item in self.col.find(): 53 | data = {} 54 | basic_info = item['basic_info'] 55 | name = basic_info['name'] 56 | if not name: 57 | continue 58 | # 基本信息 59 | data['名称'] = name 60 | data['简介'] = '\n'.join(basic_info['desc']).replace('\r\n\t', '').replace('\r\n\n\n','').replace(' ','').replace('\r\n','\n') 61 | category = basic_info['category'] 62 | data['所属类别'] = category 63 | cates += category 64 | inspect = item['inspect_info'] 65 | inspects += inspect 66 | attributes = basic_info['attributes'] 67 | # 成因及预防 68 | data['预防措施'] = item['prevent_info'] 69 | data['成因'] = item['cause_info'] 70 | # 并发症 71 | data['症状'] = list(set([i for i in item["symptom_info"][0] if i[0] not in self.stop_words])) 72 | for attr in attributes: 73 | attr_pair = attr.split(':') 74 | if len(attr_pair) == 2: 75 | key = attr_pair[0] 76 | value = attr_pair[1] 77 | data[key] = value 78 | # 检查 79 | inspects = item['inspect_info'] 80 | jcs = [] 81 | for inspect in inspects: 82 | jc_name = self.get_inspect(inspect) 83 | if jc_name: 84 | jcs.append(jc_name) 85 | data['检查'] = jcs 86 | # 食物 87 | food_info = item['food_info'] 88 | if food_info: 89 | data['宜食'] = food_info['good'] 90 | data['忌食'] = food_info['bad'] 91 | data['推荐'] = food_info['recommand'] 92 | # 药品 93 | drug_info = item['drug_info'] 94 | data['药品推荐'] = list(set([i.split('(')[-1].replace(')','') for i in drug_info])) 95 | data['药品明细'] = drug_info 96 | data_modify = {} 97 | for attr, value in data.items(): 98 | attr_en = self.key_dict.get(attr) 99 | if attr_en: 100 | data_modify[attr_en] = value 101 | if attr_en in ['yibao_status', 'get_prob', 'easy_get', 'get_way', "cure_lasttime", "cured_prob"]: 102 | data_modify[attr_en] = value.replace(' ','').replace('\t','') 103 | elif attr_en in ['cure_department', 'cure_way', 'common_drug']: 104 | data_modify[attr_en] = [i for i in value.split(' ') if i] 105 | elif attr_en in ['acompany']: 106 | acompany = [i for i in self.cuter.max_biward_cut(data_modify[attr_en]) if len(i) > 1] 107 | data_modify[attr_en] = acompany 108 | 109 | try: 110 | self.db['medical'].insert(data_modify) 111 | count += 1 112 | print(count) 113 | except Exception as e: 114 | print(e) 115 | 116 | return 117 | 118 | 119 | def get_inspect(self, url): 120 | res = self.db['jc'].find_one({'url':url}) 121 | if not res: 122 | return '' 123 | else: 124 | return res['name'] 125 | 126 | def modify_jc(self): 127 | for item in self.db['jc'].find(): 128 | url = item['url'] 129 | content = item['html'] 130 | selector = etree.HTML(content) 131 | name = selector.xpath('//title/text()')[0].split('结果分析')[0] 132 | desc = selector.xpath('//meta[@name="description"]/@content')[0].replace('\r\n\t','') 133 | self.db['jc'].update({'url':url}, {'$set':{'name':name, 'desc':desc}}) 134 | 135 | 136 | 137 | if __name__ == '__main__': 138 | handler = MedicalGraph() 139 | -------------------------------------------------------------------------------- /prepare_data/data_spider.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # coding: utf-8 3 | # File: data_spider.py 4 | # Author: lhy 5 | # Date: 18-10-3 6 | 7 | 8 | import urllib.request 9 | import urllib.parse 10 | from lxml import etree 11 | import pymongo 12 | import re 13 | 14 | '''基于司法网的犯罪案件采集''' 15 | class CrimeSpider: 16 | def __init__(self): 17 | self.conn = pymongo.MongoClient() 18 | self.db = self.conn['medical'] 19 | self.col = self.db['data'] 20 | 21 | '''根据url,请求html''' 22 | def get_html(self, url): 23 | headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) ' 24 | 'Chrome/51.0.2704.63 Safari/537.36'} 25 | req = urllib.request.Request(url=url, headers=headers) 26 | res = urllib.request.urlopen(req) 27 | html = res.read().decode('gbk') 28 | return html 29 | 30 | '''url解析''' 31 | def url_parser(self, content): 32 | selector = etree.HTML(content) 33 | urls = ['http://www.anliguan.com' + i for i in selector.xpath('//h2[@class="item-title"]/a/@href')] 34 | return urls 35 | 36 | '''测试''' 37 | def spider_main(self): 38 | for page in range(1, 11000): 39 | try: 40 | basic_url = 'http://jib.xywy.com/il_sii/gaishu/%s.htm'%page 41 | cause_url = 'http://jib.xywy.com/il_sii/cause/%s.htm'%page 42 | prevent_url = 'http://jib.xywy.com/il_sii/prevent/%s.htm'%page 43 | symptom_url = 'http://jib.xywy.com/il_sii/symptom/%s.htm'%page 44 | inspect_url = 'http://jib.xywy.com/il_sii/inspect/%s.htm'%page 45 | treat_url = 'http://jib.xywy.com/il_sii/treat/%s.htm'%page 46 | food_url = 'http://jib.xywy.com/il_sii/food/%s.htm'%page 47 | drug_url = 'http://jib.xywy.com/il_sii/drug/%s.htm'%page 48 | data = {} 49 | data['url'] = basic_url 50 | data['basic_info'] = self.basicinfo_spider(basic_url) 51 | data['cause_info'] = self.common_spider(cause_url) 52 | data['prevent_info'] = self.common_spider(prevent_url) 53 | data['symptom_info'] = self.symptom_spider(symptom_url) 54 | data['inspect_info'] = self.inspect_spider(inspect_url) 55 | data['treat_info'] = self.treat_spider(treat_url) 56 | data['food_info'] = self.food_spider(food_url) 57 | data['drug_info'] = self.drug_spider(drug_url) 58 | print(page, basic_url) 59 | self.col.insert(data) 60 | 61 | except Exception as e: 62 | print(e, page) 63 | return 64 | 65 | '''基本信息解析''' 66 | def basicinfo_spider(self, url): 67 | html = self.get_html(url) 68 | selector = etree.HTML(html) 69 | title = selector.xpath('//title/text()')[0] 70 | category = selector.xpath('//div[@class="wrap mt10 nav-bar"]/a/text()') 71 | desc = selector.xpath('//div[@class="jib-articl-con jib-lh-articl"]/p/text()') 72 | ps = selector.xpath('//div[@class="mt20 articl-know"]/p') 73 | infobox = [] 74 | for p in ps: 75 | info = p.xpath('string(.)').replace('\r','').replace('\n','').replace('\xa0', '').replace(' ', '').replace('\t','') 76 | infobox.append(info) 77 | basic_data = {} 78 | basic_data['category'] = category 79 | basic_data['name'] = title.split('的简介')[0] 80 | basic_data['desc'] = desc 81 | basic_data['attributes'] = infobox 82 | return basic_data 83 | 84 | '''treat_infobox治疗解析''' 85 | def treat_spider(self, url): 86 | html = self.get_html(url) 87 | selector = etree.HTML(html) 88 | ps = selector.xpath('//div[starts-with(@class,"mt20 articl-know")]/p') 89 | infobox = [] 90 | for p in ps: 91 | info = p.xpath('string(.)').replace('\r','').replace('\n','').replace('\xa0', '').replace(' ', '').replace('\t','') 92 | infobox.append(info) 93 | return infobox 94 | 95 | '''treat_infobox治疗解析''' 96 | def drug_spider(self, url): 97 | html = self.get_html(url) 98 | selector = etree.HTML(html) 99 | drugs = [i.replace('\n','').replace('\t', '').replace(' ','') for i in selector.xpath('//div[@class="fl drug-pic-rec mr30"]/p/a/text()')] 100 | return drugs 101 | 102 | '''food治疗解析''' 103 | def food_spider(self, url): 104 | html = self.get_html(url) 105 | selector = etree.HTML(html) 106 | divs = selector.xpath('//div[@class="diet-img clearfix mt20"]') 107 | try: 108 | food_data = {} 109 | food_data['good'] = divs[0].xpath('./div/p/text()') 110 | food_data['bad'] = divs[1].xpath('./div/p/text()') 111 | food_data['recommand'] = divs[2].xpath('./div/p/text()') 112 | except: 113 | return {} 114 | 115 | return food_data 116 | 117 | '''症状信息解析''' 118 | def symptom_spider(self, url): 119 | html = self.get_html(url) 120 | selector = etree.HTML(html) 121 | symptoms = selector.xpath('//a[@class="gre" ]/text()') 122 | ps = selector.xpath('//p') 123 | detail = [] 124 | for p in ps: 125 | info = p.xpath('string(.)').replace('\r','').replace('\n','').replace('\xa0', '').replace(' ', '').replace('\t','') 126 | detail.append(info) 127 | symptoms_data = {} 128 | symptoms_data['symptoms'] = symptoms 129 | symptoms_data['symptoms_detail'] = detail 130 | return symptoms, detail 131 | 132 | '''检查信息解析''' 133 | def inspect_spider(self, url): 134 | html = self.get_html(url) 135 | selector = etree.HTML(html) 136 | inspects = selector.xpath('//li[@class="check-item"]/a/@href') 137 | return inspects 138 | 139 | '''通用解析模块''' 140 | def common_spider(self, url): 141 | html = self.get_html(url) 142 | selector = etree.HTML(html) 143 | ps = selector.xpath('//p') 144 | infobox = [] 145 | for p in ps: 146 | info = p.xpath('string(.)').replace('\r', '').replace('\n', '').replace('\xa0', '').replace(' ','').replace('\t', '') 147 | if info: 148 | infobox.append(info) 149 | return '\n'.join(infobox) 150 | '''检查项抓取模块''' 151 | def inspect_crawl(self): 152 | for page in range(1, 3685): 153 | try: 154 | url = 'http://jck.xywy.com/jc_%s.html'%page 155 | html = self.get_html(url) 156 | data = {} 157 | data['url']= url 158 | data['html'] = html 159 | self.db['jc'].insert(data) 160 | print(url) 161 | except Exception as e: 162 | print(e) 163 | 164 | 165 | handler = CrimeSpider() 166 | handler.inspect_crawl() -------------------------------------------------------------------------------- /prepare_data/max_cut.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # coding: utf-8 3 | # File: maxmatch.py 4 | # Author: lhy 5 | # Date: 18-3-26 6 | 7 | class CutWords: 8 | def __init__(self): 9 | dict_path = './disease.txt' 10 | self.word_dict, self.max_wordlen = self.load_words(dict_path) 11 | 12 | # 加载词典 13 | def load_words(self, dict_path): 14 | words = list() 15 | max_len = 0 16 | for line in open(dict_path): 17 | wd = line.strip() 18 | if not wd: 19 | continue 20 | if len(wd) > max_len: 21 | max_len = len(wd) 22 | words.append(wd) 23 | return words, max_len 24 | 25 | # 最大向前匹配 26 | def max_forward_cut(self, sent): 27 | # 1.从左向右取待切分汉语句的m个字符作为匹配字段,m为大机器词典中最长词条个数。 28 | # 2.查找大机器词典并进行匹配。若匹配成功,则将这个匹配字段作为一个词切分出来。 29 | cutlist = [] 30 | index = 0 31 | while index < len(sent): 32 | matched = False 33 | for i in range(self.max_wordlen, 0, -1): 34 | cand_word = sent[index: index + i] 35 | if cand_word in self.word_dict: 36 | cutlist.append(cand_word) 37 | matched = True 38 | break 39 | 40 | # 如果没有匹配上,则按字符切分 41 | if not matched: 42 | i = 1 43 | cutlist.append(sent[index]) 44 | index += i 45 | return cutlist 46 | 47 | # 最大向后匹配 48 | def max_backward_cut(self, sent): 49 | # 1.从右向左取待切分汉语句的m个字符作为匹配字段,m为大机器词典中最长词条个数。 50 | # 2.查找大机器词典并进行匹配。若匹配成功,则将这个匹配字段作为一个词切分出来。 51 | cutlist = [] 52 | index = len(sent) 53 | max_wordlen = 5 54 | while index > 0: 55 | matched = False 56 | for i in range(self.max_wordlen, 0, -1): 57 | tmp =i 58 | cand_word = sent[index - tmp: index] 59 | # 如果匹配上,则将字典中的字符加入到切分字符中 60 | if cand_word in self.word_dict: 61 | cutlist.append(cand_word) 62 | matched = True 63 | break 64 | # 如果没有匹配上,则按字符切分 65 | if not matched: 66 | tmp = 1 67 | cutlist.append(sent[index - 1]) 68 | 69 | index -= tmp 70 | 71 | return cutlist[::-1] 72 | 73 | # 双向最大向前匹配 74 | def max_biward_cut(self, sent): 75 | # 双向最大匹配法是将正向最大匹配法得到的分词结果和逆向最大匹配法的到的结果进行比较,从而决定正确的分词方法。 76 | # 启发式规则: 77 | # 1.如果正反向分词结果词数不同,则取分词数量较少的那个。 78 | # 2.如果分词结果词数相同 a.分词结果相同,就说明没有歧义,可返回任意一个。 b.分词结果不同,返回其中单字较少的那个。 79 | forward_cutlist = self.max_forward_cut(sent) 80 | backward_cutlist = self.max_backward_cut(sent) 81 | count_forward = len(forward_cutlist) 82 | count_backward = len(backward_cutlist) 83 | 84 | def compute_single(word_list): 85 | num = 0 86 | for word in word_list: 87 | if len(word) == 1: 88 | num += 1 89 | return num 90 | 91 | if count_forward == count_backward: 92 | if compute_single(forward_cutlist) > compute_single(backward_cutlist): 93 | return backward_cutlist 94 | else: 95 | return forward_cutlist 96 | 97 | elif count_backward > count_forward: 98 | return forward_cutlist 99 | 100 | else: 101 | return backward_cutlist 102 | 103 | -------------------------------------------------------------------------------- /question_classifier.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # coding: utf-8 3 | # File: question_classifier.py 4 | # Author: lhy 5 | # Date: 18-10-4 6 | #讲解链接:https://blog.csdn.net/vivian_ll/article/details/89840281 7 | import os 8 | import ahocorasick 9 | 10 | class QuestionClassifier: 11 | def __init__(self): 12 | cur_dir = '/'.join(os.path.abspath(__file__).split('/')[:-1]) 13 | # 特征词路径(即医学实体词词典) 14 | self.disease_path = os.path.join(cur_dir, 'dict/disease.txt') 15 | self.department_path = os.path.join(cur_dir, 'dict/department.txt') 16 | self.check_path = os.path.join(cur_dir, 'dict/check.txt') 17 | self.drug_path = os.path.join(cur_dir, 'dict/drug.txt') 18 | self.food_path = os.path.join(cur_dir, 'dict/food.txt') 19 | self.producer_path = os.path.join(cur_dir, 'dict/producer.txt') 20 | self.symptom_path = os.path.join(cur_dir, 'dict/symptom.txt') 21 | self.deny_path = os.path.join(cur_dir, 'dict/deny.txt') 22 | # 加载特征词 23 | self.disease_wds= [i.strip() for i in open(self.disease_path) if i.strip()] 24 | self.department_wds= [i.strip() for i in open(self.department_path) if i.strip()] 25 | self.check_wds= [i.strip() for i in open(self.check_path) if i.strip()] 26 | self.drug_wds= [i.strip() for i in open(self.drug_path) if i.strip()] 27 | self.food_wds= [i.strip() for i in open(self.food_path) if i.strip()] 28 | self.producer_wds= [i.strip() for i in open(self.producer_path) if i.strip()] 29 | self.symptom_wds= [i.strip() for i in open(self.symptom_path) if i.strip()] 30 | self.region_words = set(self.department_wds + self.disease_wds + self.check_wds + self.drug_wds + self.food_wds + self.producer_wds + self.symptom_wds) 31 | self.deny_words = [i.strip() for i in open(self.deny_path) if i.strip()] 32 | # 构造领域actree 33 | self.region_tree = self.build_actree(list(self.region_words)) 34 | # 构建词典 35 | self.wdtype_dict = self.build_wdtype_dict() 36 | # 问句疑问词(分类的依据特征词) 37 | self.symptom_qwds = ['症状', '表征', '现象', '症候', '表现'] 38 | self.cause_qwds = ['原因','成因', '为什么', '怎么会', '怎样才', '咋样才', '怎样会', '如何会', '为啥', '为何', '如何才会', '怎么才会', '会导致', '会造成'] 39 | self.acompany_qwds = ['并发症', '并发', '一起发生', '一并发生', '一起出现', '一并出现', '一同发生', '一同出现', '伴随发生', '伴随', '共现'] 40 | self.food_qwds = ['饮食', '饮用', '吃', '食', '伙食', '膳食', '喝', '菜' ,'忌口', '补品', '保健品', '食谱', '菜谱', '食用', '食物','补品'] 41 | self.drug_qwds = ['药', '药品', '用药', '胶囊', '口服液', '炎片'] 42 | self.prevent_qwds = ['预防', '防范', '抵制', '抵御', '防止','躲避','逃避','避开','免得','逃开','避开','避掉','躲开','躲掉','绕开', 43 | '怎样才能不', '怎么才能不', '咋样才能不','咋才能不', '如何才能不', 44 | '怎样才不', '怎么才不', '咋样才不','咋才不', '如何才不', 45 | '怎样才可以不', '怎么才可以不', '咋样才可以不', '咋才可以不', '如何可以不', 46 | '怎样才可不', '怎么才可不', '咋样才可不', '咋才可不', '如何可不'] 47 | self.lasttime_qwds = ['周期', '多久', '多长时间', '多少时间', '几天', '几年', '多少天', '多少小时', '几个小时', '多少年'] 48 | self.cureway_qwds = ['怎么治疗', '如何医治', '怎么医治', '怎么治', '怎么医', '如何治', '医治方式', '疗法', '咋治', '怎么办', '咋办', '咋治'] 49 | self.cureprob_qwds = ['多大概率能治好', '多大几率能治好', '治好希望大么', '几率', '几成', '比例', '可能性', '能治', '可治', '可以治', '可以医'] 50 | self.easyget_qwds = ['易感人群', '容易感染', '易发人群', '什么人', '哪些人', '感染', '染上', '得上'] 51 | self.check_qwds = ['检查', '检查项目', '查出', '检查', '测出', '试出'] 52 | self.belong_qwds = ['属于什么科', '属于', '什么科', '科室'] 53 | self.cure_qwds = ['治疗什么', '治啥', '治疗啥', '医治啥', '治愈啥', '主治啥', '主治什么', '有什么用', '有何用', '用处', '用途', 54 | '有什么好处', '有什么益处', '有何益处', '用来', '用来做啥', '用来作甚', '需要', '要'] 55 | 56 | print('model init finished ......') 57 | 58 | return 59 | 60 | '''分类主函数''' 61 | def classify(self, question): 62 | data = {} 63 | medical_dict = self.check_medical(question) 64 | if not medical_dict: 65 | if "diseases_dict" in globals(): 66 | medical_dict=diseases_dict# 判断是否是首次提问,若首次提问,则diseases_dict无值 67 | else: 68 | return {} 69 | data['args'] = medical_dict 70 | #收集query问句当中所涉及到的实体类型,结合特征词对问句进行分类 71 | types = [] 72 | for type_ in medical_dict.values(): 73 | types += type_ 74 | question_type = 'others' 75 | 76 | question_types = [] 77 | 78 | # 症状 79 | if self.check_words(self.symptom_qwds, question) and ('disease' in types): 80 | question_type = 'disease_symptom' 81 | question_types.append(question_type) 82 | 83 | if self.check_words(self.symptom_qwds, question) and ('symptom' in types): 84 | question_type = 'symptom_disease' 85 | question_types.append(question_type) 86 | 87 | # 原因 88 | if self.check_words(self.cause_qwds, question) and ('disease' in types): 89 | question_type = 'disease_cause' 90 | question_types.append(question_type) 91 | # 并发症 92 | if self.check_words(self.acompany_qwds, question) and ('disease' in types): 93 | question_type = 'disease_acompany' 94 | question_types.append(question_type) 95 | 96 | # 推荐食品 97 | if self.check_words(self.food_qwds, question) and 'disease' in types: 98 | deny_status = self.check_words(self.deny_words, question) 99 | if deny_status: 100 | question_type = 'disease_not_food' 101 | else: 102 | question_type = 'disease_do_food' 103 | question_types.append(question_type) 104 | 105 | #已知食物找疾病 106 | if self.check_words(self.food_qwds+self.cure_qwds, question) and 'food' in types: 107 | deny_status = self.check_words(self.deny_words, question) 108 | if deny_status: 109 | question_type = 'food_not_disease'#忌吃 110 | else: 111 | question_type = 'food_do_disease'#宜吃 112 | question_types.append(question_type) 113 | 114 | # 推荐药品 115 | if self.check_words(self.drug_qwds, question) and 'disease' in types: 116 | question_type = 'disease_drug' 117 | question_types.append(question_type) 118 | 119 | # 药品治啥病 120 | if self.check_words(self.cure_qwds, question) and 'drug' in types: 121 | question_type = 'drug_disease' 122 | question_types.append(question_type) 123 | 124 | # 疾病接受检查项目 125 | if self.check_words(self.check_qwds, question) and 'disease' in types: 126 | question_type = 'disease_check' 127 | question_types.append(question_type) 128 | 129 | # 已知检查项目查相应疾病 130 | if self.check_words(self.check_qwds+self.cure_qwds, question) and 'check' in types: 131 | question_type = 'check_disease' 132 | question_types.append(question_type) 133 | 134 | # 症状防御 135 | if self.check_words(self.prevent_qwds, question) and 'disease' in types: 136 | question_type = 'disease_prevent' 137 | question_types.append(question_type) 138 | 139 | # 疾病医疗周期 140 | if self.check_words(self.lasttime_qwds, question) and 'disease' in types: 141 | question_type = 'disease_lasttime' 142 | question_types.append(question_type) 143 | 144 | # 疾病治疗方式 145 | if self.check_words(self.cureway_qwds, question) and 'disease' in types: 146 | question_type = 'disease_cureway' 147 | question_types.append(question_type) 148 | 149 | # 疾病治愈可能性 150 | if self.check_words(self.cureprob_qwds, question) and 'disease' in types: 151 | question_type = 'disease_cureprob' 152 | question_types.append(question_type) 153 | 154 | # 疾病易感染人群 155 | if self.check_words(self.easyget_qwds, question) and 'disease' in types : 156 | question_type = 'disease_easyget' 157 | question_types.append(question_type) 158 | 159 | # 若没有查到相关的外部查询信息,那么则将该疾病的描述信息返回 160 | if question_types == [] and 'disease' in types: 161 | question_types = ['disease_desc'] 162 | 163 | # 若没有查到相关的外部查询信息,那么则将该疾病的描述信息返回 164 | if question_types == [] and 'symptom' in types: 165 | question_types = ['symptom_disease'] 166 | 167 | # 将多个分类结果进行合并处理,组装成一个字典 168 | data['question_types'] = question_types 169 | 170 | return data 171 | 172 | '''构造词对应的类型''' 173 | def build_wdtype_dict(self): 174 | wd_dict = dict() 175 | for wd in self.region_words: 176 | wd_dict[wd] = [] 177 | if wd in self.disease_wds: 178 | wd_dict[wd].append('disease') 179 | if wd in self.department_wds: 180 | wd_dict[wd].append('department') 181 | if wd in self.check_wds: 182 | wd_dict[wd].append('check') 183 | if wd in self.drug_wds: 184 | wd_dict[wd].append('drug') 185 | if wd in self.food_wds: 186 | wd_dict[wd].append('food') 187 | if wd in self.symptom_wds: 188 | wd_dict[wd].append('symptom') 189 | if wd in self.producer_wds: 190 | wd_dict[wd].append('producer') 191 | return wd_dict 192 | 193 | '''构造actree,加速过滤''' 194 | def build_actree(self, wordlist): 195 | actree = ahocorasick.Automaton() 196 | for index, word in enumerate(wordlist): 197 | actree.add_word(word, (index, word)) 198 | actree.make_automaton() 199 | return actree 200 | 201 | '''问句过滤:考虑多轮对话''' 202 | #用户的第二个问题没有疾病实体,默认采用上一轮的疾病实体 203 | 204 | 205 | def check_medical(self, question): 206 | region_wds = [] 207 | for i in self.region_tree.iter(question): 208 | #返回:6, (1, 'XX药')),(15, (2, '感冒')),。。。。。。 209 | wd = i[1][1] 210 | region_wds.append(wd) 211 | stop_wds = [] 212 | for wd1 in region_wds: 213 | for wd2 in region_wds: 214 | if wd1 in wd2 and wd1 != wd2: 215 | stop_wds.append(wd1)## stop_wds取重复的短的词,如region_wds=['乙肝', '肝硬化', '硬化'],则stop_wds=['硬化'] 216 | final_wds = [i for i in region_wds if i not in stop_wds]#取长词 217 | final_dict = {i:self.wdtype_dict.get(i) for i in final_wds}# 获取词和词所对应的实体类型 218 | global diseases_dict 219 | if final_dict: 220 | diseases_dict=final_dict 221 | return final_dict 222 | 223 | '''基于特征词进行分类''' 224 | def check_words(self, wds, sent): 225 | for wd in wds: 226 | if wd in sent: 227 | return True 228 | return False 229 | 230 | 231 | if __name__ == '__main__': 232 | handler = QuestionClassifier() 233 | while 1: 234 | question = input('input an question:') 235 | data = handler.classify(question) 236 | print(data) -------------------------------------------------------------------------------- /question_parser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # coding: utf-8 3 | # File: question_parser.py 4 | # Author: lhy 5 | # Date: 18-10-4 6 | 7 | class QuestionPaser: 8 | 9 | '''构建实体节点''' 10 | def build_entitydict(self, args): 11 | entity_dict = {} 12 | for arg, types in args.items(): 13 | for type in types: 14 | if type not in entity_dict: 15 | entity_dict[type] = [arg] 16 | else: 17 | entity_dict[type].append(arg) 18 | 19 | return entity_dict 20 | 21 | '''解析主函数''' 22 | def parser_main(self, res_classify): 23 | args = res_classify['args'] 24 | entity_dict = self.build_entitydict(args) 25 | question_types = res_classify['question_types'] 26 | sqls = [] 27 | for question_type in question_types: 28 | sql_ = {} 29 | sql_['question_type'] = question_type 30 | sql = [] 31 | if question_type == 'disease_symptom': 32 | sql = self.sql_transfer(question_type, entity_dict.get('disease')) 33 | 34 | elif question_type == 'symptom_disease': 35 | sql = self.sql_transfer(question_type, entity_dict.get('symptom')) 36 | 37 | elif question_type == 'disease_cause': 38 | sql = self.sql_transfer(question_type, entity_dict.get('disease')) 39 | 40 | elif question_type == 'disease_acompany': 41 | sql = self.sql_transfer(question_type, entity_dict.get('disease')) 42 | 43 | elif question_type == 'disease_not_food': 44 | sql = self.sql_transfer(question_type, entity_dict.get('disease')) 45 | 46 | elif question_type == 'disease_do_food': 47 | sql = self.sql_transfer(question_type, entity_dict.get('disease')) 48 | 49 | elif question_type == 'food_not_disease': 50 | sql = self.sql_transfer(question_type, entity_dict.get('food')) 51 | 52 | elif question_type == 'food_do_disease': 53 | sql = self.sql_transfer(question_type, entity_dict.get('food')) 54 | 55 | elif question_type == 'disease_drug': 56 | sql = self.sql_transfer(question_type, entity_dict.get('disease')) 57 | 58 | elif question_type == 'drug_disease': 59 | sql = self.sql_transfer(question_type, entity_dict.get('drug')) 60 | 61 | elif question_type == 'disease_check': 62 | sql = self.sql_transfer(question_type, entity_dict.get('disease')) 63 | 64 | elif question_type == 'check_disease': 65 | sql = self.sql_transfer(question_type, entity_dict.get('check')) 66 | 67 | elif question_type == 'disease_prevent': 68 | sql = self.sql_transfer(question_type, entity_dict.get('disease')) 69 | 70 | elif question_type == 'disease_lasttime': 71 | sql = self.sql_transfer(question_type, entity_dict.get('disease')) 72 | 73 | elif question_type == 'disease_cureway': 74 | sql = self.sql_transfer(question_type, entity_dict.get('disease')) 75 | 76 | elif question_type == 'disease_cureprob': 77 | sql = self.sql_transfer(question_type, entity_dict.get('disease')) 78 | 79 | elif question_type == 'disease_easyget': 80 | sql = self.sql_transfer(question_type, entity_dict.get('disease')) 81 | 82 | elif question_type == 'disease_desc': 83 | sql = self.sql_transfer(question_type, entity_dict.get('disease')) 84 | 85 | if sql: 86 | sql_['sql'] = sql 87 | 88 | sqls.append(sql_) 89 | 90 | return sqls 91 | 92 | '''针对不同的问题,分开进行处理''' 93 | def sql_transfer(self, question_type, entities): 94 | if not entities: 95 | return [] 96 | 97 | # 查询语句 98 | sql = [] 99 | # 查询疾病的原因 100 | if question_type == 'disease_cause': 101 | sql = ["MATCH (m:Disease) where m.name = '{0}' return m.name, m.cause".format(i) for i in entities] 102 | 103 | # 查询疾病的防御措施 104 | elif question_type == 'disease_prevent': 105 | sql = ["MATCH (m:Disease) where m.name = '{0}' return m.name, m.prevent".format(i) for i in entities] 106 | 107 | # 查询疾病的持续时间 108 | elif question_type == 'disease_lasttime': 109 | sql = ["MATCH (m:Disease) where m.name = '{0}' return m.name, m.cure_lasttime".format(i) for i in entities] 110 | 111 | # 查询疾病的治愈概率 112 | elif question_type == 'disease_cureprob': 113 | sql = ["MATCH (m:Disease) where m.name = '{0}' return m.name, m.cured_prob".format(i) for i in entities] 114 | 115 | # 查询疾病的治疗方式 116 | elif question_type == 'disease_cureway': 117 | sql = ["MATCH (m:Disease) where m.name = '{0}' return m.name, m.cure_way".format(i) for i in entities] 118 | 119 | # 查询疾病的易发人群 120 | elif question_type == 'disease_easyget': 121 | sql = ["MATCH (m:Disease) where m.name = '{0}' return m.name, m.easy_get".format(i) for i in entities] 122 | 123 | # 查询疾病的相关介绍 124 | elif question_type == 'disease_desc': 125 | sql = ["MATCH (m:Disease) where m.name = '{0}' return m.name, m.desc".format(i) for i in entities] 126 | 127 | # 查询疾病有哪些症状 128 | elif question_type == 'disease_symptom': 129 | sql = ["MATCH (m:Disease)-[r:has_symptom]->(n:Symptom) where m.name = '{0}' return m.name, r.name, n.name".format(i) for i in entities] 130 | 131 | # 查询症状会导致哪些疾病 132 | elif question_type == 'symptom_disease': 133 | sql = ["MATCH (m:Disease)-[r:has_symptom]->(n:Symptom) where n.name = '{0}' return m.name, r.name, n.name".format(i) for i in entities] 134 | 135 | # 查询疾病的并发症 136 | elif question_type == 'disease_acompany':#双向 137 | sql1 = ["MATCH (m:Disease)-[r:acompany_with]->(n:Disease) where m.name = '{0}' return m.name, r.name, n.name".format(i) for i in entities] 138 | sql2 = ["MATCH (m:Disease)-[r:acompany_with]->(n:Disease) where n.name = '{0}' return m.name, r.name, n.name".format(i) for i in entities] 139 | sql = sql1 + sql2 140 | # 查询疾病的忌口 141 | elif question_type == 'disease_not_food': 142 | sql = ["MATCH (m:Disease)-[r:no_eat]->(n:Food) where m.name = '{0}' return m.name, r.name, n.name".format(i) for i in entities] 143 | 144 | # 查询疾病建议吃的东西 145 | elif question_type == 'disease_do_food': 146 | sql1 = ["MATCH (m:Disease)-[r:do_eat]->(n:Food) where m.name = '{0}' return m.name, r.name, n.name".format(i) for i in entities] 147 | sql2 = ["MATCH (m:Disease)-[r:recommand_eat]->(n:Food) where m.name = '{0}' return m.name, r.name, n.name".format(i) for i in entities] 148 | sql = sql1 + sql2 149 | 150 | # 已知忌口查疾病 151 | elif question_type == 'food_not_disease': 152 | sql = ["MATCH (m:Disease)-[r:no_eat]->(n:Food) where n.name = '{0}' return m.name, r.name, n.name".format(i) for i in entities] 153 | 154 | # 已知推荐查疾病 155 | elif question_type == 'food_do_disease': 156 | sql1 = ["MATCH (m:Disease)-[r:do_eat]->(n:Food) where n.name = '{0}' return m.name, r.name, n.name".format(i) for i in entities] 157 | sql2 = ["MATCH (m:Disease)-[r:recommand_eat]->(n:Food) where n.name = '{0}' return m.name, r.name, n.name".format(i) for i in entities] 158 | sql = sql1 + sql2 159 | 160 | # 查询疾病常用药品-药品别名记得扩充 161 | elif question_type == 'disease_drug': 162 | sql1 = ["MATCH (m:Disease)-[r:common_drug]->(n:Drug) where m.name = '{0}' return m.name, r.name, n.name".format(i) for i in entities] 163 | sql2 = ["MATCH (m:Disease)-[r:recommand_drug]->(n:Drug) where m.name = '{0}' return m.name, r.name, n.name".format(i) for i in entities] 164 | sql = sql1 + sql2 165 | 166 | # 已知药品查询能够治疗的疾病 167 | elif question_type == 'drug_disease': 168 | sql1 = ["MATCH (m:Disease)-[r:common_drug]->(n:Drug) where n.name = '{0}' return m.name, r.name, n.name".format(i) for i in entities] 169 | sql2 = ["MATCH (m:Disease)-[r:recommand_drug]->(n:Drug) where n.name = '{0}' return m.name, r.name, n.name".format(i) for i in entities] 170 | sql = sql1 + sql2 171 | # 查询疾病应该进行的检查 172 | elif question_type == 'disease_check': 173 | sql = ["MATCH (m:Disease)-[r:need_check]->(n:Check) where m.name = '{0}' return m.name, r.name, n.name".format(i) for i in entities] 174 | 175 | # 已知检查查询疾病 176 | elif question_type == 'check_disease': 177 | sql = ["MATCH (m:Disease)-[r:need_check]->(n:Check) where n.name = '{0}' return m.name, r.name, n.name".format(i) for i in entities] 178 | 179 | return sql 180 | 181 | 182 | 183 | if __name__ == '__main__': 184 | handler = QuestionPaser() 185 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pandas==0.20.3 2 | web.py==0.40.dev1 3 | py2neo==4.2.0 4 | jieba==0.39 5 | scikit_learn==0.20.3 6 | -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | #-*- coding: UTF-8 -*- 2 | # @Time : 2020/3/1 14:43 3 | # @File : run.py.py 4 | # @Software: PyCharm 5 | 6 | #python run.py 127.0.0.1:1234 7 | 8 | import sys 9 | import web 10 | from chatbot_graph import ChatBotGraph 11 | render = web.template.render('templates/') 12 | 13 | urls = ('/', 'index','/add','add') 14 | app = web.application(urls, globals()) 15 | 16 | handler= ChatBotGraph() 17 | print("create question object finish! ") 18 | 19 | # 主页显示类 20 | class index: 21 | def GET(self): 22 | return render.index() 23 | 24 | def POST(self): 25 | text=web.input() 26 | print(text) 27 | raise web.seeother('/') 28 | 29 | # 处理问题类 30 | class add: 31 | # get方式处理问题 32 | def GET(self): 33 | pass 34 | 35 | # post方式处理问题 36 | def POST(self): 37 | def enablePrint(): 38 | sys.stdout = sys.__stdout__ 39 | enablePrint() 40 | while 1: 41 | text=web.input() 42 | # 简单的过滤掉无效post请求 43 | if text['id']=="bei": 44 | question=text['q'] 45 | print("received question:",question) 46 | print("now get answer!") 47 | answer=dealquestion(question) 48 | print("得到的答案是:",answer) 49 | if len(str(answer).strip())==0: 50 | answer="我也不知道呢!" 51 | # print("return answer!") 52 | return answer 53 | else: 54 | pass 55 | 56 | 57 | # 处理问题的方法 58 | def dealquestion(question): 59 | # 查询知识图谱 60 | answer = handler.chat_main(question) 61 | return answer 62 | 63 | if __name__=="__main__": 64 | web.internalerror = web.debugerror 65 | 66 | app.run() -------------------------------------------------------------------------------- /static/css/_chat.scss: -------------------------------------------------------------------------------- 1 | $height: 500px; 2 | $mecolor:#00f; 3 | $backcolor:#EEEEEE; 4 | #chat{ 5 | height:800px; 6 | background: $backcolor; 7 | overflow-y:auto; 8 | z-index: 999; 9 | .me{ 10 | margin:10px; 11 | margin-left:30%; 12 | margin-right:5%; 13 | text-align:right; 14 | position:relative; 15 | .qipao{ 16 | position:absolute; 17 | width:0; 18 | height:0; 19 | border-width:4px; 20 | border-style:solid; 21 | border-color:transparent transparent transparent $mecolor; 22 | right:-8px; 23 | top:11px; 24 | } 25 | div.item{ 26 | display:inline-block; 27 | border-radius: 5px; 28 | padding:7px; 29 | background:$mecolor; 30 | } 31 | } 32 | .robot{ 33 | margin:10px; 34 | margin-right:30%; 35 | text-align:left; 36 | margin-left:5%; 37 | position:relative; 38 | 39 | .qipao{ 40 | position:absolute; 41 | width:0; 42 | height:0; 43 | border-width:4px; 44 | border-style:solid; 45 | border-color:transparent white transparent transparent; 46 | left:-8px; 47 | top:11px; 48 | 49 | } 50 | div.item{ 51 | display:inline-block; 52 | border-radius: 5px; 53 | padding:7px; 54 | background:white; 55 | //background: url("/static/img/robot_ico.png") no-repeat; 56 | } 57 | } 58 | } 59 | #test{ 60 | width:0; 61 | height:0; 62 | border-width:100px; 63 | border-style:solid; 64 | border-color:transparent transparent transparent $mecolor; 65 | } 66 | -------------------------------------------------------------------------------- /static/css/loginnew.css: -------------------------------------------------------------------------------- 1 | 2 | a { 3 | text-decoration: none; 4 | } 5 | 6 | ul ol li { 7 | list-style: none; 8 | } 9 | 10 | body { 11 | /*background-color: #2F4056;*/ 12 | } 13 | 14 | 15 | .bxback { 16 | z-index: -99; 17 | bottom: 0px; 18 | position: absolute; 19 | left: 0; 20 | top: 0; 21 | width: 100%; 22 | overflow: hidden; 23 | } 24 | 25 | .bxback .item { 26 | position: absolute; 27 | left: 0; 28 | top: 0; 29 | width: 100%; 30 | height: 100%; 31 | overflow: hidden; 32 | background-size: 100% 100%; 33 | } 34 | 35 | -------------------------------------------------------------------------------- /static/css/style.css: -------------------------------------------------------------------------------- 1 | .chat_wrap{ 2 | background: url(/static/img/kuang101.png) no-repeat; 3 | background-size: 100% 100%; 4 | padding-top:28px; 5 | padding-bottom: 30px; 6 | padding-left: 4px; 7 | padding-right: 4px; 8 | } 9 | .chat_left{ 10 | background: url(/static/img/r11.png) right bottom no-repeat; 11 | height: 600px; 12 | } 13 | .chat_right{ 14 | background: url(/static/img/r2.png) left bottom no-repeat; 15 | height: 600px; 16 | } 17 | #chat { 18 | /*min-height: 500px;*/ 19 | border-radius: 12px; 20 | padding-top: 10px; 21 | padding-bottom: 20px; 22 | height:600px; 23 | overflow-y: auto; } 24 | #chat .me { 25 | margin: 10px; 26 | margin-left: 30%; 27 | margin-right: 5%; 28 | text-align: right; 29 | position: relative; } 30 | #chat .me .qipao { 31 | position: absolute; 32 | width: 0; 33 | height: 0; 34 | border-width: 4px; 35 | border-style: solid; 36 | border-color: transparent transparent transparent #b2e281; 37 | right: -8px; 38 | top: 11px; } 39 | #chat .me div.item { 40 | display: inline-block; 41 | border-radius: 5px; 42 | padding: 7px; 43 | background: #b2e281; } 44 | #chat .robot { 45 | margin: 10px; 46 | margin-right: 30%; 47 | text-align: left; 48 | margin-left: 5%; 49 | position: relative; } 50 | #chat .robot .qipao { 51 | position: absolute; 52 | width: 0; 53 | height: 0; 54 | border-width: 4px; 55 | border-style: solid; 56 | border-color: transparent white transparent transparent; 57 | left: -8px; 58 | top: 11px; } 59 | #chat .robot div.item { 60 | display: inline-block; 61 | border-radius: 5px; 62 | padding: 7px; 63 | background: white; } 64 | #info{ 65 | height:44px; 66 | font-size: 30px; 67 | color: #fff; 68 | border-radius: 12px; 69 | width:300px; 70 | background: none; 71 | } 72 | #send{ 73 | border: none; 74 | width: 75px; 75 | height:50px; 76 | background: url(/static/img/btn3.png) no-repeat; 77 | background-size: 100% 100%; 78 | } 79 | 80 | 81 | .test-1::-webkit-scrollbar {/*滚动条整体样式*/ 82 | width: 2px; /*高宽分别对应横竖滚动条的尺寸*/ 83 | height: 1px; 84 | 85 | } 86 | .test-1::-webkit-scrollbar-thumb {/*滚动条里面小方块*/ 87 | border-radius: 15px; 88 | -webkit-box-shadow: inset 0 0 5px rgba(0,0,0,0.2); 89 | background: #123488; 90 | opacity: 0.5; 91 | } 92 | .test-1::-webkit-scrollbar-track {/*滚动条里面轨道*/ 93 | -webkit-box-shadow: inset 0 0 5px rgba(0,0,0,0.2); 94 | border-radius: 10px; 95 | background: #11446D; 96 | opacity: 0.5; 97 | } 98 | 99 | #test { 100 | width: 0; 101 | height: 0; 102 | border-width: 100px; 103 | border-style: solid; 104 | border-color: transparent transparent transparent #b2e281; } 105 | 106 | @font-face { 107 | font-family: myFirstFont; 108 | src: url(../fonts/Artifika-Regular.ttf); } 109 | body { 110 | font-family: myFirstFont,DFKai-SB; } 111 | 112 | /*# sourceMappingURL=style.css.map */ 113 | -------------------------------------------------------------------------------- /static/css/style.css.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "mappings": "AAGA,KAAK;EACH,UAAU,EAFD,OAAO;EAGhB,UAAU,EALH,KAAK;EAMZ,QAAQ,EAAC,IAAI;EACb,SAAG;IACD,MAAM,EAAC,IAAI;IACX,WAAW,EAAC,GAAG;IACf,YAAY,EAAC,EAAE;IACf,UAAU,EAAC,KAAK;IAChB,QAAQ,EAAC,QAAQ;IACjB,gBAAM;MACJ,QAAQ,EAAC,QAAQ;MACjB,KAAK,EAAC,CAAC;MACP,MAAM,EAAC,CAAC;MACR,YAAY,EAAC,GAAG;MAChB,YAAY,EAAC,KAAK;MAClB,YAAY,EAAC,2CAA4C;MACzD,KAAK,EAAC,IAAI;MACV,GAAG,EAAC,IAAI;IAEV,kBAAQ;MACN,OAAO,EAAC,YAAY;MACpB,aAAa,EAAE,GAAG;MAClB,OAAO,EAAC,GAAG;MACX,UAAU,EA1BP,OAAO;EA6Bd,YAAM;IACJ,MAAM,EAAC,IAAI;IACX,YAAY,EAAC,GAAG;IAChB,UAAU,EAAC,IAAI;IACf,WAAW,EAAC,EAAE;IACd,QAAQ,EAAC,QAAQ;IACjB,mBAAM;MACJ,QAAQ,EAAC,QAAQ;MACjB,KAAK,EAAC,CAAC;MACP,MAAM,EAAC,CAAC;MACR,YAAY,EAAC,GAAG;MAChB,YAAY,EAAC,KAAK;MAClB,YAAY,EAAC,yCAAyC;MACtD,IAAI,EAAC,IAAI;MACT,GAAG,EAAC,IAAI;IAEV,qBAAQ;MACP,OAAO,EAAC,YAAY;MACpB,aAAa,EAAE,GAAG;MAClB,OAAO,EAAC,GAAG;MACX,UAAU,EAAC,KAAK;;AAIrB,KAAK;EACH,KAAK,EAAC,CAAC;EACP,MAAM,EAAC,CAAC;EACR,YAAY,EAAC,KAAK;EAClB,YAAY,EAAC,KAAK;EAClB,YAAY,EAAC,2CAA4C;;AC1D3D,UAGC;EAFA,WAAW,EAAE,WAAW;EACxB,GAAG,EAAC,kCAAkC;AAEvC,IAAI;EACF,WAAW,EAAE,oBAAoB", 4 | "sources": ["_chat.scss","style.scss"], 5 | "names": [], 6 | "file": "style.css" 7 | } -------------------------------------------------------------------------------- /static/css/style.scss: -------------------------------------------------------------------------------- 1 | @import 'chat'; 2 | @font-face{ 3 | font-family: myFirstFont; 4 | src:url(../fonts/Artifika-Regular.ttf); 5 | } 6 | body{ 7 | font-family: myFirstFont,DFKai-SB; 8 | } 9 | -------------------------------------------------------------------------------- /static/fonts/Artifika-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nynCoder/Medical-QA/c448008af8b94a9cc1a7d7ccbd0dee29de3f9cb9/static/fonts/Artifika-Regular.ttf -------------------------------------------------------------------------------- /static/img/bg12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nynCoder/Medical-QA/c448008af8b94a9cc1a7d7ccbd0dee29de3f9cb9/static/img/bg12.jpg -------------------------------------------------------------------------------- /static/img/bg14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nynCoder/Medical-QA/c448008af8b94a9cc1a7d7ccbd0dee29de3f9cb9/static/img/bg14.jpg -------------------------------------------------------------------------------- /static/img/bg15.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nynCoder/Medical-QA/c448008af8b94a9cc1a7d7ccbd0dee29de3f9cb9/static/img/bg15.jpg -------------------------------------------------------------------------------- /static/img/bg16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nynCoder/Medical-QA/c448008af8b94a9cc1a7d7ccbd0dee29de3f9cb9/static/img/bg16.jpg -------------------------------------------------------------------------------- /static/img/bg33.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nynCoder/Medical-QA/c448008af8b94a9cc1a7d7ccbd0dee29de3f9cb9/static/img/bg33.jpg -------------------------------------------------------------------------------- /static/img/btn3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nynCoder/Medical-QA/c448008af8b94a9cc1a7d7ccbd0dee29de3f9cb9/static/img/btn3.gif -------------------------------------------------------------------------------- /static/img/btn3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nynCoder/Medical-QA/c448008af8b94a9cc1a7d7ccbd0dee29de3f9cb9/static/img/btn3.png -------------------------------------------------------------------------------- /static/img/btn32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nynCoder/Medical-QA/c448008af8b94a9cc1a7d7ccbd0dee29de3f9cb9/static/img/btn32.png -------------------------------------------------------------------------------- /static/img/kuang1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nynCoder/Medical-QA/c448008af8b94a9cc1a7d7ccbd0dee29de3f9cb9/static/img/kuang1.png -------------------------------------------------------------------------------- /static/img/kuang101.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nynCoder/Medical-QA/c448008af8b94a9cc1a7d7ccbd0dee29de3f9cb9/static/img/kuang101.png -------------------------------------------------------------------------------- /static/img/r1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nynCoder/Medical-QA/c448008af8b94a9cc1a7d7ccbd0dee29de3f9cb9/static/img/r1.png -------------------------------------------------------------------------------- /static/img/r11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nynCoder/Medical-QA/c448008af8b94a9cc1a7d7ccbd0dee29de3f9cb9/static/img/r11.png -------------------------------------------------------------------------------- /static/img/r2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nynCoder/Medical-QA/c448008af8b94a9cc1a7d7ccbd0dee29de3f9cb9/static/img/r2.png -------------------------------------------------------------------------------- /static/js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.5 (http://getbootstrap.com) 3 | * Copyright 2011-2015 Twitter, Inc. 4 | * Licensed under the MIT license 5 | */ 6 | if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.5",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.5",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),a(c.target).is('input[type="radio"]')||a(c.target).is('input[type="checkbox"]')||c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.5",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.5",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.5",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger("shown.bs.dropdown",h)}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth
',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),c.isInStateTrue()?void 0:(clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide())},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.5",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.5",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.5",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); -------------------------------------------------------------------------------- /static/js/jquery-3.1.1.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery v3.1.1 | (c) jQuery Foundation | jquery.org/license */ 2 | !function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.1.1",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null==a?f.call(this):a<0?this[a+this.length]:this[a]},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0&&("form"in a||"label"in a)},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"form"in b?b.parentNode&&b.disabled===!1?"label"in b?"label"in b.parentNode?b.parentNode.disabled===a:b.disabled===a:b.isDisabled===a||b.isDisabled!==!a&&ea(b)===a:b.disabled===a:"label"in b&&b.disabled===a}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}}):(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c,d,e,f=b.getElementById(a);if(f){if(c=f.getAttributeNode("id"),c&&c.value===a)return[f];e=b.getElementsByName(a),d=0;while(f=e[d++])if(c=f.getAttributeNode("id"),c&&c.value===a)return[f]}return[]}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,c,e){var f,i,j,k,l,m="function"==typeof a&&a,n=!e&&g(a=m.selector||a);if(c=c||[],1===n.length){if(i=n[0]=n[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&9===b.nodeType&&p&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(_,aa),b)||[])[0],!b)return c;m&&(b=b.parentNode),a=a.slice(i.shift().value.length)}f=V.needsContext.test(a)?0:i.length;while(f--){if(j=i[f],d.relative[k=j.type])break;if((l=d.find[k])&&(e=l(j.matches[0].replace(_,aa),$.test(i[0].type)&&qa(b.parentNode)||b))){if(i.splice(f,1),a=e.length&&sa(i),!a)return G.apply(c,e),c;break}}}return(m||h(a,n))(e,b,!p,c,!b||$.test(a)&&qa(b.parentNode)||b),c},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext,B=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,C=/^.[^:#\[\.,]*$/;function D(a,b,c){return r.isFunction(b)?r.grep(a,function(a,d){return!!b.call(a,d,a)!==c}):b.nodeType?r.grep(a,function(a){return a===b!==c}):"string"!=typeof b?r.grep(a,function(a){return i.call(b,a)>-1!==c}):C.test(b)?r.filter(b,a,c):(b=r.filter(b,a),r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType}))}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(D(this,a||[],!1))},not:function(a){return this.pushStack(D(this,a||[],!0))},is:function(a){return!!D(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var E,F=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,G=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||E,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:F.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),B.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};G.prototype=r.fn,E=r(d);var H=/^(?:parents|prev(?:Until|All))/,I={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function J(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return J(a,"nextSibling")},prev:function(a){return J(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return a.contentDocument||r.merge([],a.childNodes)}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(I[a]||r.uniqueSort(e),H.test(a)&&e.reverse()),this.pushStack(e)}});var K=/[^\x20\t\r\n\f]+/g;function L(a){var b={};return r.each(a.match(K)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?L(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function M(a){return a}function N(a){throw a}function O(a,b,c){var d;try{a&&r.isFunction(d=a.promise)?d.call(a).done(b).fail(c):a&&r.isFunction(d=a.then)?d.call(a,b,c):b.call(void 0,a)}catch(a){c.call(void 0,a)}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b=f&&(d!==N&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:M,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:M)),c[2][3].add(g(0,a,r.isFunction(d)?d:N))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(O(a,g.done(h(c)).resolve,g.reject),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)O(e[c],h(c),g.reject);return g.promise()}});var P=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&P.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var Q=r.Deferred();r.fn.ready=function(a){return Q.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,holdReady:function(a){a?r.readyWait++:r.ready(!0)},ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||Q.resolveWith(d,[r]))}}),r.ready.then=Q.then;function R(){d.removeEventListener("DOMContentLoaded",R), 3 | a.removeEventListener("load",R),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",R),a.addEventListener("load",R));var S=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)S(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h1,null,!0)},removeData:function(a){return this.each(function(){W.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=V.get(a,b),c&&(!d||r.isArray(c)?d=V.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return V.get(a,c)||V.access(a,c,{empty:r.Callbacks("once memory").add(function(){V.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length\x20\t\r\n\f]+)/i,ka=/^$|\/(?:java|ecma)script/i,la={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};la.optgroup=la.option,la.tbody=la.tfoot=la.colgroup=la.caption=la.thead,la.th=la.td;function ma(a,b){var c;return c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[],void 0===b||b&&r.nodeName(a,b)?r.merge([a],c):c}function na(a,b){for(var c=0,d=a.length;c-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=ma(l.appendChild(f),"script"),j&&na(g),c){k=0;while(f=g[k++])ka.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var qa=d.documentElement,ra=/^key/,sa=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ta=/^([^.]*)(?:\.(.+)|)/;function ua(){return!0}function va(){return!1}function wa(){try{return d.activeElement}catch(a){}}function xa(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)xa(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=va;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(qa,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(K)||[""],j=b.length;while(j--)h=ta.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.hasData(a)&&V.get(a);if(q&&(i=q.events)){b=(b||"").match(K)||[""],j=b.length;while(j--)if(h=ta.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&V.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(V.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c=1))for(;j!==this;j=j.parentNode||this)if(1===j.nodeType&&("click"!==a.type||j.disabled!==!0)){for(f=[],g={},c=0;c-1:r.find(e,this,null,[j]).length),g[e]&&f.push(d);f.length&&h.push({elem:j,handlers:f})}return j=this,i\x20\t\r\n\f]*)[^>]*)\/>/gi,za=/\s*$/g;function Da(a,b){return r.nodeName(a,"table")&&r.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a:a}function Ea(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function Fa(a){var b=Ba.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Ga(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(V.hasData(a)&&(f=V.access(a),g=V.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;c1&&"string"==typeof q&&!o.checkClone&&Aa.test(q))return a.each(function(e){var f=a.eq(e);s&&(b[0]=q.call(this,e,f.html())),Ia(f,b,c,d)});if(m&&(e=pa(b,a[0].ownerDocument,!1,a,d),f=e.firstChild,1===e.childNodes.length&&(e=f),f||d)){for(h=r.map(ma(e,"script"),Ea),i=h.length;l")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=r.contains(a.ownerDocument,a);if(!(o.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||r.isXMLDoc(a)))for(g=ma(h),f=ma(a),d=0,e=f.length;d0&&na(g,!i&&ma(a,"script")),h},cleanData:function(a){for(var b,c,d,e=r.event.special,f=0;void 0!==(c=a[f]);f++)if(T(c)){if(b=c[V.expando]){if(b.events)for(d in b.events)e[d]?r.event.remove(c,d):r.removeEvent(c,d,b.handle);c[V.expando]=void 0}c[W.expando]&&(c[W.expando]=void 0)}}}),r.fn.extend({detach:function(a){return Ja(this,a,!0)},remove:function(a){return Ja(this,a)},text:function(a){return S(this,function(a){return void 0===a?r.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return Ia(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Da(this,a);b.appendChild(a)}})},prepend:function(){return Ia(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Da(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ia(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ia(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(r.cleanData(ma(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null!=a&&a,b=null==b?a:b,this.map(function(){return r.clone(this,a,b)})},html:function(a){return S(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!za.test(a)&&!la[(ja.exec(a)||["",""])[1].toLowerCase()]){a=r.htmlPrefilter(a);try{for(;c1)}});function Ya(a,b,c,d,e){return new Ya.prototype.init(a,b,c,d,e)}r.Tween=Ya,Ya.prototype={constructor:Ya,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||r.easing._default,this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(r.cssNumber[c]?"":"px")},cur:function(){var a=Ya.propHooks[this.prop];return a&&a.get?a.get(this):Ya.propHooks._default.get(this)},run:function(a){var b,c=Ya.propHooks[this.prop];return this.options.duration?this.pos=b=r.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Ya.propHooks._default.set(this),this}},Ya.prototype.init.prototype=Ya.prototype,Ya.propHooks={_default:{get:function(a){var b;return 1!==a.elem.nodeType||null!=a.elem[a.prop]&&null==a.elem.style[a.prop]?a.elem[a.prop]:(b=r.css(a.elem,a.prop,""),b&&"auto"!==b?b:0)},set:function(a){r.fx.step[a.prop]?r.fx.step[a.prop](a):1!==a.elem.nodeType||null==a.elem.style[r.cssProps[a.prop]]&&!r.cssHooks[a.prop]?a.elem[a.prop]=a.now:r.style(a.elem,a.prop,a.now+a.unit)}}},Ya.propHooks.scrollTop=Ya.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},r.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2},_default:"swing"},r.fx=Ya.prototype.init,r.fx.step={};var Za,$a,_a=/^(?:toggle|show|hide)$/,ab=/queueHooks$/;function bb(){$a&&(a.requestAnimationFrame(bb),r.fx.tick())}function cb(){return a.setTimeout(function(){Za=void 0}),Za=r.now()}function db(a,b){var c,d=0,e={height:a};for(b=b?1:0;d<4;d+=2-b)c=ba[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function eb(a,b,c){for(var d,e=(hb.tweeners[b]||[]).concat(hb.tweeners["*"]),f=0,g=e.length;f1)},removeAttr:function(a){return this.each(function(){r.removeAttr(this,a)})}}),r.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return"undefined"==typeof a.getAttribute?r.prop(a,b,c):(1===f&&r.isXMLDoc(a)||(e=r.attrHooks[b.toLowerCase()]||(r.expr.match.bool.test(b)?ib:void 0)), 4 | void 0!==c?null===c?void r.removeAttr(a,b):e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:(a.setAttribute(b,c+""),c):e&&"get"in e&&null!==(d=e.get(a,b))?d:(d=r.find.attr(a,b),null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio"===b&&r.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}},removeAttr:function(a,b){var c,d=0,e=b&&b.match(K);if(e&&1===a.nodeType)while(c=e[d++])a.removeAttribute(c)}}),ib={set:function(a,b,c){return b===!1?r.removeAttr(a,c):a.setAttribute(c,c),c}},r.each(r.expr.match.bool.source.match(/\w+/g),function(a,b){var c=jb[b]||r.find.attr;jb[b]=function(a,b,d){var e,f,g=b.toLowerCase();return d||(f=jb[g],jb[g]=e,e=null!=c(a,b,d)?g:null,jb[g]=f),e}});var kb=/^(?:input|select|textarea|button)$/i,lb=/^(?:a|area)$/i;r.fn.extend({prop:function(a,b){return S(this,r.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[r.propFix[a]||a]})}}),r.extend({prop:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return 1===f&&r.isXMLDoc(a)||(b=r.propFix[b]||b,e=r.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=r.find.attr(a,"tabindex");return b?parseInt(b,10):kb.test(a.nodeName)||lb.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),o.optSelected||(r.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null},set:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}}),r.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){r.propFix[this.toLowerCase()]=this});function mb(a){var b=a.match(K)||[];return b.join(" ")}function nb(a){return a.getAttribute&&a.getAttribute("class")||""}r.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).addClass(a.call(this,b,nb(this)))});if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=nb(c),d=1===c.nodeType&&" "+mb(e)+" "){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=mb(d),e!==h&&c.setAttribute("class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).removeClass(a.call(this,b,nb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=nb(c),d=1===c.nodeType&&" "+mb(e)+" "){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=mb(d),e!==h&&c.setAttribute("class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):r.isFunction(a)?this.each(function(c){r(this).toggleClass(a.call(this,c,nb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=r(this),f=a.match(K)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else void 0!==a&&"boolean"!==c||(b=nb(this),b&&V.set(this,"__className__",b),this.setAttribute&&this.setAttribute("class",b||a===!1?"":V.get(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+mb(nb(c))+" ").indexOf(b)>-1)return!0;return!1}});var ob=/\r/g;r.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=r.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,r(this).val()):a,null==e?e="":"number"==typeof e?e+="":r.isArray(e)&&(e=r.map(e,function(a){return null==a?"":a+""})),b=r.valHooks[this.type]||r.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=r.valHooks[e.type]||r.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(ob,""):null==c?"":c)}}}),r.extend({valHooks:{option:{get:function(a){var b=r.find.attr(a,"value");return null!=b?b:mb(r.text(a))}},select:{get:function(a){var b,c,d,e=a.options,f=a.selectedIndex,g="select-one"===a.type,h=g?null:[],i=g?f+1:e.length;for(d=f<0?i:g?f:0;d-1)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),r.each(["radio","checkbox"],function(){r.valHooks[this]={set:function(a,b){if(r.isArray(b))return a.checked=r.inArray(r(a).val(),b)>-1}},o.checkOn||(r.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var pb=/^(?:focusinfocus|focusoutblur)$/;r.extend(r.event,{trigger:function(b,c,e,f){var g,h,i,j,k,m,n,o=[e||d],p=l.call(b,"type")?b.type:b,q=l.call(b,"namespace")?b.namespace.split("."):[];if(h=i=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!pb.test(p+r.event.triggered)&&(p.indexOf(".")>-1&&(q=p.split("."),p=q.shift(),q.sort()),k=p.indexOf(":")<0&&"on"+p,b=b[r.expando]?b:new r.Event(p,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=q.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:r.makeArray(c,[b]),n=r.event.special[p]||{},f||!n.trigger||n.trigger.apply(e,c)!==!1)){if(!f&&!n.noBubble&&!r.isWindow(e)){for(j=n.delegateType||p,pb.test(j+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),i=h;i===(e.ownerDocument||d)&&o.push(i.defaultView||i.parentWindow||a)}g=0;while((h=o[g++])&&!b.isPropagationStopped())b.type=g>1?j:n.bindType||p,m=(V.get(h,"events")||{})[b.type]&&V.get(h,"handle"),m&&m.apply(h,c),m=k&&h[k],m&&m.apply&&T(h)&&(b.result=m.apply(h,c),b.result===!1&&b.preventDefault());return b.type=p,f||b.isDefaultPrevented()||n._default&&n._default.apply(o.pop(),c)!==!1||!T(e)||k&&r.isFunction(e[p])&&!r.isWindow(e)&&(i=e[k],i&&(e[k]=null),r.event.triggered=p,e[p](),r.event.triggered=void 0,i&&(e[k]=i)),b.result}},simulate:function(a,b,c){var d=r.extend(new r.Event,c,{type:a,isSimulated:!0});r.event.trigger(d,null,b)}}),r.fn.extend({trigger:function(a,b){return this.each(function(){r.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];if(c)return r.event.trigger(a,b,c,!0)}}),r.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(a,b){r.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),r.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),o.focusin="onfocusin"in a,o.focusin||r.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){r.event.simulate(b,a.target,r.event.fix(a))};r.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=V.access(d,b);e||d.addEventListener(a,c,!0),V.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=V.access(d,b)-1;e?V.access(d,b,e):(d.removeEventListener(a,c,!0),V.remove(d,b))}}});var qb=a.location,rb=r.now(),sb=/\?/;r.parseXML=function(b){var c;if(!b||"string"!=typeof b)return null;try{c=(new a.DOMParser).parseFromString(b,"text/xml")}catch(d){c=void 0}return c&&!c.getElementsByTagName("parsererror").length||r.error("Invalid XML: "+b),c};var tb=/\[\]$/,ub=/\r?\n/g,vb=/^(?:submit|button|image|reset|file)$/i,wb=/^(?:input|select|textarea|keygen)/i;function xb(a,b,c,d){var e;if(r.isArray(b))r.each(b,function(b,e){c||tb.test(a)?d(a,e):xb(a+"["+("object"==typeof e&&null!=e?b:"")+"]",e,c,d)});else if(c||"object"!==r.type(b))d(a,b);else for(e in b)xb(a+"["+e+"]",b[e],c,d)}r.param=function(a,b){var c,d=[],e=function(a,b){var c=r.isFunction(b)?b():b;d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(null==c?"":c)};if(r.isArray(a)||a.jquery&&!r.isPlainObject(a))r.each(a,function(){e(this.name,this.value)});else for(c in a)xb(c,a[c],b,e);return d.join("&")},r.fn.extend({serialize:function(){return r.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=r.prop(this,"elements");return a?r.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!r(this).is(":disabled")&&wb.test(this.nodeName)&&!vb.test(a)&&(this.checked||!ia.test(a))}).map(function(a,b){var c=r(this).val();return null==c?null:r.isArray(c)?r.map(c,function(a){return{name:b.name,value:a.replace(ub,"\r\n")}}):{name:b.name,value:c.replace(ub,"\r\n")}}).get()}});var yb=/%20/g,zb=/#.*$/,Ab=/([?&])_=[^&]*/,Bb=/^(.*?):[ \t]*([^\r\n]*)$/gm,Cb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Db=/^(?:GET|HEAD)$/,Eb=/^\/\//,Fb={},Gb={},Hb="*/".concat("*"),Ib=d.createElement("a");Ib.href=qb.href;function Jb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(K)||[];if(r.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Kb(a,b,c,d){var e={},f=a===Gb;function g(h){var i;return e[h]=!0,r.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Lb(a,b){var c,d,e=r.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&r.extend(!0,a,d),a}function Mb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}if(f)return f!==i[0]&&i.unshift(f),c[f]}function Nb(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}r.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:qb.href,type:"GET",isLocal:Cb.test(qb.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Hb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":r.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Lb(Lb(a,r.ajaxSettings),b):Lb(r.ajaxSettings,a)},ajaxPrefilter:Jb(Fb),ajaxTransport:Jb(Gb),ajax:function(b,c){"object"==typeof b&&(c=b,b=void 0),c=c||{};var e,f,g,h,i,j,k,l,m,n,o=r.ajaxSetup({},c),p=o.context||o,q=o.context&&(p.nodeType||p.jquery)?r(p):r.event,s=r.Deferred(),t=r.Callbacks("once memory"),u=o.statusCode||{},v={},w={},x="canceled",y={readyState:0,getResponseHeader:function(a){var b;if(k){if(!h){h={};while(b=Bb.exec(g))h[b[1].toLowerCase()]=b[2]}b=h[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return k?g:null},setRequestHeader:function(a,b){return null==k&&(a=w[a.toLowerCase()]=w[a.toLowerCase()]||a,v[a]=b),this},overrideMimeType:function(a){return null==k&&(o.mimeType=a),this},statusCode:function(a){var b;if(a)if(k)y.always(a[y.status]);else for(b in a)u[b]=[u[b],a[b]];return this},abort:function(a){var b=a||x;return e&&e.abort(b),A(0,b),this}};if(s.promise(y),o.url=((b||o.url||qb.href)+"").replace(Eb,qb.protocol+"//"),o.type=c.method||c.type||o.method||o.type,o.dataTypes=(o.dataType||"*").toLowerCase().match(K)||[""],null==o.crossDomain){j=d.createElement("a");try{j.href=o.url,j.href=j.href,o.crossDomain=Ib.protocol+"//"+Ib.host!=j.protocol+"//"+j.host}catch(z){o.crossDomain=!0}}if(o.data&&o.processData&&"string"!=typeof o.data&&(o.data=r.param(o.data,o.traditional)),Kb(Fb,o,c,y),k)return y;l=r.event&&o.global,l&&0===r.active++&&r.event.trigger("ajaxStart"),o.type=o.type.toUpperCase(),o.hasContent=!Db.test(o.type),f=o.url.replace(zb,""),o.hasContent?o.data&&o.processData&&0===(o.contentType||"").indexOf("application/x-www-form-urlencoded")&&(o.data=o.data.replace(yb,"+")):(n=o.url.slice(f.length),o.data&&(f+=(sb.test(f)?"&":"?")+o.data,delete o.data),o.cache===!1&&(f=f.replace(Ab,"$1"),n=(sb.test(f)?"&":"?")+"_="+rb++ +n),o.url=f+n),o.ifModified&&(r.lastModified[f]&&y.setRequestHeader("If-Modified-Since",r.lastModified[f]),r.etag[f]&&y.setRequestHeader("If-None-Match",r.etag[f])),(o.data&&o.hasContent&&o.contentType!==!1||c.contentType)&&y.setRequestHeader("Content-Type",o.contentType),y.setRequestHeader("Accept",o.dataTypes[0]&&o.accepts[o.dataTypes[0]]?o.accepts[o.dataTypes[0]]+("*"!==o.dataTypes[0]?", "+Hb+"; q=0.01":""):o.accepts["*"]);for(m in o.headers)y.setRequestHeader(m,o.headers[m]);if(o.beforeSend&&(o.beforeSend.call(p,y,o)===!1||k))return y.abort();if(x="abort",t.add(o.complete),y.done(o.success),y.fail(o.error),e=Kb(Gb,o,c,y)){if(y.readyState=1,l&&q.trigger("ajaxSend",[y,o]),k)return y;o.async&&o.timeout>0&&(i=a.setTimeout(function(){y.abort("timeout")},o.timeout));try{k=!1,e.send(v,A)}catch(z){if(k)throw z;A(-1,z)}}else A(-1,"No Transport");function A(b,c,d,h){var j,m,n,v,w,x=c;k||(k=!0,i&&a.clearTimeout(i),e=void 0,g=h||"",y.readyState=b>0?4:0,j=b>=200&&b<300||304===b,d&&(v=Mb(o,y,d)),v=Nb(o,v,y,j),j?(o.ifModified&&(w=y.getResponseHeader("Last-Modified"),w&&(r.lastModified[f]=w),w=y.getResponseHeader("etag"),w&&(r.etag[f]=w)),204===b||"HEAD"===o.type?x="nocontent":304===b?x="notmodified":(x=v.state,m=v.data,n=v.error,j=!n)):(n=x,!b&&x||(x="error",b<0&&(b=0))),y.status=b,y.statusText=(c||x)+"",j?s.resolveWith(p,[m,x,y]):s.rejectWith(p,[y,x,n]),y.statusCode(u),u=void 0,l&&q.trigger(j?"ajaxSuccess":"ajaxError",[y,o,j?m:n]),t.fireWith(p,[y,x]),l&&(q.trigger("ajaxComplete",[y,o]),--r.active||r.event.trigger("ajaxStop")))}return y},getJSON:function(a,b,c){return r.get(a,b,c,"json")},getScript:function(a,b){return r.get(a,void 0,b,"script")}}),r.each(["get","post"],function(a,b){r[b]=function(a,c,d,e){return r.isFunction(c)&&(e=e||d,d=c,c=void 0),r.ajax(r.extend({url:a,type:b,dataType:e,data:c,success:d},r.isPlainObject(a)&&a))}}),r._evalUrl=function(a){return r.ajax({url:a,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},r.fn.extend({wrapAll:function(a){var b;return this[0]&&(r.isFunction(a)&&(a=a.call(this[0])),b=r(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this},wrapInner:function(a){return r.isFunction(a)?this.each(function(b){r(this).wrapInner(a.call(this,b))}):this.each(function(){var b=r(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=r.isFunction(a);return this.each(function(c){r(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(a){return this.parent(a).not("body").each(function(){r(this).replaceWith(this.childNodes)}),this}}),r.expr.pseudos.hidden=function(a){return!r.expr.pseudos.visible(a)},r.expr.pseudos.visible=function(a){return!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)},r.ajaxSettings.xhr=function(){try{return new a.XMLHttpRequest}catch(b){}};var Ob={0:200,1223:204},Pb=r.ajaxSettings.xhr();o.cors=!!Pb&&"withCredentials"in Pb,o.ajax=Pb=!!Pb,r.ajaxTransport(function(b){var c,d;if(o.cors||Pb&&!b.crossDomain)return{send:function(e,f){var g,h=b.xhr();if(h.open(b.type,b.url,b.async,b.username,b.password),b.xhrFields)for(g in b.xhrFields)h[g]=b.xhrFields[g];b.mimeType&&h.overrideMimeType&&h.overrideMimeType(b.mimeType),b.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest");for(g in e)h.setRequestHeader(g,e[g]);c=function(a){return function(){c&&(c=d=h.onload=h.onerror=h.onabort=h.onreadystatechange=null,"abort"===a?h.abort():"error"===a?"number"!=typeof h.status?f(0,"error"):f(h.status,h.statusText):f(Ob[h.status]||h.status,h.statusText,"text"!==(h.responseType||"text")||"string"!=typeof h.responseText?{binary:h.response}:{text:h.responseText},h.getAllResponseHeaders()))}},h.onload=c(),d=h.onerror=c("error"),void 0!==h.onabort?h.onabort=d:h.onreadystatechange=function(){4===h.readyState&&a.setTimeout(function(){c&&d()})},c=c("abort");try{h.send(b.hasContent&&b.data||null)}catch(i){if(c)throw i}},abort:function(){c&&c()}}}),r.ajaxPrefilter(function(a){a.crossDomain&&(a.contents.script=!1)}),r.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(a){return r.globalEval(a),a}}}),r.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),r.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(e,f){b=r(" 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 | 21 |
22 |
23 |

Medical Question Answering System
Based on Knowledge Graph

24 |
25 |
26 |
27 |
28 | 29 | 30 | 31 | 32 |
33 | 34 |
35 | 36 |
37 |
38 |
39 |
40 | 41 | 42 | 43 |
44 | 45 |
46 | 47 | 48 |
49 |
50 |
51 | 52 |
53 | 54 |
55 | 56 | 57 | 58 | 59 | 60 | --------------------------------------------------------------------------------