@tekkare/romulus 0.1.251 → 0.1.252

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (265) hide show
  1. package/dist/module.json +1 -1
  2. package/layer/app/app.config.ts +6 -0
  3. package/layer/app/components/OipPubmedSummary.vue +223 -0
  4. package/layer/app/components/OipResourceDrawer.vue +331 -0
  5. package/layer/app/components/OipResourceDrawerLink.vue +40 -0
  6. package/layer/app/components/OipResourceField.vue +41 -0
  7. package/layer/app/components/OipResourceGenericCard.vue +54 -0
  8. package/layer/app/components/OipResourceSource.vue +50 -0
  9. package/layer/app/components/OipResourceTags.vue +65 -0
  10. package/layer/app/components/OipResourceView.vue +107 -0
  11. package/layer/app/components/oip-drugs/fiches/AssociationFiche.vue +775 -0
  12. package/layer/app/components/oip-drugs/fiches/BrandFiche.vue +1111 -0
  13. package/layer/app/components/oip-drugs/fiches/IndicationFiche.vue +182 -0
  14. package/layer/app/components/oip-drugs/fiches/JoFiche.vue +101 -0
  15. package/layer/app/components/oip-drugs/fiches/PndsFiche.vue +279 -0
  16. package/layer/app/components/oip-drugs/heart/JoCard.vue +314 -0
  17. package/layer/app/components/oip-drugs/heart/TextHighlight.vue +326 -0
  18. package/layer/app/components/oip-drugs/heart/ansm/BrandCard.vue +187 -0
  19. package/layer/app/components/oip-drugs/heart/ansm/CisDetail.vue +363 -0
  20. package/layer/app/components/oip-drugs/heart/ansm/CisTable.vue +167 -0
  21. package/layer/app/components/oip-drugs/heart/associations/NationalAssemblyCard.vue +373 -0
  22. package/layer/app/components/oip-drugs/heart/associations/NationalAssemblyFiche.vue +372 -0
  23. package/layer/app/components/oip-drugs/heart/associations/TranscriptionCard.vue +55 -0
  24. package/layer/app/components/oip-drugs/heart/elastic/SourceContainerElastic.vue +103 -0
  25. package/layer/app/components/oip-drugs/heart/france/BrandGroupCard.vue +450 -0
  26. package/layer/app/components/oip-drugs/heart/mitm/Card.vue +199 -0
  27. package/layer/app/components/oip-drugs/heart/mitm/Source.vue +23 -0
  28. package/layer/app/components/oip-drugs/heart/mitm/details/Overview.vue +381 -0
  29. package/layer/app/components/oip-drugs/heart/put/Card.vue +212 -0
  30. package/layer/app/components/oip-drugs/heart/results/AccesCard.vue +696 -0
  31. package/layer/app/components/oip-drugs/heart/results/AlertCard.vue +342 -0
  32. package/layer/app/components/oip-drugs/heart/results/AsmrCursor.vue +229 -0
  33. package/layer/app/components/oip-drugs/heart/results/EvalCard.vue +370 -0
  34. package/layer/app/components/oip-drugs/heart/results/IndicCard.vue +743 -0
  35. package/layer/app/components/oip-drugs/heart/results/MatchExplain.vue +243 -0
  36. package/layer/app/components/oip-drugs/heart/results/PrefRows.vue +105 -0
  37. package/layer/app/components/oip-drugs/heart/results/details/HasHistory.vue +487 -0
  38. package/layer/app/components/oip-drugs/heart/results/details/InternationalCountryCard.vue +461 -0
  39. package/layer/app/components/oip-drugs/heart/results/details/InternationalOverview.vue +200 -0
  40. package/layer/app/components/oip-drugs/heart/results/details/InternationalPriceDrawer.vue +647 -0
  41. package/layer/app/components/oip-drugs/heart/results/details/Monograph.vue +128 -0
  42. package/layer/app/components/oip-drugs/heart/results/details/OpinionDrawer.vue +535 -0
  43. package/layer/app/components/oip-drugs/heart/results/details/PresentationDrawer.vue +738 -0
  44. package/layer/app/components/oip-drugs/heart/results/details/Price.vue +227 -0
  45. package/layer/app/components/oip-drugs/heart/results/details/PriceOverview.vue +1261 -0
  46. package/layer/app/components/oip-drugs/heart/results/details/RecordDrawer.vue +494 -0
  47. package/layer/app/components/oip-drugs/heart/results/details/RecordList.vue +426 -0
  48. package/layer/app/components/oip-drugs/heart/results/les/Card.vue +340 -0
  49. package/layer/app/components/oip-drugs/heart/results/les/Drawer.vue +67 -0
  50. package/layer/app/components/oip-drugs/heart/results/les/IndicationDetail.vue +149 -0
  51. package/layer/app/components/oip-drugs/heart/results/les/Source.vue +20 -0
  52. package/layer/app/components/oip-drugs/heart/results/les/SourceTag.vue +39 -0
  53. package/layer/app/components/oip-drugs/heart/searchPage/DynamicSource.vue +29 -0
  54. package/layer/app/components/oip-drugs/medicam/market/Chart.vue +121 -0
  55. package/layer/app/components/oip-drugs/outpatient/ProductEvolution.vue +236 -0
  56. package/layer/app/components/oip-drugs/resources/Ansm.vue +75 -0
  57. package/layer/app/components/oip-drugs/resources/Association.vue +30 -0
  58. package/layer/app/components/oip-drugs/resources/Brand.vue +65 -0
  59. package/layer/app/components/oip-drugs/resources/Contribution.vue +216 -0
  60. package/layer/app/components/oip-drugs/resources/Hospital.vue +151 -0
  61. package/layer/app/components/oip-drugs/resources/Indication.vue +54 -0
  62. package/layer/app/components/oip-drugs/resources/Jo.vue +57 -0
  63. package/layer/app/components/oip-drugs/resources/Medicam.vue +140 -0
  64. package/layer/app/components/oip-drugs/resources/Mitm.vue +84 -0
  65. package/layer/app/components/oip-drugs/resources/NationalAssembly.vue +93 -0
  66. package/layer/app/components/oip-drugs/resources/Outpatient.vue +97 -0
  67. package/layer/app/components/oip-drugs/resources/Pnds.vue +24 -0
  68. package/layer/app/components/oip-drugs/resources/Price.vue +106 -0
  69. package/layer/app/components/oip-drugs/resources/Put.vue +143 -0
  70. package/layer/app/components/oip-science/appel-a-projet/Cards.vue +162 -0
  71. package/layer/app/components/oip-science/clinical-trials/Cards.vue +382 -0
  72. package/layer/app/components/oip-science/clinical-trials/CardsGlobal.vue +273 -0
  73. package/layer/app/components/oip-science/clinical-trials/LinkedList.vue +366 -0
  74. package/layer/app/components/oip-science/clinical-trials/PairBadge.vue +25 -0
  75. package/layer/app/components/oip-science/clinical-trials/PairBody.vue +182 -0
  76. package/layer/app/components/oip-science/clinical-trials/SitesDrawer.vue +95 -0
  77. package/layer/app/components/oip-science/common/Form.vue +73 -0
  78. package/layer/app/components/oip-science/common/MatchAvgTag.vue +283 -0
  79. package/layer/app/components/oip-science/common/NoData.vue +16 -0
  80. package/layer/app/components/oip-science/common/ScoreBadge.vue +35 -0
  81. package/layer/app/components/oip-science/common/Source.vue +31 -0
  82. package/layer/app/components/oip-science/common/TableTags.vue +279 -0
  83. package/layer/app/components/oip-science/common/Timeline.vue +104 -0
  84. package/layer/app/components/oip-science/common/ToolTips.vue +89 -0
  85. package/layer/app/components/oip-science/cross/publicationPubmed.vue +484 -0
  86. package/layer/app/components/oip-science/crossData/CrossDataLocations.vue +71 -0
  87. package/layer/app/components/oip-science/ctgov/Investigators.vue +148 -0
  88. package/layer/app/components/oip-science/ctgov/Locations.vue +102 -0
  89. package/layer/app/components/oip-science/ctgov/Publications.vue +52 -0
  90. package/layer/app/components/oip-science/ctgov/Sponsors.vue +66 -0
  91. package/layer/app/components/oip-science/ctgov/StudyDescription.vue +1136 -0
  92. package/layer/app/components/oip-science/ctgov-centers/ClinicalTrials.vue +40 -0
  93. package/layer/app/components/oip-science/ctgov-centers/Publications.vue +50 -0
  94. package/layer/app/components/oip-science/ctgov-investigators/ClinicalTrials.vue +43 -0
  95. package/layer/app/components/oip-science/ctgov-investigators/Informations.vue +171 -0
  96. package/layer/app/components/oip-science/ctgov-investigators/Publications.vue +44 -0
  97. package/layer/app/components/oip-science/elastic/DateRangeFilter.vue +209 -0
  98. package/layer/app/components/oip-science/elastic/Filter.vue +719 -0
  99. package/layer/app/components/oip-science/elastic/MatchExplain.vue +152 -0
  100. package/layer/app/components/oip-science/establishments/Cards.vue +183 -0
  101. package/layer/app/components/oip-science/establishments/ClinicalTrials.vue +62 -0
  102. package/layer/app/components/oip-science/establishments/Hcps.vue +227 -0
  103. package/layer/app/components/oip-science/establishments/Informations.vue +263 -0
  104. package/layer/app/components/oip-science/establishments/LinkedList.vue +153 -0
  105. package/layer/app/components/oip-science/establishments/MatchedLabels.vue +194 -0
  106. package/layer/app/components/oip-science/establishments/PublicFinance.vue +62 -0
  107. package/layer/app/components/oip-science/euct/Investigators.vue +125 -0
  108. package/layer/app/components/oip-science/euct/Locations.vue +102 -0
  109. package/layer/app/components/oip-science/euct/Products.vue +101 -0
  110. package/layer/app/components/oip-science/euct/Publications.vue +89 -0
  111. package/layer/app/components/oip-science/euct/Sponsors.vue +86 -0
  112. package/layer/app/components/oip-science/euct/StudyDescription.vue +1075 -0
  113. package/layer/app/components/oip-science/euct-centers/ClinicalTrials.vue +40 -0
  114. package/layer/app/components/oip-science/euct-centers/Publications.vue +55 -0
  115. package/layer/app/components/oip-science/euct-investigators/ClinicalTrials.vue +41 -0
  116. package/layer/app/components/oip-science/euct-investigators/Informations.vue +171 -0
  117. package/layer/app/components/oip-science/euct-investigators/Publications.vue +51 -0
  118. package/layer/app/components/oip-science/hcps/AppelsAProjet.vue +62 -0
  119. package/layer/app/components/oip-science/hcps/Cards.vue +237 -0
  120. package/layer/app/components/oip-science/hcps/ClinicalTrials.vue +78 -0
  121. package/layer/app/components/oip-science/hcps/Informations.vue +296 -0
  122. package/layer/app/components/oip-science/hcps/LinkedList.vue +153 -0
  123. package/layer/app/components/oip-science/hcps/MatchedLabels.vue +207 -0
  124. package/layer/app/components/oip-science/hcps/Pubmed.vue +128 -0
  125. package/layer/app/components/oip-science/local/SliderTabs.vue +99 -0
  126. package/layer/app/components/oip-science/local/SliderTabsFull.vue +135 -0
  127. package/layer/app/components/oip-science/pubmed/Authors.vue +128 -0
  128. package/layer/app/components/oip-science/pubmed/Cards.vue +417 -0
  129. package/layer/app/components/oip-science/pubmed/ClinicalTrials.vue +71 -0
  130. package/layer/app/components/oip-science/resources/Phrc.vue +39 -0
  131. package/layer/app/composables/useOipChapterScroll.ts +43 -0
  132. package/layer/app/composables/useOipResourceHooks.ts +27 -0
  133. package/layer/app/composables/useOipResourceNav.ts +66 -0
  134. package/layer/app/composables/useOipResourceViews.ts +117 -0
  135. package/layer/app/composables/useOipResources.ts +83 -0
  136. package/layer/app/oip-drugs/PORTING.md +120 -0
  137. package/layer/app/oip-drugs/composables/useAssociationDetail.ts +220 -0
  138. package/layer/app/oip-drugs/composables/useAssociationExport.ts +63 -0
  139. package/layer/app/oip-drugs/composables/useAssociationLookup.ts +105 -0
  140. package/layer/app/oip-drugs/composables/useCardPrefOptions.ts +201 -0
  141. package/layer/app/oip-drugs/composables/useCurrency.ts +121 -0
  142. package/layer/app/oip-drugs/composables/useDirectusSources.ts +224 -0
  143. package/layer/app/oip-drugs/composables/useHeartBrandMatches.ts +112 -0
  144. package/layer/app/oip-drugs/composables/useHeartDetailSearches.ts +479 -0
  145. package/layer/app/oip-drugs/composables/useHeartIntlApprovals.ts +661 -0
  146. package/layer/app/oip-drugs/composables/useHeartLesIndication.ts +168 -0
  147. package/layer/app/oip-drugs/composables/useHeartMatchExplain.ts +231 -0
  148. package/layer/app/oip-drugs/composables/useHeartReports.ts +239 -0
  149. package/layer/app/oip-drugs/composables/useHeartSearchFields.ts +233 -0
  150. package/layer/app/oip-drugs/composables/useMergeByKey.ts +23 -0
  151. package/layer/app/oip-drugs/composables/useOipDrugsElastic.ts +621 -0
  152. package/layer/app/oip-drugs/composables/usePageEmpty.ts +58 -0
  153. package/layer/app/oip-drugs/composables/v-useSalesSource.ts +64 -0
  154. package/layer/app/oip-drugs/drugs.css +500 -0
  155. package/layer/app/oip-drugs/esIndices.ts +322 -0
  156. package/layer/app/oip-drugs/kinds.ts +264 -0
  157. package/layer/app/oip-drugs/locales/en.json +1571 -0
  158. package/layer/app/oip-drugs/locales/fr.json +1571 -0
  159. package/layer/app/oip-drugs/monographs/blincyto-has.vue +289 -0
  160. package/layer/app/oip-drugs/monographs/darzalex-has.vue +276 -0
  161. package/layer/app/oip-drugs/monographs/dupixent-has.vue +216 -0
  162. package/layer/app/oip-drugs/monographs/enhertu-has.vue +280 -0
  163. package/layer/app/oip-drugs/monographs/has-sein-2023.vue +1649 -0
  164. package/layer/app/oip-drugs/monographs/ibrance-has.vue +277 -0
  165. package/layer/app/oip-drugs/monographs/imfinzi-has.vue +293 -0
  166. package/layer/app/oip-drugs/monographs/keytruda-has.vue +334 -0
  167. package/layer/app/oip-drugs/monographs/kisqali-has.vue +201 -0
  168. package/layer/app/oip-drugs/monographs/opdivo-has.vue +231 -0
  169. package/layer/app/oip-drugs/monographs/ozempic-has.vue +186 -0
  170. package/layer/app/oip-drugs/monographs/tecentriq-has.vue +312 -0
  171. package/layer/app/oip-drugs/monographs/yescarta-has.vue +272 -0
  172. package/layer/app/oip-drugs/types/elastic.ts +113 -0
  173. package/layer/app/oip-drugs/types/es/agencies.ts +152 -0
  174. package/layer/app/oip-drugs/types/es/directory.ts +54 -0
  175. package/layer/app/oip-drugs/types/es/docByIndex.ts +151 -0
  176. package/layer/app/oip-drugs/types/es/openmedic.ts +67 -0
  177. package/layer/app/oip-drugs/types/es/outpatient.ts +71 -0
  178. package/layer/app/oip-drugs/types/es/portal.ts +130 -0
  179. package/layer/app/oip-drugs/types/es/retail.ts +107 -0
  180. package/layer/app/oip-drugs/types/es/statistics.ts +29 -0
  181. package/layer/app/oip-drugs/types/heart/CurrencyRate.ts +16 -0
  182. package/layer/app/oip-drugs/types/heart/ansm.ts +106 -0
  183. package/layer/app/oip-drugs/types/heart/tag.ts +29 -0
  184. package/layer/app/oip-drugs/utils/associationFields.ts +118 -0
  185. package/layer/app/oip-drugs/utils/contributions.ts +36 -0
  186. package/layer/app/oip-drugs/utils/countryMapping.ts +387 -0
  187. package/layer/app/oip-drugs/utils/esHighlight.ts +21 -0
  188. package/layer/app/oip-drugs/utils/exportSheets.ts +57 -0
  189. package/layer/app/oip-drugs/utils/heartIntlPrice.ts +228 -0
  190. package/layer/app/oip-drugs/utils/heartStyleAndFormat.ts +262 -0
  191. package/layer/app/oip-drugs/utils/heartStyleAnsm.ts +71 -0
  192. package/layer/app/oip-drugs/utils/latestDate.ts +31 -0
  193. package/layer/app/oip-drugs/utils/p-heartCardTags.ts +53 -0
  194. package/layer/app/oip-drugs/utils/priceExport.ts +269 -0
  195. package/layer/app/oip-drugs/utils/q-hasRatings.ts +43 -0
  196. package/layer/app/oip-drugs/utils/s-openCard.ts +20 -0
  197. package/layer/app/oip-drugs/utils/sourceUuids.ts +155 -0
  198. package/layer/app/oip-drugs/utils/styleAndFormat.ts +40 -0
  199. package/layer/app/oip-drugs/utils/v-salesSeries.ts +156 -0
  200. package/layer/app/oip-drugs/utils/v-scopeTags.ts +23 -0
  201. package/layer/app/oip-science/PORTING.md +93 -0
  202. package/layer/app/oip-science/assets/smartSelection.json +428 -0
  203. package/layer/app/oip-science/composables/useAuthorMatching.ts +342 -0
  204. package/layer/app/oip-science/composables/useCTCenter.ts +107 -0
  205. package/layer/app/oip-science/composables/useCTInvestigators.ts +107 -0
  206. package/layer/app/oip-science/composables/useEstablishmentCrossData.ts +139 -0
  207. package/layer/app/oip-science/composables/useEstablishmentMatching.ts +176 -0
  208. package/layer/app/oip-science/composables/useEuct.ts +418 -0
  209. package/layer/app/oip-science/composables/useEuctCenter.ts +107 -0
  210. package/layer/app/oip-science/composables/useEuctInvestigators.ts +107 -0
  211. package/layer/app/oip-science/composables/useGlobalClinicalTrials.ts +624 -0
  212. package/layer/app/oip-science/composables/useLinkedTrials.ts +83 -0
  213. package/layer/app/oip-science/composables/useOipElastic.ts +658 -0
  214. package/layer/app/oip-science/composables/useOipSearchExplain.ts +166 -0
  215. package/layer/app/oip-science/composables/usePubmed.ts +487 -0
  216. package/layer/app/oip-science/composables/useSearchFields.ts +153 -0
  217. package/layer/app/oip-science/indexConfig.json +794 -0
  218. package/layer/app/oip-science/locales/en.json +483 -0
  219. package/layer/app/oip-science/locales/fr.json +483 -0
  220. package/layer/app/oip-science/science.css +1182 -0
  221. package/layer/app/oip-science/utils/a-openCard.ts +23 -0
  222. package/layer/app/oip-science/utils/b-openCard.ts +35 -0
  223. package/layer/app/oip-science/utils/c-filterContext.ts +81 -0
  224. package/layer/app/oip-science/utils/f-types.ts +241 -0
  225. package/layer/app/oip-science/utils/frDiseaseTerms.ts +269 -0
  226. package/layer/app/oip-science/utils/matchMethod.ts +53 -0
  227. package/layer/app/oip-science/utils/sliderTabs.ts +13 -0
  228. package/layer/app/oip-science/utils/sources.ts +15 -0
  229. package/layer/app/oip-science/utils/styleAndFormat.ts +608 -0
  230. package/layer/app/oip-science/utils/toPresets.ts +7 -0
  231. package/layer/app/oip-science/utils/valueTable.ts +126 -0
  232. package/layer/app/plugins/oip-science-i18n.ts +17 -0
  233. package/layer/app/utils/oipFicheRoutes.ts +118 -0
  234. package/layer/app/utils/oipResources.ts +440 -0
  235. package/layer/nuxt.config.ts +22 -2
  236. package/layer/public/reports/blincyto-has.html +292 -0
  237. package/layer/public/reports/blincyto-has.pdf +0 -0
  238. package/layer/public/reports/darzalex-has.html +279 -0
  239. package/layer/public/reports/darzalex-has.pdf +0 -0
  240. package/layer/public/reports/dupixent-has.html +217 -0
  241. package/layer/public/reports/dupixent-has.pdf +0 -0
  242. package/layer/public/reports/enhertu-has.html +283 -0
  243. package/layer/public/reports/enhertu-has.pdf +0 -0
  244. package/layer/public/reports/has-sein-2023.html +1639 -0
  245. package/layer/public/reports/ibrance-has.html +280 -0
  246. package/layer/public/reports/ibrance-has.pdf +0 -0
  247. package/layer/public/reports/imfinzi-has.html +297 -0
  248. package/layer/public/reports/imfinzi-has.pdf +0 -0
  249. package/layer/public/reports/keytruda-has.html +337 -0
  250. package/layer/public/reports/keytruda-has.pdf +0 -0
  251. package/layer/public/reports/kisqali-has.html +202 -0
  252. package/layer/public/reports/kisqali-has.pdf +0 -0
  253. package/layer/public/reports/opdivo-has.html +232 -0
  254. package/layer/public/reports/opdivo-has.pdf +0 -0
  255. package/layer/public/reports/ozempic-has.html +188 -0
  256. package/layer/public/reports/ozempic-has.pdf +0 -0
  257. package/layer/public/reports/tecentriq-has.html +315 -0
  258. package/layer/public/reports/tecentriq-has.pdf +0 -0
  259. package/layer/public/reports/yescarta-has.html +275 -0
  260. package/layer/public/reports/yescarta-has.pdf +0 -0
  261. package/layer/server/api/oip/es.post.ts +99 -0
  262. package/layer/server/api/oip/pdf.get.ts +70 -0
  263. package/layer/server/api/oip/reports.get.ts +98 -0
  264. package/layer/server/api/oip/resource-types.get.ts +22 -0
  265. package/package.json +1 -1
@@ -0,0 +1,484 @@
1
+ <!--
2
+ Liste filtree des publications PubMed d'une fiche (onglets « Publications »
3
+ des essais, investigateurs, centres, professionnels, etablissements),
4
+ anciennement CrossPublicationPubmed de France Augmented Science.
5
+
6
+ Les filtres (periode, type d'article, onglets GeneRIF) vivaient dans les
7
+ stores de FAS ranges par route ; ici chaque liste a son propre contexte
8
+ (c-filterContext), donne a ses filtres : deux listes a l'ecran ne partagent
9
+ rien.
10
+ -->
11
+ <template>
12
+ <div class="oip-science">
13
+ <div class="oip_grid gap-16">
14
+ <div v-if="geneSymbol">
15
+ <div class="grid gap-16">
16
+ <div class="kk_two_grid" v-if="geneSymbol">
17
+ <OipScienceCommonForm
18
+ :title="$t('oipScience.cards.firstReference')"
19
+ :content="ifNoRes(geneFirstRef, 'date')"
20
+ />
21
+ <OipScienceCommonForm
22
+ :title="$t('oipScience.cards.lastGeneRif')"
23
+ :content="ifNoRes(geneLastRif, 'date')"
24
+ />
25
+ </div>
26
+
27
+ <OipScienceLocalSliderTabs
28
+ v-if="geneSymbol"
29
+ sliderId="sliderTabsFull"
30
+ :filterValues="getStore('SliderMenu')"
31
+ filterName="SliderMenu"
32
+ :active-tab="activeMenu"
33
+ @change-element="switchMenu"
34
+ :tabs="[
35
+ { label: $t('oipScience.cards.all') },
36
+ { label: $t('oipScience.cards.reference') },
37
+ { label: 'GeneRIF' },
38
+ ]"
39
+ isFullWidth
40
+ />
41
+
42
+ </div>
43
+ </div>
44
+
45
+ <!-- Recherche et filtres dans la barre des autres pages ; les onglets
46
+ GeneRIF n'ont pas de filtres, la recherche reste seule. -->
47
+ <RmFilterBar
48
+ v-if="!geneSymbol || activeMenu === 0"
49
+
50
+ :class="{ pub_fbar_top: !geneSymbol }"
51
+ :filters-amount="filtersAmount"
52
+ @clear-filters="clearFilters"
53
+ :lang="$i18n.locale === 'en' ? 'en' : 'fr'"
54
+ >
55
+ <template #Search>
56
+ <RmSearchBar
57
+ v-model="searchText"
58
+ class="pub_search"
59
+ :placeholder="$t('oipScience.common.searchPublications')"
60
+ :have-advanced="false"
61
+ />
62
+ </template>
63
+ <template #Filters>
64
+ <OipScienceElasticDateRangeFilter
65
+ :key="'period' + filtersKey"
66
+ :filterValues="getStore('Period')"
67
+ filterName="Period"
68
+ :label="$t('oipScience.ui.filters.period')"
69
+ field="article_date"
70
+ :indexFirstSelection="5"
71
+ />
72
+
73
+ <OipScienceElasticFilter
74
+ :key="'type' + filtersKey"
75
+ :filterValues="getStore('Article Type')"
76
+ field="publication_types"
77
+ filterName="Article Type"
78
+ :label="$t('oipScience.common.articleType')"
79
+ :indexQuery="indexQuery"
80
+ :matchQuery="matchQuery"
81
+ :cluster="clusterName"
82
+ />
83
+ </template>
84
+ </RmFilterBar>
85
+
86
+ <template v-if="!isLoadingQuery">
87
+ <RmResultsLine icon="Article" :items="[{ count: filteredItems.length, label: $t('oipScience.resultKpis.publications', filteredItems.length) }]" />
88
+ <RmSearchBar
89
+ v-if="geneSymbol && activeMenu !== 0"
90
+ v-model="searchText"
91
+ :placeholder="$t('oipScience.common.searchPublications')"
92
+ :have-advanced="false"
93
+ />
94
+
95
+ <!-- Meme carte que /pubmed. Seules les cartes affichees chargent la
96
+ fiche complete (resume, tags) : une liste peut compter 1000 articles. -->
97
+ <div class="oip_grid gap-8">
98
+ <template v-for="item in visibleItems" :key="item.PubMed_ID">
99
+ <OipSciencePubmedCards
100
+ v-if="fullDocs.get(String(item.PubMed_ID))"
101
+ :data="fullDocs.get(String(item.PubMed_ID))"
102
+ :id="String(item.PubMed_ID)"
103
+ :highlight="highlight"
104
+ />
105
+ </template>
106
+ </div>
107
+
108
+ <RmLoadingState v-if="isLoadingDocs" :lines="2" />
109
+ <RmEmptyState
110
+ v-if="!filteredItems.length"
111
+ :title="$t('oipScience.common.noMatchingResult')"
112
+ />
113
+ <RmButton
114
+ v-if="visibleItems.length < filteredItems.length"
115
+ variant="secondary"
116
+ class="load_more"
117
+ @click="displayCount += PAGE_SIZE"
118
+ >{{ $t('oipScience.common.loadMore') }}</RmButton>
119
+ </template>
120
+ </div>
121
+ <RmLoadingState v-if="isLoadingQuery" :lines="3" :chart-height="240" />
122
+ </div>
123
+ </template>
124
+
125
+ <script setup lang="ts">
126
+ import type { PropType } from 'vue';
127
+ import indexConfig from '../../../oip-science/indexConfig.json';
128
+ import useOipElastic from '../../../oip-science/composables/useOipElastic';
129
+ import usePubmed from '../../../oip-science/composables/usePubmed';
130
+ import { ifNoRes } from '../../../oip-science/utils/styleAndFormat';
131
+ import { provideOipFilterContext } from '../../../oip-science/utils/c-filterContext';
132
+
133
+ const props = defineProps({
134
+ customQuery: {
135
+ type: Object,
136
+ default: {},
137
+ },
138
+ geneSymbol: {
139
+ type: String,
140
+ default: null,
141
+ },
142
+ from: {
143
+ type: String,
144
+ required: true,
145
+ },
146
+ // Garde pour l'API de FAS, qui ne s'en servait pas.
147
+ searchHighlight: {
148
+ type: Array,
149
+ default: [],
150
+ },
151
+ // Termes surlignes dans les cartes (FAS les lisait dans le filterStore).
152
+ highlight: {
153
+ type: Array as PropType<string[]>,
154
+ default: () => [],
155
+ },
156
+ });
157
+
158
+ // Etat des filtres de cette liste (anciennement filterStore et
159
+ // elasticQueryStore, par route).
160
+ const filterContext = provideOipFilterContext();
161
+
162
+ const { queryElastic } = useOipElastic();
163
+ const indexQuery = ref(indexConfig.pubmed.index);
164
+ const clusterName = ref(indexConfig.pubmed.cluster);
165
+ const { pubmedContentFromIds } = usePubmed();
166
+ const { queryCustomElastic } = useOipElastic();
167
+ // :::::::::::::::::::::::::::::
168
+ const resultsQuery = ref<any[] | null>(null);
169
+
170
+ const isLoadingQuery = ref(true);
171
+ // :::::::::::::::::::::::::::::
172
+ // FILTERS
173
+
174
+ // :::::::::::::::::::::::::::::
175
+
176
+ const getStore = (filter: string) => {
177
+ return filterContext.findValuesStore({ filter });
178
+ };
179
+
180
+ // Puce « Filtres » : ce qui est pose a la main, et l'effacement. La requete
181
+ // de la page (`Custom Query`) reste, seuls ces deux filtres partent ; les
182
+ // composants sont remontes pour revenir a leur etat initial.
183
+ const PUB_FILTERS = ['Period', 'Article Type'];
184
+ const filtersKey = ref(0);
185
+ const filtersAmount = computed(() =>
186
+ PUB_FILTERS.reduce((sum, filter) => sum + getStore(filter).flat().length, 0),
187
+ );
188
+ function clearFilters() {
189
+ for (const filter of PUB_FILTERS) {
190
+ filterContext.resetSelectedFilters({ filter });
191
+ filterContext.removeQueryFilter({ filter });
192
+ }
193
+ filtersKey.value++;
194
+ }
195
+
196
+ // La requete de la page est posee avant le montage des filtres (FAS la posait
197
+ // a onMounted) : la premiere recherche part deja restreinte a la liste.
198
+ filterContext.setQueryFilters({
199
+ filter: 'Custom Query',
200
+ values: props.customQuery,
201
+ keyName: null,
202
+ });
203
+
204
+ onMounted(async () => {
205
+ if (props.geneSymbol) {
206
+ await getLastRefRif();
207
+ }
208
+ });
209
+
210
+ const sources = ref([
211
+ 'PubMed_ID',
212
+ 'title',
213
+ 'authors',
214
+ 'article_date',
215
+ 'publication_types',
216
+ 'genomics',
217
+ ]);
218
+
219
+ const geneFirstRef = ref(null as any);
220
+ const geneLastRif = ref(null as any);
221
+
222
+ // :::::::::::::::::::::::::::::
223
+ const queriesFilters = computed(() => {
224
+ return filterContext.findQueryFromPage({});
225
+ });
226
+
227
+ const matchQuery = computed(() => {
228
+ return queriesFilters.value.filter(
229
+ (x) => x.filter !== 'Take' && x.filter !== 'Sort'
230
+ );
231
+ });
232
+
233
+ watch(
234
+ () => queriesFilters.value,
235
+ async (newVal, oldVal) => {
236
+ if (
237
+ !resultsQuery.value ||
238
+ (filterContext.findQueryFromPage({}).length != 0 &&
239
+ JSON.stringify(oldVal) !== JSON.stringify(newVal))
240
+ ) {
241
+ await launchQuery();
242
+ }
243
+ },
244
+ { deep: true, flush: 'post' }
245
+ );
246
+
247
+ // :::::::::::::::::::::::::::::
248
+
249
+ const getLastRefRif = async () => {
250
+ try {
251
+ await queryCustomElastic({
252
+ index: indexConfig.gene.index,
253
+ cluster: indexConfig.gene.cluster,
254
+ customQuery: {
255
+ _source: ['date_first_reference', 'date_last_generif_update'],
256
+
257
+ from: 0,
258
+ query: {
259
+ bool: {
260
+ must: [
261
+ {
262
+ term: {
263
+ 'symbol.keyword': props.geneSymbol,
264
+ },
265
+ },
266
+ ],
267
+ },
268
+ },
269
+ size: 1000,
270
+ track_total_hits: true,
271
+ },
272
+ }).then((res) => {
273
+ if (res?.results?.length) {
274
+ geneFirstRef.value = res.results[0].date_first_reference;
275
+ geneLastRif.value = res.results[0].date_last_generif_update;
276
+ }
277
+ });
278
+ } catch (error) {
279
+ console.error('Error in launchQuery:', error);
280
+ }
281
+ };
282
+
283
+ const launchQuery = async (): Promise<void> => {
284
+ isLoadingQuery.value = true;
285
+
286
+ try {
287
+ if (!props.geneSymbol) {
288
+ await getPubmedInfos();
289
+ }
290
+ // "is_gene_nih": true si
291
+ // => gene qui vient de la base NIH
292
+
293
+ // "is_hugo_reference": true
294
+ // => gene qui vient de la base HUGO
295
+
296
+ // "is_gene_rif": true
297
+ // => gene qui vient de la base Hugo mais qui possede des articles pubmed
298
+
299
+ // "is_tekkare_reference": true,
300
+ // => gene qui vient d'aucune des bases
301
+
302
+ // Quand un true les autres sont false
303
+ if (props.geneSymbol) {
304
+ if (activeMenu.value === 0) {
305
+ await getPubmedInfos();
306
+ } else if (activeMenu.value === 1) {
307
+ await getGeneInfos('is_hugo_reference');
308
+ } else if (activeMenu.value === 2) {
309
+ await getGeneInfos('is_nih_generif');
310
+ }
311
+ }
312
+ } catch (e) {
313
+ console.error('Error queuing query:', e);
314
+ } finally {
315
+ isLoadingQuery.value = false;
316
+ }
317
+ };
318
+
319
+ const getPubmedInfos = async () => {
320
+ await queryElastic({
321
+ index: indexQuery.value,
322
+ source: sources.value,
323
+ take: 1000,
324
+ customQuery: matchQuery.value,
325
+ sortQuery: {
326
+ values: [
327
+ {
328
+ article_date: {
329
+ order: 'desc',
330
+ },
331
+ },
332
+ ],
333
+ },
334
+ cluster: clusterName.value,
335
+ }).then((res) => {
336
+ resultsQuery.value = res?.results;
337
+ });
338
+ };
339
+
340
+ const getGeneInfos = async (source: string) => {
341
+ await pubmedContentFromIds({
342
+ customQuery: {
343
+ _source: sources.value,
344
+
345
+ from: 0,
346
+ query: {
347
+ bool: {
348
+ must: [
349
+ {
350
+ term: {
351
+ delete: false,
352
+ },
353
+ },
354
+
355
+ {
356
+ nested: {
357
+ path: 'genomics',
358
+ query: {
359
+ bool: {
360
+ must: [
361
+ {
362
+ terms: {
363
+ 'genomics.gene_symbol.keyword': [props.geneSymbol],
364
+ },
365
+ },
366
+ {
367
+ term: {
368
+ [`genomics.${source}`]: true,
369
+ },
370
+ },
371
+ ],
372
+ },
373
+ },
374
+ },
375
+ },
376
+ ],
377
+ },
378
+ },
379
+ size: 1000,
380
+ track_total_hits: true,
381
+ sort: [{ article_date: { order: 'desc' } }],
382
+ },
383
+ }).then((res: any) => {
384
+ resultsQuery.value = res.results;
385
+ });
386
+ };
387
+
388
+ // :::::::::::::::::::::::::::::
389
+
390
+ // :::::::::::::::::::::::::::::
391
+ // CARDS
392
+
393
+ // Filtre local et affichage par paquets de cartes.
394
+ const searchText = ref('');
395
+ const PAGE_SIZE = 20;
396
+ const displayCount = ref(PAGE_SIZE);
397
+
398
+ const filteredItems = computed(() => {
399
+ const items = resultsQuery.value || [];
400
+ const q = searchText.value.trim().toLowerCase();
401
+ if (!q) return items;
402
+ return items.filter((item: any) =>
403
+ [
404
+ item.PubMed_ID,
405
+ item.title,
406
+ ...(item.authors || []).map(
407
+ (author: any) => `${author.forename} ${author.lastname}`
408
+ ),
409
+ ]
410
+ .filter(Boolean)
411
+ .join(' ')
412
+ .toLowerCase()
413
+ .includes(q)
414
+ );
415
+ });
416
+
417
+ const visibleItems = computed(() =>
418
+ filteredItems.value.slice(0, displayCount.value)
419
+ );
420
+
421
+ watch([searchText, resultsQuery], () => {
422
+ displayCount.value = PAGE_SIZE;
423
+ });
424
+
425
+ // Fiches completes des cartes affichees, chargees par paquet et gardees en
426
+ // cache : la requete de liste ne ramene que les champs du filtre.
427
+ const fullDocs = ref(new Map<string, any>());
428
+ const isLoadingDocs = ref(false);
429
+
430
+ watch(
431
+ visibleItems,
432
+ async (items) => {
433
+ const missing = items
434
+ .map((item: any) => String(item.PubMed_ID ?? ''))
435
+ .filter((id) => id && !fullDocs.value.has(id));
436
+ if (!missing.length) return;
437
+ isLoadingDocs.value = true;
438
+ try {
439
+ const res = await queryCustomElastic({
440
+ index: indexConfig.pubmed.index,
441
+ cluster: indexConfig.pubmed.cluster,
442
+ customQuery: {
443
+ size: missing.length,
444
+ _source: {
445
+ excludes: ['embedding', 'embeddings_small', 'med_embeddings'],
446
+ },
447
+ query: { terms: { PubMed_ID: missing } },
448
+ },
449
+ });
450
+ const next = new Map(fullDocs.value);
451
+ for (const doc of res?.results || []) {
452
+ if (doc?.PubMed_ID != null) next.set(String(doc.PubMed_ID), doc);
453
+ }
454
+ fullDocs.value = next;
455
+ } finally {
456
+ isLoadingDocs.value = false;
457
+ }
458
+ },
459
+ { immediate: true }
460
+ );
461
+
462
+ const activeMenu = ref(0 as number);
463
+ watch(
464
+ () => activeMenu.value,
465
+ async (oldValue, newValue) => {
466
+ if (oldValue !== newValue) {
467
+ await launchQuery();
468
+ }
469
+ }
470
+ );
471
+
472
+ const switchMenu = (val: any) => {
473
+ activeMenu.value = val;
474
+ };
475
+
476
+ // :::::::::::::::::::::::::::::
477
+ </script>
478
+
479
+ <style scoped>
480
+ .load_more {
481
+ align-self: center;
482
+ justify-self: center;
483
+ }
484
+ </style>
@@ -0,0 +1,71 @@
1
+ <!--
2
+ Etablissements rapproches d'un essai (index hco__nct / hco__euct), pour un
3
+ essai et son jumeau de l'autre registre. Rendu avec la carte de la page
4
+ /establishments. (Portage de components/crossData/CrossDataLocations.vue de
5
+ France Augmented Science.)
6
+ -->
7
+ <template>
8
+ <OipScienceEstablishmentsLinkedList
9
+ :locations="locations"
10
+ :title="$t('oipScience.ui.matchedEstablishments')"
11
+ :sources="['CT.gov', 'EUCT', 'FINESS']"
12
+ index-last-update="CT.gov"
13
+ :loading="isLoading"
14
+ />
15
+ </template>
16
+
17
+ <script setup lang="ts">
18
+ import type { LinkedLocation } from '../establishments/LinkedList.vue';
19
+ import useEstablishmentMatching from '../../../oip-science/composables/useEstablishmentMatching';
20
+
21
+ const props = defineProps<{
22
+ id: string;
23
+ nctLinked?: string | null;
24
+ }>();
25
+
26
+ const { fetchTrialHcoMatches } = useEstablishmentMatching();
27
+
28
+ type Match = Awaited<ReturnType<typeof fetchTrialHcoMatches>>[number];
29
+ const matches = ref<Match[]>([]);
30
+ const isLoading = ref(true);
31
+
32
+ async function loadAll() {
33
+ isLoading.value = true;
34
+ try {
35
+ const ids = [props.id, props.nctLinked].filter(Boolean) as string[];
36
+ const results = await Promise.all(
37
+ ids.map((id) =>
38
+ fetchTrialHcoMatches(id, id.startsWith('NCT') ? 'nct' : 'euct').catch(
39
+ () => [] as Match[],
40
+ ),
41
+ ),
42
+ );
43
+ // Un etablissement par FINESS, les deux registres confondus.
44
+ const seen = new Set<string>();
45
+ const merged: Match[] = [];
46
+ for (const list of results) {
47
+ for (const m of list) {
48
+ if (!m.hco_et_code || seen.has(m.hco_et_code)) continue;
49
+ seen.add(m.hco_et_code);
50
+ merged.push(m);
51
+ }
52
+ }
53
+ matches.value = merged;
54
+ } finally {
55
+ isLoading.value = false;
56
+ }
57
+ }
58
+
59
+ onMounted(loadAll);
60
+ watch(() => props.id, loadAll);
61
+
62
+ const locations = computed<LinkedLocation[]>(() =>
63
+ matches.value.map((m) => ({
64
+ name: m.hco_et_label || m.name || '',
65
+ city: m.hco_et_city || null,
66
+ country: 'France',
67
+ finess: m.hco_et_code || null,
68
+ score: typeof m.score === 'number' ? m.score : null,
69
+ })),
70
+ );
71
+ </script>
@@ -0,0 +1,148 @@
1
+ <!--
2
+ Onglet « Investigateurs » d'un essai CT.gov (portage de la page
3
+ /ctgov/[id]/investigators de France Augmented Science).
4
+ -->
5
+ <template>
6
+ <div class="oip-science">
7
+ <OipScienceHcpsLinkedList
8
+ :people="people"
9
+ :title="$t('oipScience.trial.investigatorsAndContacts')"
10
+ :sources="['CT.gov']"
11
+ index-last-update="CT.gov"
12
+ :loading="isLoadingQueryClinicalTrial || isLoadingMatches"
13
+ />
14
+ </div>
15
+ </template>
16
+
17
+ <script setup lang="ts">
18
+ import useGlobalClinicalTrials from '../../../oip-science/composables/useGlobalClinicalTrials';
19
+ import useAuthorMatching from '../../../oip-science/composables/useAuthorMatching';
20
+ import type { LinkedPerson } from '../hcps/LinkedList.vue';
21
+
22
+ const props = withDefaults(
23
+ defineProps<{
24
+ /** Numero NCT de l'essai. */
25
+ id: string;
26
+ highlight?: string[];
27
+ lang?: 'fr' | 'en';
28
+ }>(),
29
+ { highlight: () => [], lang: 'fr' },
30
+ );
31
+
32
+ const { findClinicalTrialById, isLoadingQueryClinicalTrial, resultsQueryClinicalTrial } = useGlobalClinicalTrials();
33
+ const { normalize, fetchNctHcpMatches } = useAuthorMatching();
34
+
35
+ type HcpMatch = Awaited<ReturnType<typeof fetchNctHcpMatches>>[number];
36
+ const hcpMatches = ref<HcpMatch[]>([]);
37
+ const hcpByName = ref<Map<string, HcpMatch>>(new Map());
38
+ const isLoadingMatches = ref(true);
39
+
40
+ async function loadMatchedHcps(nctId: string) {
41
+ const matches = await fetchNctHcpMatches(nctId);
42
+ hcpMatches.value = matches;
43
+ const map = new Map<string, HcpMatch>();
44
+ for (const m of matches) {
45
+ const first = normalize(m.first_name);
46
+ const family = normalize(m.family_name);
47
+ if (first && family) {
48
+ map.set(first + family, m);
49
+ map.set(family + first, m);
50
+ }
51
+ if (family) map.set(family, m);
52
+ }
53
+ hcpByName.value = map;
54
+ }
55
+
56
+ function findHcpMatch(name: string | null | undefined): HcpMatch | null {
57
+ if (!name) return null;
58
+ const n = normalize(name);
59
+ if (!n) return null;
60
+ const direct = hcpByName.value.get(n);
61
+ if (direct) return direct;
62
+ for (const m of hcpMatches.value) {
63
+ const family = normalize(m.family_name);
64
+ if (family && family.length >= 3 && n.includes(family)) {
65
+ const first = normalize(m.first_name);
66
+ if (!first || first.length < 2 || n.includes(first.slice(0, 2))) {
67
+ return m;
68
+ }
69
+ }
70
+ }
71
+ return null;
72
+ }
73
+
74
+ async function load() {
75
+ if (!props.id) return;
76
+ const nctId = props.id.toString();
77
+ isLoadingMatches.value = true;
78
+ await Promise.all([
79
+ findClinicalTrialById({
80
+ id: nctId,
81
+ source: ['facilitiesInvestigators', 'facilitiesContacts', 'overall_officials', 'facilities'],
82
+ }),
83
+ loadMatchedHcps(nctId).finally(() => {
84
+ isLoadingMatches.value = false;
85
+ }),
86
+ ]);
87
+ }
88
+
89
+ onMounted(load);
90
+ watch(() => props.id, load);
91
+
92
+ const people = computed<LinkedPerson[]>(() => {
93
+ const trial = resultsQueryClinicalTrial.value as any;
94
+ const facilities: any[] = trial?.facilities || [];
95
+ const facilityOf = (id: any) => facilities.find((f) => f.facility_id === id);
96
+ const seen = new Set<string>();
97
+
98
+ const person = (
99
+ name: string | null | undefined,
100
+ role: string | null | undefined,
101
+ facility?: any | null,
102
+ affiliation?: string | null,
103
+ ): LinkedPerson => {
104
+ const match = findHcpMatch(name);
105
+ if (match) seen.add(match.hcp_code);
106
+ return {
107
+ name: name || null,
108
+ role: role || null,
109
+ facility: facility?.name || affiliation || null,
110
+ city: facility?.city || null,
111
+ country: facility?.country || null,
112
+ rpps: match?.hcp_code || null,
113
+ score: match?.score ?? null,
114
+ };
115
+ };
116
+
117
+ const rows: LinkedPerson[] = [
118
+ ...(trial?.facilitiesInvestigators || []).map((y: any) =>
119
+ person(y.facility_investigator_name, y.facility_investigator_role, facilityOf(y.facility_id)),
120
+ ),
121
+ ...(trial?.overall_officials || []).map((y: any) => person(y.name, y.role, null, y.affiliation)),
122
+ ...(trial?.facilitiesContacts || []).map((y: any) =>
123
+ person(y.facility_contact_name, y.facility_contact_type, facilityOf(y.facility_id)),
124
+ ),
125
+ ];
126
+
127
+ // Professionnels rapproches par l'index mais absents du registre par leur nom.
128
+ for (const m of hcpMatches.value) {
129
+ if (seen.has(m.hcp_code)) continue;
130
+ rows.push({
131
+ name: `${m.family_name} ${m.first_name}`.trim(),
132
+ role: m.specialty.find((s: any) => s.type === 'PRINCIPAL')?.label || m.specialty[0]?.label || null,
133
+ country: 'France',
134
+ rpps: m.hcp_code,
135
+ score: m.score,
136
+ });
137
+ }
138
+
139
+ // Une personne par RPPS, sinon par nom ; la France en tete.
140
+ const unique = new Map<string, LinkedPerson>();
141
+ for (const row of rows) {
142
+ const key = row.rpps ? `rpps:${row.rpps}` : `name:${normalize(row.name || '')}`;
143
+ if (key === 'name:' || unique.has(key)) continue;
144
+ unique.set(key, row);
145
+ }
146
+ return [...unique.values()].sort((a, b) => (a.country === 'France' ? 0 : 1) - (b.country === 'France' ? 0 : 1));
147
+ });
148
+ </script>