@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,176 @@
1
+ import indexConfig from '../indexConfig.json';
2
+ import useOipElastic from './useOipElastic';
3
+
4
+ export default function useEstablishmentMatching() {
5
+ const { queryCustomElastic } = useOipElastic();
6
+
7
+ type TrialHcoMatch = {
8
+ hco_et_code: string;
9
+ hco_ej_code: string | null;
10
+ hco_et_label: string | null;
11
+ hco_ej_label: string | null;
12
+ hco_et_city: string | null;
13
+ hco_et_dpt_label: string | null;
14
+ hco_et_region_label: string | null;
15
+ hco_et_level_label_3: string | null;
16
+ hco_et_level_label_4: string | null;
17
+ hco_et_sector: string | null;
18
+ name: string | null;
19
+ match_method: string | null;
20
+ score: number | null;
21
+ };
22
+
23
+ async function fetchTrialHcoMatches(
24
+ trialId: string,
25
+ source: 'nct' | 'euct',
26
+ ): Promise<TrialHcoMatch[]> {
27
+ const cfg = source === 'nct' ? indexConfig.hcoNct : indexConfig.hcoEuct;
28
+ const res = await queryCustomElastic({
29
+ index: cfg.index,
30
+ cluster: cfg.cluster,
31
+ customQuery: {
32
+ size: 1,
33
+ query: { term: { clinical_trial_id: trialId } },
34
+ },
35
+ });
36
+
37
+ const doc = res?.results?.[0];
38
+ const matches: TrialHcoMatch[] = [];
39
+ for (const h of doc?.hco || []) {
40
+ if (!h?.hco_et_code) continue;
41
+ matches.push({
42
+ hco_et_code: h.hco_et_code,
43
+ hco_ej_code: h.hco_ej_code || null,
44
+ hco_et_label: h.hco_et_label || null,
45
+ hco_ej_label: h.hco_ej_label || null,
46
+ hco_et_city: h.hco_et_city || null,
47
+ hco_et_dpt_label: h.hco_et_dpt_label || null,
48
+ hco_et_region_label: h.hco_et_region_label || null,
49
+ hco_et_level_label_3: h.hco_et_level_label_3 || null,
50
+ hco_et_level_label_4: h.hco_et_level_label_4 || null,
51
+ hco_et_sector: h.hco_et_sector || null,
52
+ name: h.name || null,
53
+ match_method: h.match_method || null,
54
+ score: typeof h.score === 'number' ? h.score : null,
55
+ });
56
+ }
57
+ return matches;
58
+ }
59
+
60
+ const fetchNctHcoMatches = (nctId: string) => fetchTrialHcoMatches(nctId, 'nct');
61
+ const fetchEuctHcoMatches = (euctId: string) => fetchTrialHcoMatches(euctId, 'euct');
62
+
63
+ // Bulk variant: one ES request fetches all HCO matches for many trial IDs
64
+ // at once. Used by the Generate-a-Report bulk export to enrich the
65
+ // Locations / Enrollment-sites tabs with FINESS without N round-trips.
66
+ async function fetchTrialHcoMatchesBulk(
67
+ trialIds: string[],
68
+ source: 'nct' | 'euct',
69
+ ): Promise<Map<string, TrialHcoMatch[]>> {
70
+ const out = new Map<string, TrialHcoMatch[]>();
71
+ if (!trialIds.length) return out;
72
+ const cfg = source === 'nct' ? indexConfig.hcoNct : indexConfig.hcoEuct;
73
+ const res = await queryCustomElastic({
74
+ index: cfg.index,
75
+ cluster: cfg.cluster,
76
+ customQuery: {
77
+ size: trialIds.length,
78
+ query: { terms: { clinical_trial_id: trialIds } },
79
+ },
80
+ });
81
+ for (const doc of res?.results || []) {
82
+ const id = doc?.clinical_trial_id;
83
+ if (!id) continue;
84
+ const matches: TrialHcoMatch[] = [];
85
+ for (const h of doc?.hco || []) {
86
+ if (!h?.hco_et_code) continue;
87
+ matches.push({
88
+ hco_et_code: h.hco_et_code,
89
+ hco_ej_code: h.hco_ej_code || null,
90
+ hco_et_label: h.hco_et_label || null,
91
+ hco_ej_label: h.hco_ej_label || null,
92
+ hco_et_city: h.hco_et_city || null,
93
+ hco_et_dpt_label: h.hco_et_dpt_label || null,
94
+ hco_et_region_label: h.hco_et_region_label || null,
95
+ hco_et_level_label_3: h.hco_et_level_label_3 || null,
96
+ hco_et_level_label_4: h.hco_et_level_label_4 || null,
97
+ hco_et_sector: h.hco_et_sector || null,
98
+ name: h.name || null,
99
+ match_method: h.match_method || null,
100
+ score: typeof h.score === 'number' ? h.score : null,
101
+ });
102
+ }
103
+ out.set(String(id), matches);
104
+ }
105
+ return out;
106
+ }
107
+
108
+ type HcoTrialRow = {
109
+ clinical_trial_id: string;
110
+ hco_et_code: string;
111
+ score: number | null;
112
+ match_method: string | null;
113
+ };
114
+
115
+ async function fetchTrialsForHcos(
116
+ finessCodes: string[],
117
+ source: 'nct' | 'euct',
118
+ ): Promise<HcoTrialRow[]> {
119
+ if (!finessCodes.length) return [];
120
+ const cfg = source === 'nct' ? indexConfig.hcoNct : indexConfig.hcoEuct;
121
+ const codeSet = new Set(finessCodes);
122
+ const res = await queryCustomElastic({
123
+ index: cfg.index,
124
+ cluster: cfg.cluster,
125
+ customQuery: {
126
+ size: 10000,
127
+ _source: ['clinical_trial_id', 'hco'],
128
+ query: { terms: { 'hco.hco_et_code.keyword': finessCodes } },
129
+ },
130
+ });
131
+ const rows: HcoTrialRow[] = [];
132
+ for (const doc of res?.results || []) {
133
+ if (!doc?.clinical_trial_id) continue;
134
+ for (const h of doc.hco || []) {
135
+ if (!h?.hco_et_code || !codeSet.has(h.hco_et_code)) continue;
136
+ rows.push({
137
+ clinical_trial_id: doc.clinical_trial_id,
138
+ hco_et_code: h.hco_et_code,
139
+ score: typeof h.score === 'number' ? h.score : null,
140
+ match_method: h.match_method || null,
141
+ });
142
+ }
143
+ }
144
+ return rows;
145
+ }
146
+
147
+ async function filterExistingEstablishments(
148
+ finessCodes: (string | null)[],
149
+ ): Promise<Set<string>> {
150
+ const codes = [...new Set(finessCodes.filter((x): x is string => !!x))];
151
+ if (codes.length === 0) return new Set();
152
+ const res = await queryCustomElastic({
153
+ index: indexConfig.establishments.index,
154
+ cluster: indexConfig.establishments.cluster,
155
+ customQuery: {
156
+ size: codes.length,
157
+ _source: ['hco_et_code'],
158
+ query: { terms: { hco_et_code: codes } },
159
+ },
160
+ });
161
+ const set = new Set<string>();
162
+ for (const r of res?.results || []) {
163
+ if (typeof r?.hco_et_code === 'string') set.add(r.hco_et_code);
164
+ }
165
+ return set;
166
+ }
167
+
168
+ return {
169
+ fetchTrialHcoMatches,
170
+ fetchTrialHcoMatchesBulk,
171
+ fetchNctHcoMatches,
172
+ fetchEuctHcoMatches,
173
+ fetchTrialsForHcos,
174
+ filterExistingEstablishments,
175
+ };
176
+ }
@@ -0,0 +1,418 @@
1
+ import indexConfig from '../indexConfig.json';
2
+ import useOipElastic from './useOipElastic';
3
+ import { getSexe } from '../utils/styleAndFormat';
4
+
5
+ // Types de France Augmented Science (types/Euct) : pas portes, le document reste libre.
6
+ type Euct = any;
7
+ type EuctCountries = any;
8
+ type EuctCoSponsor = any;
9
+ type EuctSecondaryObjective = any;
10
+ type EuctPrincipalInclusionCriteria = any;
11
+ type EuctPrincipalExclusionCriteria = any;
12
+ type EuctPrimaryEndPoint = any;
13
+ type EuctSecondaryEndPoint = any;
14
+ type EuctHcp = any;
15
+ type EuctSites = any;
16
+
17
+ export default function useEuct() {
18
+ const { queryElastic, queryCustomElastic, queryOneElastic } = useOipElastic();
19
+
20
+ // GENE CONTENT
21
+ const isLoadingeuctList = ref(false as boolean);
22
+
23
+ const indexQuery = ref(indexConfig.euct.index);
24
+ const cluster = ref(indexConfig.euct.cluster);
25
+ const resultsQueryEuct = ref(null as Euct | null);
26
+ const euctListResult = ref(null as Euct[] | null);
27
+
28
+ const isLoadingQueryEuct = ref(false);
29
+
30
+ const findEuctById = ({
31
+ id,
32
+ source,
33
+ }: {
34
+ id: string;
35
+ source?: string[];
36
+ }): Promise<Euct> => {
37
+ isLoadingQueryEuct.value = true;
38
+ return new Promise<Euct>(async (resolve, reject) => {
39
+ try {
40
+ const res = await queryOneElastic({
41
+ index: indexQuery.value,
42
+ path: 'euct_number',
43
+ source: source,
44
+ cluster: cluster.value,
45
+ id,
46
+ });
47
+ // `queryOneElastic` returns `undefined` when the trial isn't in the
48
+ // index. Resolving anyway matters: guarding the resolve on a truthy
49
+ // result left the promise pending forever and the detail page stuck
50
+ // on its loader.
51
+ resultsQueryEuct.value = (res as Euct) ?? null;
52
+ resolve(res as Euct);
53
+ } catch (error) {
54
+ reject(error);
55
+ } finally {
56
+ isLoadingQueryEuct.value = false;
57
+ }
58
+ });
59
+ };
60
+
61
+ const findManyEuctById = async ({
62
+ ids,
63
+ source,
64
+ }: {
65
+ ids: string[];
66
+ source?: string[];
67
+ }): Promise<Euct[]> => {
68
+ isLoadingQueryEuct.value = true;
69
+
70
+ try {
71
+ const query = {
72
+ from: 0,
73
+ query: {
74
+ bool: {
75
+ must: [
76
+ {
77
+ terms: {
78
+ _id: ids,
79
+ },
80
+ },
81
+ ],
82
+ },
83
+ },
84
+ size: 1000,
85
+ track_total_hits: true,
86
+ sort: [{ first_submitted_date: { order: 'desc' } }],
87
+ _source: {
88
+ include: source || [],
89
+ exclude: [],
90
+ },
91
+ };
92
+
93
+ const res = await queryCustomElastic({
94
+ index: indexConfig.euct.index,
95
+ customQuery: query,
96
+ cluster: cluster.value,
97
+ });
98
+
99
+ if (res && res.results) {
100
+ euctListResult.value = res.results as Euct[];
101
+ return res.results as Euct[];
102
+ } else {
103
+ throw new Error('Aucun résultat trouvé');
104
+ }
105
+ } catch (error) {
106
+ throw error;
107
+ } finally {
108
+ isLoadingQueryEuct.value = false;
109
+ }
110
+ };
111
+
112
+ async function euctContentFromIds({
113
+ ids,
114
+ customQuery,
115
+ }: {
116
+ ids?: any;
117
+ customQuery?: any;
118
+ }): Promise<any> {
119
+ isLoadingeuctList.value = true;
120
+ return new Promise((resolve, reject) => {
121
+ let query = {
122
+ from: 0,
123
+ query: {
124
+ bool: {
125
+ must: [
126
+ {
127
+ terms: {
128
+ _id: ids,
129
+ },
130
+ },
131
+ ],
132
+ },
133
+ },
134
+ size: 1000,
135
+ track_total_hits: true,
136
+ sort: [{ first_submitted_date: { order: 'desc' } }],
137
+ };
138
+
139
+ queryCustomElastic({
140
+ index: 'ope_block374_clinical_trials_euct',
141
+ customQuery: customQuery ? customQuery : query,
142
+ })
143
+ .then((res) => {
144
+ if (res) {
145
+ euctListResult.value = res?.results;
146
+ resolve(res);
147
+ } else {
148
+ reject('Aucun résultat trouvé');
149
+ }
150
+ })
151
+ .catch((error) => {
152
+ reject(error);
153
+ })
154
+ .finally(() => {
155
+ isLoadingeuctList.value = false;
156
+ });
157
+ });
158
+ }
159
+
160
+ // SPECIAL QUERY
161
+ // ::::::::::::::::::::::::::::::::::::::::::::::::::
162
+
163
+ const euctDiseaseQuery = (name: string, synonymes?: string[]) => {
164
+ if (synonymes && synonymes.length) {
165
+ const mergeResult = synonymes.concat(name);
166
+ const query = {
167
+ bool: {
168
+ should: [] as any[],
169
+ },
170
+ };
171
+
172
+ mergeResult.forEach((name) => {
173
+ query.bool.should.push({
174
+ query_string: {
175
+ query: `(\"${name}\")`,
176
+ fields: [
177
+ 'public_title',
178
+ 'full_title',
179
+ 'main_objective_en',
180
+ 'rare_diseases.concat_disease_name_orpha_code',
181
+ 'rare_diseases.disease_synonyms',
182
+ 'medical_condition.condition_name_en',
183
+ ],
184
+ default_operator: 'AND',
185
+ analyze_wildcard: true,
186
+ allow_leading_wildcard: false,
187
+ phrase_slop: 0,
188
+ auto_generate_synonyms_phrase_query: true,
189
+ fuzziness: 'AUTO',
190
+ },
191
+ });
192
+ });
193
+ return query;
194
+ }
195
+
196
+ return {
197
+ bool: {
198
+ must: [
199
+ {
200
+ query_string: {
201
+ query: `(\"${name}\")`,
202
+ fields: [
203
+ 'rare_diseases.concat_disease_name_orpha_code',
204
+ 'rare_diseases.disease_synonyms',
205
+ 'medical_condition.condition_name_en',
206
+ ],
207
+ default_operator: 'AND',
208
+ analyze_wildcard: true,
209
+ allow_leading_wildcard: false,
210
+ phrase_slop: 0,
211
+ auto_generate_synonyms_phrase_query: true,
212
+ fuzziness: 'AUTO',
213
+ },
214
+ },
215
+ ],
216
+ },
217
+ };
218
+ };
219
+
220
+ const euctMoleculeQuery = (label: string) => ({
221
+ bool: {
222
+ must: [
223
+ {
224
+ query_string: {
225
+ query: `(\"${label}\")`,
226
+ fields: [
227
+ 'products.productDictionaryInfo.prodName',
228
+ 'products.productDictionaryInfo.atcName',
229
+ 'products.productDictionaryInfo.atcCode',
230
+ 'products.productDictionaryInfo.activeSubstanceName',
231
+ ],
232
+ default_operator: 'AND',
233
+ analyze_wildcard: true,
234
+ allow_leading_wildcard: false,
235
+ phrase_slop: 0,
236
+ auto_generate_synonyms_phrase_query: true,
237
+ fuzziness: 'AUTO',
238
+ },
239
+ },
240
+ ],
241
+ },
242
+ });
243
+
244
+ const euctIdsQuery = (ids: string) => ({
245
+ bool: {
246
+ must: [
247
+ {
248
+ terms: {
249
+ _id: ids,
250
+ },
251
+ },
252
+ ],
253
+ },
254
+ });
255
+
256
+ // FOR EXPORTS AND TABLES
257
+ // ::::::::::::::::::::::::::::::::::::::::::::::::::
258
+ const formatEUCTData = (results: Euct[]) => {
259
+ return results.map((item: Euct) => ({
260
+ ...item,
261
+
262
+ therapeutic_area: item.therapeutic_area?.length
263
+ ? item.therapeutic_area.join(',\n')
264
+ : null,
265
+
266
+ medical_condition: item?.medical_condition?.length
267
+ ? item.medical_condition
268
+ .map((x: any) => x.condition_name_en)
269
+ .join(',\n')
270
+ : null,
271
+
272
+ products: item.products?.length
273
+ ? item.products
274
+ .map((x: any) =>
275
+ x.productDictionaryInfo.prodName
276
+ ? x.productDictionaryInfo.prodName
277
+ : x.productDictionaryInfo.activeSubstanceName
278
+ )
279
+ .join(',\n')
280
+ : null,
281
+
282
+ countries: item.countries?.length
283
+ ? item.countries.map((x: EuctCountries) => x.country_name).join(',\n')
284
+ : null,
285
+
286
+ co_sponsors: item.co_sponsors?.length
287
+ ? item.co_sponsors
288
+ .map((x: EuctCoSponsor) => x?.organisation?.name)
289
+ .join(',\n')
290
+ : null,
291
+
292
+ enrollment_status: `${
293
+ item.enrollment_status?.total_planned_enrollment || 0
294
+ } subjects in ${item.sites?.length || 0} site(s)`,
295
+
296
+ trial_scope: item?.trial_scope?.length
297
+ ? item.trial_scope.join(',\n')
298
+ : null,
299
+
300
+ secondary_objective: item.secondary_objective?.length
301
+ ? item.secondary_objective
302
+ .sort((a: any, b: any) => a.objective_number - b.objective_number)
303
+ .map(
304
+ (x: EuctSecondaryObjective) =>
305
+ `${x.objective_number}. ${x.second_objective_en}`
306
+ )
307
+ .join(',\n\n')
308
+ : null,
309
+
310
+ principal_inclusion_criteria: item?.principal_inclusion_criteria?.length
311
+ ? item.principal_inclusion_criteria
312
+ .sort((a: any, b: any) => a.criteria_number - b.criteria_number)
313
+ .map(
314
+ (x: EuctPrincipalInclusionCriteria) =>
315
+ `${x.criteria_number}. ${x.inclusion_criteria_en}`
316
+ )
317
+ .join(',\n\n')
318
+ : null,
319
+
320
+ principal_exclusion_criteria: item?.principal_exclusion_criteria?.length
321
+ ? item.principal_exclusion_criteria
322
+ .sort((a: any, b: any) => a.criteria_number - b.criteria_number)
323
+ .map(
324
+ (x: EuctPrincipalExclusionCriteria) =>
325
+ `${x.criteria_number}. ${x.exclusion_criteria_en}`
326
+ )
327
+ .join(',\n\n')
328
+ : null,
329
+
330
+ primary_end_point: item?.primary_end_point?.length
331
+ ? item.primary_end_point
332
+ .sort((a: any, b: any) => a.end_point_number - b.end_point_number)
333
+ .map(
334
+ (x: EuctPrimaryEndPoint) =>
335
+ `${x.end_point_number}. ${x.primary_end_point_en}`
336
+ )
337
+ .join(',\n\n')
338
+ : null,
339
+
340
+ secondary_end_point: item?.secondary_end_point?.length
341
+ ? item.secondary_end_point
342
+ .sort((a: any, b: any) => a.end_point_number - b.end_point_number)
343
+ .map(
344
+ (x: EuctSecondaryEndPoint) =>
345
+ `${x.end_point_number}. ${x.secondary_end_point_en}`
346
+ )
347
+ .join(',\n\n')
348
+ : null,
349
+
350
+ hcps: item.hcps?.length
351
+ ? item.hcps
352
+ .map(
353
+ (x: EuctHcp) =>
354
+ `Name: ${x.title} ${x.first_name} ${x.last_name} - Role: ${x.department_name} - Country: ${x.country} - RPPS: ${x.hcp_id}`
355
+ )
356
+ .join(',\n\n')
357
+ : null,
358
+
359
+ sites: item.sites?.length
360
+ ? item.sites
361
+ .map(
362
+ (x: EuctSites) =>
363
+ `Name: ${x.organisation_name} - Country: ${x.country} - City: ${x.city}`
364
+ )
365
+ .join(',\n\n')
366
+ : null,
367
+
368
+ rare_diseases: item?.rare_diseases?.length
369
+ ? item.rare_diseases
370
+ .map((x: any) => `${x.concat_disease_name_orpha_code}`)
371
+ .join(',\n\n')
372
+ : null,
373
+
374
+ oncology_icd10: item.diseases?.length
375
+ ? item.diseases
376
+ .flatMap((x) => x.icd10?.map((icd) => icd.combined_label) ?? [])
377
+ .filter((v, i, a) => a.indexOf(v) === i)
378
+ .join(',\n')
379
+ : null,
380
+
381
+ oncology_terms: item.diseases?.length
382
+ ? item.diseases
383
+ .flatMap((x) => x.section?.map((s) => s.label) ?? [])
384
+ .filter((v, i, a) => v && a.indexOf(v) === i)
385
+ .join(',\n')
386
+ : null,
387
+
388
+ subject_population: item.subject_population?.age_range,
389
+
390
+ subject_gender: getSexe(
391
+ item?.subject_population?.has_female,
392
+ item?.subject_population?.has_male
393
+ ),
394
+
395
+ pubmed_citations_id: item.pubmed_citations_id?.length || 0,
396
+
397
+ euct_link: `https://euclinicaltrials.eu/search-for-clinical-trials/?lang=en&EUCT=${item.euct_number}?lang=en`,
398
+
399
+ nct_link: item.nct_number
400
+ ? `https://clinicaltrials.gov/ct2/show/${item.nct_number}`
401
+ : null,
402
+ }));
403
+ };
404
+
405
+ return {
406
+ euctContentFromIds,
407
+ euctListResult,
408
+ isLoadingeuctList,
409
+ euctIdsQuery,
410
+ euctDiseaseQuery,
411
+ euctMoleculeQuery,
412
+ findEuctById,
413
+ resultsQueryEuct,
414
+ isLoadingQueryEuct,
415
+ findManyEuctById,
416
+ formatEUCTData,
417
+ };
418
+ }
@@ -0,0 +1,107 @@
1
+ import indexConfig from '../indexConfig.json';
2
+ import useOipElastic from './useOipElastic';
3
+ import type { EuctCenters } from '../utils/f-types';
4
+
5
+ export default function useEuctCenter() {
6
+ // Une instance par composant (dans FAS, useElastic etait appele au niveau du module)
7
+ const { queryCustomElastic, queryOneElastic } = useOipElastic();
8
+ const indexQuery = ref(indexConfig.euctCenters.index);
9
+ const cluster = ref(indexConfig.euctCenters.cluster);
10
+
11
+ const resultsEuctCenters = ref(null as EuctCenters | null);
12
+ const euctCentersResultsList = ref(null as EuctCenters[] | null);
13
+ const isLoadingQueryEuctCenters = ref(false);
14
+
15
+ const findEuctCentersById = ({
16
+ id,
17
+ source,
18
+ }: {
19
+ id: string;
20
+ source?: string[];
21
+ }): Promise<EuctCenters> => {
22
+ isLoadingQueryEuctCenters.value = true;
23
+ return new Promise<EuctCenters>(async (resolve, reject) => {
24
+ try {
25
+ const res = await queryOneElastic({
26
+ index: indexQuery.value,
27
+ path: '_id',
28
+ source: {
29
+ include: source || [],
30
+ exclude: [],
31
+ },
32
+ cluster: cluster.value,
33
+ id,
34
+ });
35
+ if (res) {
36
+ resultsEuctCenters.value = res as EuctCenters;
37
+ resolve(res as EuctCenters);
38
+ } else {
39
+ // Document absent de l'index : la fiche montre son etat vide.
40
+ resolve(null as any);
41
+ }
42
+ } catch (error) {
43
+ reject(error);
44
+ } finally {
45
+ isLoadingQueryEuctCenters.value = false;
46
+ }
47
+ });
48
+ };
49
+
50
+ const findManyEuctCentersById = async ({
51
+ ids,
52
+ source,
53
+ }: {
54
+ ids: string[];
55
+ source?: string[];
56
+ }): Promise<EuctCenters[]> => {
57
+ isLoadingQueryEuctCenters.value = true;
58
+
59
+ try {
60
+ const query = {
61
+ from: 0,
62
+
63
+ query: {
64
+ bool: {
65
+ must: [
66
+ {
67
+ terms: {
68
+ _id: ids,
69
+ },
70
+ },
71
+ ],
72
+ },
73
+ },
74
+ size: 1000,
75
+ track_total_hits: true,
76
+ _source: {
77
+ include: source || [],
78
+ exclude: [],
79
+ },
80
+ };
81
+
82
+ const res = await queryCustomElastic({
83
+ index: indexQuery.value,
84
+ customQuery: query,
85
+ cluster: cluster.value,
86
+ });
87
+ if (res && res.results) {
88
+ euctCentersResultsList.value = res.results as EuctCenters[];
89
+ return res.results as EuctCenters[];
90
+ } else {
91
+ throw new Error('Aucun résultat trouvé');
92
+ }
93
+ } catch (error) {
94
+ throw error;
95
+ } finally {
96
+ isLoadingQueryEuctCenters.value = false;
97
+ }
98
+ };
99
+
100
+ return {
101
+ euctCentersResultsList,
102
+ resultsEuctCenters,
103
+ isLoadingQueryEuctCenters,
104
+ findEuctCentersById,
105
+ findManyEuctCentersById,
106
+ };
107
+ }