@tekkare/romulus 0.1.251 → 0.1.253

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 (267) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/runtime/utils/projects.d.ts +8 -0
  3. package/dist/runtime/utils/projects.js +8 -5
  4. package/layer/app/app.config.ts +6 -0
  5. package/layer/app/components/OipPubmedSummary.vue +223 -0
  6. package/layer/app/components/OipResourceDrawer.vue +331 -0
  7. package/layer/app/components/OipResourceDrawerLink.vue +40 -0
  8. package/layer/app/components/OipResourceField.vue +41 -0
  9. package/layer/app/components/OipResourceGenericCard.vue +54 -0
  10. package/layer/app/components/OipResourceSource.vue +50 -0
  11. package/layer/app/components/OipResourceTags.vue +65 -0
  12. package/layer/app/components/OipResourceView.vue +107 -0
  13. package/layer/app/components/oip-drugs/fiches/AssociationFiche.vue +775 -0
  14. package/layer/app/components/oip-drugs/fiches/BrandFiche.vue +1111 -0
  15. package/layer/app/components/oip-drugs/fiches/IndicationFiche.vue +182 -0
  16. package/layer/app/components/oip-drugs/fiches/JoFiche.vue +101 -0
  17. package/layer/app/components/oip-drugs/fiches/PndsFiche.vue +279 -0
  18. package/layer/app/components/oip-drugs/heart/JoCard.vue +314 -0
  19. package/layer/app/components/oip-drugs/heart/TextHighlight.vue +326 -0
  20. package/layer/app/components/oip-drugs/heart/ansm/BrandCard.vue +187 -0
  21. package/layer/app/components/oip-drugs/heart/ansm/CisDetail.vue +363 -0
  22. package/layer/app/components/oip-drugs/heart/ansm/CisTable.vue +167 -0
  23. package/layer/app/components/oip-drugs/heart/associations/NationalAssemblyCard.vue +373 -0
  24. package/layer/app/components/oip-drugs/heart/associations/NationalAssemblyFiche.vue +372 -0
  25. package/layer/app/components/oip-drugs/heart/associations/TranscriptionCard.vue +55 -0
  26. package/layer/app/components/oip-drugs/heart/elastic/SourceContainerElastic.vue +103 -0
  27. package/layer/app/components/oip-drugs/heart/france/BrandGroupCard.vue +450 -0
  28. package/layer/app/components/oip-drugs/heart/mitm/Card.vue +199 -0
  29. package/layer/app/components/oip-drugs/heart/mitm/Source.vue +23 -0
  30. package/layer/app/components/oip-drugs/heart/mitm/details/Overview.vue +381 -0
  31. package/layer/app/components/oip-drugs/heart/put/Card.vue +212 -0
  32. package/layer/app/components/oip-drugs/heart/results/AccesCard.vue +696 -0
  33. package/layer/app/components/oip-drugs/heart/results/AlertCard.vue +342 -0
  34. package/layer/app/components/oip-drugs/heart/results/AsmrCursor.vue +229 -0
  35. package/layer/app/components/oip-drugs/heart/results/EvalCard.vue +370 -0
  36. package/layer/app/components/oip-drugs/heart/results/IndicCard.vue +743 -0
  37. package/layer/app/components/oip-drugs/heart/results/MatchExplain.vue +243 -0
  38. package/layer/app/components/oip-drugs/heart/results/PrefRows.vue +105 -0
  39. package/layer/app/components/oip-drugs/heart/results/details/HasHistory.vue +487 -0
  40. package/layer/app/components/oip-drugs/heart/results/details/InternationalCountryCard.vue +461 -0
  41. package/layer/app/components/oip-drugs/heart/results/details/InternationalOverview.vue +200 -0
  42. package/layer/app/components/oip-drugs/heart/results/details/InternationalPriceDrawer.vue +647 -0
  43. package/layer/app/components/oip-drugs/heart/results/details/Monograph.vue +128 -0
  44. package/layer/app/components/oip-drugs/heart/results/details/OpinionDrawer.vue +535 -0
  45. package/layer/app/components/oip-drugs/heart/results/details/PresentationDrawer.vue +738 -0
  46. package/layer/app/components/oip-drugs/heart/results/details/Price.vue +227 -0
  47. package/layer/app/components/oip-drugs/heart/results/details/PriceOverview.vue +1261 -0
  48. package/layer/app/components/oip-drugs/heart/results/details/RecordDrawer.vue +494 -0
  49. package/layer/app/components/oip-drugs/heart/results/details/RecordList.vue +426 -0
  50. package/layer/app/components/oip-drugs/heart/results/les/Card.vue +340 -0
  51. package/layer/app/components/oip-drugs/heart/results/les/Drawer.vue +67 -0
  52. package/layer/app/components/oip-drugs/heart/results/les/IndicationDetail.vue +149 -0
  53. package/layer/app/components/oip-drugs/heart/results/les/Source.vue +20 -0
  54. package/layer/app/components/oip-drugs/heart/results/les/SourceTag.vue +39 -0
  55. package/layer/app/components/oip-drugs/heart/searchPage/DynamicSource.vue +29 -0
  56. package/layer/app/components/oip-drugs/medicam/market/Chart.vue +121 -0
  57. package/layer/app/components/oip-drugs/outpatient/ProductEvolution.vue +236 -0
  58. package/layer/app/components/oip-drugs/resources/Ansm.vue +75 -0
  59. package/layer/app/components/oip-drugs/resources/Association.vue +30 -0
  60. package/layer/app/components/oip-drugs/resources/Brand.vue +65 -0
  61. package/layer/app/components/oip-drugs/resources/Contribution.vue +216 -0
  62. package/layer/app/components/oip-drugs/resources/Hospital.vue +151 -0
  63. package/layer/app/components/oip-drugs/resources/Indication.vue +54 -0
  64. package/layer/app/components/oip-drugs/resources/Jo.vue +57 -0
  65. package/layer/app/components/oip-drugs/resources/Medicam.vue +140 -0
  66. package/layer/app/components/oip-drugs/resources/Mitm.vue +84 -0
  67. package/layer/app/components/oip-drugs/resources/NationalAssembly.vue +93 -0
  68. package/layer/app/components/oip-drugs/resources/Outpatient.vue +97 -0
  69. package/layer/app/components/oip-drugs/resources/Pnds.vue +24 -0
  70. package/layer/app/components/oip-drugs/resources/Price.vue +106 -0
  71. package/layer/app/components/oip-drugs/resources/Put.vue +143 -0
  72. package/layer/app/components/oip-science/appel-a-projet/Cards.vue +162 -0
  73. package/layer/app/components/oip-science/clinical-trials/Cards.vue +382 -0
  74. package/layer/app/components/oip-science/clinical-trials/CardsGlobal.vue +273 -0
  75. package/layer/app/components/oip-science/clinical-trials/LinkedList.vue +366 -0
  76. package/layer/app/components/oip-science/clinical-trials/PairBadge.vue +25 -0
  77. package/layer/app/components/oip-science/clinical-trials/PairBody.vue +182 -0
  78. package/layer/app/components/oip-science/clinical-trials/SitesDrawer.vue +95 -0
  79. package/layer/app/components/oip-science/common/Form.vue +73 -0
  80. package/layer/app/components/oip-science/common/MatchAvgTag.vue +283 -0
  81. package/layer/app/components/oip-science/common/NoData.vue +16 -0
  82. package/layer/app/components/oip-science/common/ScoreBadge.vue +35 -0
  83. package/layer/app/components/oip-science/common/Source.vue +31 -0
  84. package/layer/app/components/oip-science/common/TableTags.vue +279 -0
  85. package/layer/app/components/oip-science/common/Timeline.vue +104 -0
  86. package/layer/app/components/oip-science/common/ToolTips.vue +89 -0
  87. package/layer/app/components/oip-science/cross/publicationPubmed.vue +484 -0
  88. package/layer/app/components/oip-science/crossData/CrossDataLocations.vue +71 -0
  89. package/layer/app/components/oip-science/ctgov/Investigators.vue +148 -0
  90. package/layer/app/components/oip-science/ctgov/Locations.vue +102 -0
  91. package/layer/app/components/oip-science/ctgov/Publications.vue +52 -0
  92. package/layer/app/components/oip-science/ctgov/Sponsors.vue +66 -0
  93. package/layer/app/components/oip-science/ctgov/StudyDescription.vue +1136 -0
  94. package/layer/app/components/oip-science/ctgov-centers/ClinicalTrials.vue +40 -0
  95. package/layer/app/components/oip-science/ctgov-centers/Publications.vue +50 -0
  96. package/layer/app/components/oip-science/ctgov-investigators/ClinicalTrials.vue +43 -0
  97. package/layer/app/components/oip-science/ctgov-investigators/Informations.vue +171 -0
  98. package/layer/app/components/oip-science/ctgov-investigators/Publications.vue +44 -0
  99. package/layer/app/components/oip-science/elastic/DateRangeFilter.vue +209 -0
  100. package/layer/app/components/oip-science/elastic/Filter.vue +719 -0
  101. package/layer/app/components/oip-science/elastic/MatchExplain.vue +152 -0
  102. package/layer/app/components/oip-science/establishments/Cards.vue +183 -0
  103. package/layer/app/components/oip-science/establishments/ClinicalTrials.vue +62 -0
  104. package/layer/app/components/oip-science/establishments/Hcps.vue +227 -0
  105. package/layer/app/components/oip-science/establishments/Informations.vue +263 -0
  106. package/layer/app/components/oip-science/establishments/LinkedList.vue +153 -0
  107. package/layer/app/components/oip-science/establishments/MatchedLabels.vue +194 -0
  108. package/layer/app/components/oip-science/establishments/PublicFinance.vue +62 -0
  109. package/layer/app/components/oip-science/euct/Investigators.vue +125 -0
  110. package/layer/app/components/oip-science/euct/Locations.vue +102 -0
  111. package/layer/app/components/oip-science/euct/Products.vue +101 -0
  112. package/layer/app/components/oip-science/euct/Publications.vue +89 -0
  113. package/layer/app/components/oip-science/euct/Sponsors.vue +86 -0
  114. package/layer/app/components/oip-science/euct/StudyDescription.vue +1075 -0
  115. package/layer/app/components/oip-science/euct-centers/ClinicalTrials.vue +40 -0
  116. package/layer/app/components/oip-science/euct-centers/Publications.vue +55 -0
  117. package/layer/app/components/oip-science/euct-investigators/ClinicalTrials.vue +41 -0
  118. package/layer/app/components/oip-science/euct-investigators/Informations.vue +171 -0
  119. package/layer/app/components/oip-science/euct-investigators/Publications.vue +51 -0
  120. package/layer/app/components/oip-science/hcps/AppelsAProjet.vue +62 -0
  121. package/layer/app/components/oip-science/hcps/Cards.vue +237 -0
  122. package/layer/app/components/oip-science/hcps/ClinicalTrials.vue +78 -0
  123. package/layer/app/components/oip-science/hcps/Informations.vue +296 -0
  124. package/layer/app/components/oip-science/hcps/LinkedList.vue +153 -0
  125. package/layer/app/components/oip-science/hcps/MatchedLabels.vue +207 -0
  126. package/layer/app/components/oip-science/hcps/Pubmed.vue +128 -0
  127. package/layer/app/components/oip-science/local/SliderTabs.vue +99 -0
  128. package/layer/app/components/oip-science/local/SliderTabsFull.vue +135 -0
  129. package/layer/app/components/oip-science/pubmed/Authors.vue +128 -0
  130. package/layer/app/components/oip-science/pubmed/Cards.vue +417 -0
  131. package/layer/app/components/oip-science/pubmed/ClinicalTrials.vue +71 -0
  132. package/layer/app/components/oip-science/resources/Phrc.vue +39 -0
  133. package/layer/app/composables/useOipChapterScroll.ts +43 -0
  134. package/layer/app/composables/useOipResourceHooks.ts +27 -0
  135. package/layer/app/composables/useOipResourceNav.ts +66 -0
  136. package/layer/app/composables/useOipResourceViews.ts +117 -0
  137. package/layer/app/composables/useOipResources.ts +83 -0
  138. package/layer/app/oip-drugs/PORTING.md +120 -0
  139. package/layer/app/oip-drugs/composables/useAssociationDetail.ts +220 -0
  140. package/layer/app/oip-drugs/composables/useAssociationExport.ts +63 -0
  141. package/layer/app/oip-drugs/composables/useAssociationLookup.ts +105 -0
  142. package/layer/app/oip-drugs/composables/useCardPrefOptions.ts +201 -0
  143. package/layer/app/oip-drugs/composables/useCurrency.ts +121 -0
  144. package/layer/app/oip-drugs/composables/useDirectusSources.ts +224 -0
  145. package/layer/app/oip-drugs/composables/useHeartBrandMatches.ts +112 -0
  146. package/layer/app/oip-drugs/composables/useHeartDetailSearches.ts +479 -0
  147. package/layer/app/oip-drugs/composables/useHeartIntlApprovals.ts +661 -0
  148. package/layer/app/oip-drugs/composables/useHeartLesIndication.ts +168 -0
  149. package/layer/app/oip-drugs/composables/useHeartMatchExplain.ts +231 -0
  150. package/layer/app/oip-drugs/composables/useHeartReports.ts +239 -0
  151. package/layer/app/oip-drugs/composables/useHeartSearchFields.ts +233 -0
  152. package/layer/app/oip-drugs/composables/useMergeByKey.ts +23 -0
  153. package/layer/app/oip-drugs/composables/useOipDrugsElastic.ts +621 -0
  154. package/layer/app/oip-drugs/composables/usePageEmpty.ts +58 -0
  155. package/layer/app/oip-drugs/composables/v-useSalesSource.ts +64 -0
  156. package/layer/app/oip-drugs/drugs.css +500 -0
  157. package/layer/app/oip-drugs/esIndices.ts +322 -0
  158. package/layer/app/oip-drugs/kinds.ts +264 -0
  159. package/layer/app/oip-drugs/locales/en.json +1571 -0
  160. package/layer/app/oip-drugs/locales/fr.json +1571 -0
  161. package/layer/app/oip-drugs/monographs/blincyto-has.vue +289 -0
  162. package/layer/app/oip-drugs/monographs/darzalex-has.vue +276 -0
  163. package/layer/app/oip-drugs/monographs/dupixent-has.vue +216 -0
  164. package/layer/app/oip-drugs/monographs/enhertu-has.vue +280 -0
  165. package/layer/app/oip-drugs/monographs/has-sein-2023.vue +1649 -0
  166. package/layer/app/oip-drugs/monographs/ibrance-has.vue +277 -0
  167. package/layer/app/oip-drugs/monographs/imfinzi-has.vue +293 -0
  168. package/layer/app/oip-drugs/monographs/keytruda-has.vue +334 -0
  169. package/layer/app/oip-drugs/monographs/kisqali-has.vue +201 -0
  170. package/layer/app/oip-drugs/monographs/opdivo-has.vue +231 -0
  171. package/layer/app/oip-drugs/monographs/ozempic-has.vue +186 -0
  172. package/layer/app/oip-drugs/monographs/tecentriq-has.vue +312 -0
  173. package/layer/app/oip-drugs/monographs/yescarta-has.vue +272 -0
  174. package/layer/app/oip-drugs/types/elastic.ts +113 -0
  175. package/layer/app/oip-drugs/types/es/agencies.ts +152 -0
  176. package/layer/app/oip-drugs/types/es/directory.ts +54 -0
  177. package/layer/app/oip-drugs/types/es/docByIndex.ts +151 -0
  178. package/layer/app/oip-drugs/types/es/openmedic.ts +67 -0
  179. package/layer/app/oip-drugs/types/es/outpatient.ts +71 -0
  180. package/layer/app/oip-drugs/types/es/portal.ts +130 -0
  181. package/layer/app/oip-drugs/types/es/retail.ts +107 -0
  182. package/layer/app/oip-drugs/types/es/statistics.ts +29 -0
  183. package/layer/app/oip-drugs/types/heart/CurrencyRate.ts +16 -0
  184. package/layer/app/oip-drugs/types/heart/ansm.ts +106 -0
  185. package/layer/app/oip-drugs/types/heart/tag.ts +29 -0
  186. package/layer/app/oip-drugs/utils/associationFields.ts +118 -0
  187. package/layer/app/oip-drugs/utils/contributions.ts +36 -0
  188. package/layer/app/oip-drugs/utils/countryMapping.ts +387 -0
  189. package/layer/app/oip-drugs/utils/esHighlight.ts +21 -0
  190. package/layer/app/oip-drugs/utils/exportSheets.ts +57 -0
  191. package/layer/app/oip-drugs/utils/heartIntlPrice.ts +228 -0
  192. package/layer/app/oip-drugs/utils/heartStyleAndFormat.ts +262 -0
  193. package/layer/app/oip-drugs/utils/heartStyleAnsm.ts +71 -0
  194. package/layer/app/oip-drugs/utils/latestDate.ts +31 -0
  195. package/layer/app/oip-drugs/utils/p-heartCardTags.ts +53 -0
  196. package/layer/app/oip-drugs/utils/priceExport.ts +269 -0
  197. package/layer/app/oip-drugs/utils/q-hasRatings.ts +43 -0
  198. package/layer/app/oip-drugs/utils/s-openCard.ts +20 -0
  199. package/layer/app/oip-drugs/utils/sourceUuids.ts +155 -0
  200. package/layer/app/oip-drugs/utils/styleAndFormat.ts +40 -0
  201. package/layer/app/oip-drugs/utils/v-salesSeries.ts +156 -0
  202. package/layer/app/oip-drugs/utils/v-scopeTags.ts +23 -0
  203. package/layer/app/oip-science/PORTING.md +93 -0
  204. package/layer/app/oip-science/assets/smartSelection.json +428 -0
  205. package/layer/app/oip-science/composables/useAuthorMatching.ts +342 -0
  206. package/layer/app/oip-science/composables/useCTCenter.ts +107 -0
  207. package/layer/app/oip-science/composables/useCTInvestigators.ts +107 -0
  208. package/layer/app/oip-science/composables/useEstablishmentCrossData.ts +139 -0
  209. package/layer/app/oip-science/composables/useEstablishmentMatching.ts +176 -0
  210. package/layer/app/oip-science/composables/useEuct.ts +418 -0
  211. package/layer/app/oip-science/composables/useEuctCenter.ts +107 -0
  212. package/layer/app/oip-science/composables/useEuctInvestigators.ts +107 -0
  213. package/layer/app/oip-science/composables/useGlobalClinicalTrials.ts +624 -0
  214. package/layer/app/oip-science/composables/useLinkedTrials.ts +83 -0
  215. package/layer/app/oip-science/composables/useOipElastic.ts +658 -0
  216. package/layer/app/oip-science/composables/useOipSearchExplain.ts +166 -0
  217. package/layer/app/oip-science/composables/usePubmed.ts +487 -0
  218. package/layer/app/oip-science/composables/useSearchFields.ts +153 -0
  219. package/layer/app/oip-science/indexConfig.json +794 -0
  220. package/layer/app/oip-science/locales/en.json +483 -0
  221. package/layer/app/oip-science/locales/fr.json +483 -0
  222. package/layer/app/oip-science/science.css +1182 -0
  223. package/layer/app/oip-science/utils/a-openCard.ts +23 -0
  224. package/layer/app/oip-science/utils/b-openCard.ts +35 -0
  225. package/layer/app/oip-science/utils/c-filterContext.ts +81 -0
  226. package/layer/app/oip-science/utils/f-types.ts +241 -0
  227. package/layer/app/oip-science/utils/frDiseaseTerms.ts +269 -0
  228. package/layer/app/oip-science/utils/matchMethod.ts +53 -0
  229. package/layer/app/oip-science/utils/sliderTabs.ts +13 -0
  230. package/layer/app/oip-science/utils/sources.ts +15 -0
  231. package/layer/app/oip-science/utils/styleAndFormat.ts +608 -0
  232. package/layer/app/oip-science/utils/toPresets.ts +7 -0
  233. package/layer/app/oip-science/utils/valueTable.ts +126 -0
  234. package/layer/app/plugins/oip-science-i18n.ts +17 -0
  235. package/layer/app/utils/oipFicheRoutes.ts +118 -0
  236. package/layer/app/utils/oipResources.ts +440 -0
  237. package/layer/nuxt.config.ts +22 -2
  238. package/layer/public/reports/blincyto-has.html +292 -0
  239. package/layer/public/reports/blincyto-has.pdf +0 -0
  240. package/layer/public/reports/darzalex-has.html +279 -0
  241. package/layer/public/reports/darzalex-has.pdf +0 -0
  242. package/layer/public/reports/dupixent-has.html +217 -0
  243. package/layer/public/reports/dupixent-has.pdf +0 -0
  244. package/layer/public/reports/enhertu-has.html +283 -0
  245. package/layer/public/reports/enhertu-has.pdf +0 -0
  246. package/layer/public/reports/has-sein-2023.html +1639 -0
  247. package/layer/public/reports/ibrance-has.html +280 -0
  248. package/layer/public/reports/ibrance-has.pdf +0 -0
  249. package/layer/public/reports/imfinzi-has.html +297 -0
  250. package/layer/public/reports/imfinzi-has.pdf +0 -0
  251. package/layer/public/reports/keytruda-has.html +337 -0
  252. package/layer/public/reports/keytruda-has.pdf +0 -0
  253. package/layer/public/reports/kisqali-has.html +202 -0
  254. package/layer/public/reports/kisqali-has.pdf +0 -0
  255. package/layer/public/reports/opdivo-has.html +232 -0
  256. package/layer/public/reports/opdivo-has.pdf +0 -0
  257. package/layer/public/reports/ozempic-has.html +188 -0
  258. package/layer/public/reports/ozempic-has.pdf +0 -0
  259. package/layer/public/reports/tecentriq-has.html +315 -0
  260. package/layer/public/reports/tecentriq-has.pdf +0 -0
  261. package/layer/public/reports/yescarta-has.html +275 -0
  262. package/layer/public/reports/yescarta-has.pdf +0 -0
  263. package/layer/server/api/oip/es.post.ts +99 -0
  264. package/layer/server/api/oip/pdf.get.ts +70 -0
  265. package/layer/server/api/oip/reports.get.ts +98 -0
  266. package/layer/server/api/oip/resource-types.get.ts +22 -0
  267. package/package.json +1 -1
@@ -0,0 +1,621 @@
1
+ import { ES_INDEX } from "../esIndices";
2
+ import type { EsIndexName } from "../esIndices";
3
+ import type {
4
+ EsAggregations,
5
+ EsAggregationsSpec,
6
+ EsHit,
7
+ EsQueryClause,
8
+ EsSearchBody,
9
+ EsSort,
10
+ } from "../types/elastic";
11
+ import type { EsDocFor } from "../types/es/docByIndex";
12
+ import { useOipEsIndices } from "../../composables/useOipEsIndices";
13
+
14
+ // Portage de useHeartElastic de fr_drugs_app, pour les fiches medicament du
15
+ // layer (cf. components/oip-drugs) : memes signatures, mais les requetes
16
+ // passent par le proxy du layer (/api/oip/es, cluster `drugs`), joignable
17
+ // depuis toutes les apps. Les listes nommees (`keyName`), que fr_drugs_app
18
+ // rangeait dans son resultStore, restent dans l'instance : `lists[keyName]`.
19
+
20
+ /**
21
+ * Un filtre du portail, tel que les pages le construisent : un nom et la
22
+ * valeur qui part dans la requete. `values` reste libre, c'est tantot une
23
+ * clause, tantot un tableau de clauses, tantot un nombre (la taille).
24
+ */
25
+ export interface HeartQueryFilter {
26
+ filter?: string;
27
+ values?: any;
28
+ [option: string]: any;
29
+ }
30
+
31
+ /**
32
+ * Ce que rend une requete du portail : les documents de l'index interroge, ou
33
+ * les hits complets quand l'appelant demande `with_id` (il lui faut alors le
34
+ * `_id` Elasticsearch, cf. les projets Hub-R).
35
+ */
36
+ export interface HeartQueryResult<TDoc, TWithId extends boolean | undefined> {
37
+ results: TWithId extends true ? Array<EsHit<TDoc>> : TDoc[];
38
+ total: number;
39
+ aggregations?: EsAggregations;
40
+ }
41
+
42
+ export default function useOipDrugsElastic() {
43
+ /** Les listes nommees (`keyName`), propres a l'instance (ex-resultStore). */
44
+ const lists = reactive<Record<string, { results: any[]; total: number; aggregations?: any }>>({});
45
+
46
+ const config = useRuntimeConfig();
47
+ //
48
+ // const resultsQuery = ref([] as Array<object>);
49
+ const queryTotalResults = ref(0 as number);
50
+ // const isLoadingQuery = ref(false as boolean);
51
+
52
+ const filterResults = ref({} as Object);
53
+
54
+ /**
55
+ * La derniere requete de liste a-t-elle echoue ?
56
+ *
57
+ * Les erreurs etaient avalees dans la console : quand le cluster refusait la
58
+ * requete, la page gardait les resultats precedents a l'ecran et donnait a
59
+ * lire une liste qui ne correspondait plus aux filtres poses. Une page peut
60
+ * desormais le dire. Les pages qui ne lisent pas ce drapeau se comportent
61
+ * exactement comme avant.
62
+ */
63
+ const queryError = ref<any>(null);
64
+
65
+ const resolveCluster = (cluster: string | undefined, index: EsIndexName) => {
66
+ if (cluster && cluster !== 'default') return cluster;
67
+ if (index === ES_INDEX.CURRENCY_RATE) return 'newCluster';
68
+ // Le catalogue designe les index par id Directus : le prefixe se lit sur
69
+ // le nom.
70
+ if (useOipEsIndices().nameOf(index ?? '').startsWith('ope_block303_')) return 'newCluster';
71
+ return 'pubmed';
72
+ };
73
+
74
+ const setFetchUrl = (cluster: string, index: EsIndexName) => {
75
+ // Une seule route, quel que soit le cluster logique : le proxy choisit la cle.
76
+ return `/api/oip/es?cluster=drugs&index=${encodeURIComponent(index)}`;
77
+ };
78
+
79
+ const setFetchHeaders = (cluster: string) => {
80
+ switch (cluster) {
81
+ case 'newCluster':
82
+ return {};
83
+ case 'pubmed':
84
+ default:
85
+ return {};
86
+ }
87
+ };
88
+
89
+ const mapHits = (resolved: string, hits: any[], withId: boolean) => {
90
+ if (withId) return hits;
91
+ if (resolved === 'newCluster') {
92
+ return hits.map((x: any) => {
93
+ const p = x._source;
94
+ p.id = x?._id;
95
+ return p;
96
+ });
97
+ }
98
+ return hits;
99
+ };
100
+
101
+ async function queryElastic<
102
+ TIndex extends EsIndexName,
103
+ TWithId extends boolean | undefined = undefined,
104
+ >({
105
+ index,
106
+ take,
107
+ skip,
108
+ customQuery,
109
+ sortQuery,
110
+ keyName,
111
+ source,
112
+ highlight,
113
+ with_id,
114
+ cluster,
115
+ aggs,
116
+ collapse
117
+ }: {
118
+ index: TIndex;
119
+ take: HeartQueryFilter | number | undefined;
120
+ skip?: number;
121
+ customQuery: HeartQueryFilter[];
122
+ sortQuery?: HeartQueryFilter | HeartQueryFilter[];
123
+ keyName?: string;
124
+ source?: string[] | boolean;
125
+ highlight?: Record<string, any>;
126
+ with_id?: TWithId;
127
+ cluster?: string;
128
+ aggs?: EsAggregationsSpec;
129
+ collapse?: EsSearchBody["collapse"];
130
+ }): Promise<HeartQueryResult<EsDocFor<TIndex>, TWithId>> {
131
+ let esResults: any = {
132
+ results: [],
133
+ total: 0,
134
+ };
135
+
136
+ // isLoadingQuery.value = true;
137
+ const queryMustValues = customQuery
138
+ .map((obj) => obj.values)
139
+ .filter((item) => item !== undefined);
140
+
141
+ // QUERY
142
+ let query: any = {
143
+ from: skip ? skip : 0,
144
+ _source: source ? source : [],
145
+ query: {
146
+ bool: {
147
+ must: queryMustValues,
148
+ },
149
+ },
150
+ aggregations: aggs ? aggs : {},
151
+ highlight: highlight ? highlight : {},
152
+ size: take && typeof take === "object" ? take.values : 10,
153
+ sort: sortQuery && !Array.isArray(sortQuery) ? sortQuery.values : [],
154
+ track_total_hits: true,
155
+ };
156
+
157
+ if (collapse) query.collapse = collapse;
158
+
159
+ // FETCH
160
+ const resolved = resolveCluster(cluster, index);
161
+ await $fetch(setFetchUrl(resolved, index), {
162
+ method: 'POST',
163
+ headers: setFetchHeaders(resolved),
164
+ body: query,
165
+ })
166
+ .then((res: any) => {
167
+ queryError.value = null;
168
+ // IF NEED TO KEEP TRACK OF RESULTS ARRAY FROM STORE
169
+ if (res) {
170
+ if (keyName) {
171
+ if (!with_id) {
172
+ lists[keyName] = {
173
+ results: mapHits(resolved, res.hits.hits, false),
174
+ total: res.hits.total.value,
175
+ aggregations: res.aggregations,
176
+ };
177
+ }
178
+
179
+ if (with_id) {
180
+ lists[keyName] = {
181
+ results: res.hits.hits,
182
+ total: res.hits.total.value,
183
+ aggregations: res.aggregations,
184
+ };
185
+ }
186
+ } else {
187
+ // OTHERWISE USE resultsQuery as result
188
+
189
+ if (!with_id) {
190
+ esResults.results = mapHits(resolved, res.hits.hits, false);
191
+ esResults.total = res.hits.total.value;
192
+ esResults.aggregations = res.aggregations;
193
+ }
194
+
195
+ if (with_id) {
196
+ esResults.results = res.hits.hits;
197
+ esResults.total = res.hits.total.value;
198
+ esResults.aggregations = res.aggregations;
199
+ }
200
+ }
201
+ }
202
+
203
+ // isLoadingQuery.value = false;
204
+ })
205
+ .catch((err) => {
206
+ queryError.value = err;
207
+ console.log('Error', err);
208
+ });
209
+ return esResults;
210
+ }
211
+
212
+ // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
213
+ // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
214
+ // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
215
+
216
+ async function queryOneElastic<TIndex extends EsIndexName>({
217
+ index,
218
+ path,
219
+ id,
220
+ source,
221
+ cluster,
222
+ }: {
223
+ index: TIndex;
224
+ path: string;
225
+ id: string | number | string[] | undefined;
226
+ source?: string[] | boolean;
227
+ cluster?: string;
228
+ }): Promise<EsDocFor<TIndex>> {
229
+ let esResults: any = {};
230
+
231
+ // QUERY
232
+ let query: any = {
233
+ from: 0,
234
+ _source: source ? source : [],
235
+ query: {
236
+ bool: {
237
+ must: [{ match: { [`${path}`]: id } }],
238
+ },
239
+ },
240
+
241
+ size: 1,
242
+
243
+ track_total_hits: true,
244
+ };
245
+
246
+ // FETCH
247
+ const resolved = resolveCluster(cluster, index);
248
+ await $fetch(setFetchUrl(resolved, index), {
249
+ method: 'POST',
250
+ headers: setFetchHeaders(resolved),
251
+ body: query,
252
+ })
253
+ .then((res: any) => {
254
+ if (res) {
255
+ esResults = res.hits.hits.map((x: any) => x._source)[0];
256
+ }
257
+ })
258
+ .catch((err) => {
259
+ console.log('Error', err);
260
+ });
261
+ return esResults;
262
+ }
263
+ // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
264
+ // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
265
+ // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
266
+
267
+ async function queryCustomElastic({
268
+ index,
269
+ customQuery,
270
+ onlyOne,
271
+ cluster,
272
+ allAnswer,
273
+ }: {
274
+ index: EsIndexName;
275
+ customQuery: EsSearchBody;
276
+ onlyOne?: boolean;
277
+ cluster?: string;
278
+ allAnswer?: boolean;
279
+ }) {
280
+ let esResults: any = {
281
+ results: [],
282
+ total: 0,
283
+ };
284
+
285
+ // QUERY
286
+ let query = customQuery;
287
+
288
+ // FETCH
289
+ const resolved = resolveCluster(cluster, index);
290
+ await $fetch(setFetchUrl(resolved, index), {
291
+ method: 'POST',
292
+ headers: setFetchHeaders(resolved),
293
+ body: query,
294
+ })
295
+ .then((res: any) => {
296
+ if (res) {
297
+ if (onlyOne) {
298
+ esResults = res.hits.hits.map((x: any) => x._source)[0];
299
+ } else if (allAnswer) {
300
+ esResults = res;
301
+ } else {
302
+ esResults.results = res.hits.hits.map((x: any) => x._source);
303
+ esResults.total = res.hits.total.value;
304
+ }
305
+ }
306
+ })
307
+ .catch((err) => {
308
+ console.log('Error', err);
309
+ });
310
+ return esResults;
311
+ }
312
+ // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
313
+ // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
314
+ // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
315
+
316
+ async function queryFilter({
317
+ index,
318
+ filters,
319
+ take,
320
+ customQuery,
321
+ cluster,
322
+ }: {
323
+ index: EsIndexName;
324
+ filters: HeartQueryFilter;
325
+ take: number;
326
+ customQuery: HeartQueryFilter[];
327
+ cluster?: string;
328
+ }) {
329
+ let esResults: any[] = [];
330
+
331
+ const queryMustValues = customQuery
332
+ .filter((x: any) => x.filter != filters.name)
333
+ .map((obj: any) => obj.values)
334
+ .filter((item: any) => item !== undefined);
335
+
336
+ // QUERY
337
+ let query = {
338
+ from: 0,
339
+ _source: false,
340
+ query: {
341
+ bool: {
342
+ must: queryMustValues,
343
+ },
344
+ },
345
+
346
+ aggs: filters.agg,
347
+
348
+ size: 0,
349
+ track_total_hits: true,
350
+ };
351
+
352
+ // FETCH
353
+ const resolved = resolveCluster(cluster, index);
354
+ await $fetch(setFetchUrl(resolved, index), {
355
+ method: 'POST',
356
+ headers: setFetchHeaders(resolved),
357
+ body: query,
358
+ })
359
+ .then((res: any) => {
360
+ if (filters.isNested) {
361
+ esResults = res.aggregations[filters.path.split('.')[0]][
362
+ filters.path
363
+ ].buckets.map((x: { key: any }) => x.key);
364
+ }
365
+ else if (filters.isDoubleNested) {
366
+ esResults = res.aggregations[filters.path.split(".")[0]][`${filters.path.split(".")[0]}.${filters.path.split(".")[1]}`][
367
+ filters.path]
368
+ .buckets.map((x: { key: any }) => x.key);
369
+ } else {
370
+ esResults = res.aggregations[filters.path].buckets.map(
371
+ (x: { key: any }) => x.key
372
+ );
373
+ }
374
+ })
375
+ .catch((err) => {
376
+ console.log('Error', err);
377
+ });
378
+
379
+ return esResults;
380
+ }
381
+
382
+ // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
383
+ // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
384
+ // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
385
+
386
+ async function querySearchFilter({
387
+ index,
388
+ filters,
389
+ take,
390
+ customQuery,
391
+ cluster,
392
+ }: {
393
+ index: EsIndexName;
394
+ filters: HeartQueryFilter;
395
+ take: number;
396
+ customQuery: HeartQueryFilter[];
397
+ cluster?: string;
398
+ }) {
399
+ let esResults: any[] = [];
400
+
401
+ const queryMustValues = customQuery
402
+ .map((obj: any) => obj.values)
403
+ .filter((item: any) => item !== undefined);
404
+
405
+ let query: any = {
406
+ from: 0,
407
+ _source: false,
408
+ query: {
409
+ bool: {
410
+ must: [
411
+ {
412
+ bool: {
413
+ must: queryMustValues,
414
+ },
415
+ },
416
+ filters.searchQuery,
417
+ ],
418
+ },
419
+ },
420
+
421
+ aggs: filters.agg,
422
+
423
+ size: 0,
424
+
425
+ track_total_hits: true,
426
+ };
427
+
428
+ // FETCH
429
+ const resolved = resolveCluster(cluster, index);
430
+ await $fetch(setFetchUrl(resolved, index), {
431
+ method: 'POST',
432
+ headers: setFetchHeaders(resolved),
433
+ body: query,
434
+ })
435
+ .then((res: any) => {
436
+ // Avec une saisie, le `terms` est enveloppe dans une agg `filter`
437
+ // (alertFilter, molAtcFilter, FranceFilter) : les buckets sont alors un
438
+ // niveau plus bas, dans `filteredData`.
439
+ const bucketKeys = (agg: any) =>
440
+ (agg.filteredData?.buckets ?? agg.buckets).map((x: { key: any }) => x.key);
441
+ if (filters.isNested) {
442
+ esResults = bucketKeys(res.aggregations[filters.path][filters.path]);
443
+ }
444
+ else if (filters.isDoubleNested) {
445
+ const parts = filters.path.split(".");
446
+ esResults = bucketKeys(
447
+ res.aggregations[parts[0]][`${parts[0]}.${parts[1]}`][filters.path]
448
+ );
449
+ } else {
450
+ esResults = bucketKeys(res.aggregations[filters.path]);
451
+ }
452
+ })
453
+ .catch((err) => {
454
+ console.log("Error", err);
455
+ });
456
+ return esResults;
457
+ }
458
+
459
+ async function queryMultiFilter({
460
+ index,
461
+ filters,
462
+ take,
463
+ customQuery,
464
+ cluster
465
+ }: {
466
+ index: EsIndexName;
467
+ filters: HeartQueryFilter;
468
+ take: number;
469
+ customQuery: HeartQueryFilter[];
470
+ cluster?: string
471
+ }) {
472
+ let esResults: any[] = [];
473
+
474
+ const queryMustValues = customQuery
475
+ .filter((x: any) => x.filter != filters.name)
476
+ .map((obj: any) => obj.values)
477
+ .filter((item: any) => item !== undefined);
478
+
479
+ // QUERY
480
+ let query = {
481
+ from: 0,
482
+ _source: false,
483
+ query: {
484
+ bool: {
485
+ must: queryMustValues,
486
+ },
487
+ },
488
+
489
+ aggs: filters.agg,
490
+
491
+ size: take,
492
+ track_total_hits: true,
493
+ };
494
+
495
+ // FETCH
496
+ const resolved = resolveCluster(cluster, index);
497
+ await $fetch(setFetchUrl(resolved, index), {
498
+ method: 'POST',
499
+ headers: setFetchHeaders(resolved),
500
+ body: query,
501
+ })
502
+ .then((res: any) => {
503
+ if (filters.isNested) {
504
+ esResults = res.aggregations[filters.path.split(".")[0]][
505
+ filters.aggName
506
+ ].buckets.map((x: { key: any }) => x.key);
507
+ }
508
+ else if(filters.isDoubleNested) {
509
+ esResults = res.aggregations[filters.path.split(".")[0]][filters.path][
510
+ filters.aggName
511
+ ].buckets.map((x: { key: any }) => x.key);
512
+ } else {
513
+ esResults = res.aggregations[filters.path].buckets.map(
514
+ (x: { key: any }) => x.key
515
+ );
516
+ }
517
+ })
518
+ .catch((err) => {
519
+ console.log('Error', err);
520
+ });
521
+ return esResults;
522
+ }
523
+
524
+ // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
525
+ // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
526
+ // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
527
+
528
+ async function queryMultiSearchFilter({
529
+ index,
530
+ filters,
531
+ take,
532
+ customQuery,
533
+ cluster
534
+ }: {
535
+ index: EsIndexName;
536
+ filters: HeartQueryFilter;
537
+ take: number;
538
+ customQuery: HeartQueryFilter[];
539
+ cluster?: string
540
+ }) {
541
+ let esResults: any[] = [];
542
+
543
+ const queryMustValues = customQuery
544
+ .map((obj: any) => obj.values)
545
+ .filter((item: any) => item !== undefined);
546
+
547
+ let query: any = {
548
+ from: 0,
549
+ _source: false,
550
+ query: {
551
+ bool: {
552
+ must: [
553
+ {
554
+ bool: {
555
+ must: queryMustValues,
556
+ },
557
+ },
558
+ filters.searchQuery,
559
+ ],
560
+ },
561
+ },
562
+
563
+ aggs: filters.agg,
564
+
565
+ size: take,
566
+
567
+ track_total_hits: true,
568
+ };
569
+
570
+ // FETCH
571
+ const resolved = resolveCluster(cluster, index);
572
+ await $fetch(setFetchUrl(resolved, index), {
573
+ method: 'POST',
574
+ headers: setFetchHeaders(resolved),
575
+ body: query,
576
+ })
577
+ .then((res: any) => {
578
+ if (filters.isNested) {
579
+ esResults = res.aggregations[filters.path][filters.path][
580
+ filters.aggName
581
+ ].buckets.map((x: { key: any }) => x.key);
582
+ }else if(filters.isDoubleNested) {
583
+ esResults = res.aggregations[filters.path][filters.path][
584
+ filters.aggName
585
+ ].buckets.map((x: { key: any }) => x.key);
586
+ } else {
587
+ esResults = res.aggregations[filters.path].buckets.map(
588
+ (x: { key: any }) => x.key
589
+ );
590
+ }
591
+ })
592
+ .catch((err) => {
593
+ console.log("Error", err);
594
+ });
595
+ return esResults;
596
+ }
597
+
598
+
599
+
600
+ // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
601
+ // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
602
+ // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
603
+
604
+ return {
605
+ lists,
606
+ queryElastic,
607
+ queryError,
608
+ // isLoadingQuery,
609
+ queryTotalResults,
610
+ // resultsQuery,
611
+ // :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
612
+ queryFilter,
613
+ filterResults,
614
+ queryCustomElastic,
615
+ querySearchFilter,
616
+
617
+ queryOneElastic,
618
+ queryMultiFilter,
619
+ queryMultiSearchFilter
620
+ };
621
+ }
@@ -0,0 +1,58 @@
1
+ import type { InjectionKey } from "vue";
2
+
3
+ /**
4
+ * Etat vide d'une page a plusieurs blocs.
5
+ *
6
+ * Les blocs d'un ecran lisent la meme selection : quand elle ne ramene rien,
7
+ * ils se vident ensemble et repetent le meme message autant de fois qu'il y a
8
+ * de cartes. La page en montre un seul a leur place, et cache la pile.
9
+ *
10
+ * Chaque bloc s'inscrit lui-meme et dit s'il est vide. La page ne tient donc
11
+ * aucune liste : rien a tenir a jour quand un bloc arrive, part ou change de
12
+ * requete. Lire le cache des requetes aurait evite de toucher les blocs, mais
13
+ * leurs cles se composent a l'execution (`"medicamForecastSales" + index`) —
14
+ * la page aurait du refaire ce calcul, et se serait tue en silence a la
15
+ * premiere divergence.
16
+ *
17
+ * Un bloc cache par un onglet reste monte, donc compte : la page ne sort son
18
+ * etat vide que si TOUS ses blocs sont vides, onglets compris. C'est
19
+ * volontairement prudent — jamais d'etat vide par-dessus un onglet qui a de la
20
+ * donnee, au prix de laisser passer un onglet vide quand un autre est plein.
21
+ */
22
+ type Registry = Record<number, boolean>;
23
+
24
+ const PAGE_EMPTY = Symbol("oip-page-empty") as InjectionKey<Registry>;
25
+
26
+ let nextId = 0;
27
+
28
+ /**
29
+ * Cote page : ouvre le registre et rend le verdict.
30
+ *
31
+ * Vrai quand au moins un bloc s'est inscrit et qu'aucun n'a de ligne. Tant
32
+ * qu'un bloc n'a pas repondu, il n'est pas inscrit : la page reste affichee,
33
+ * avec ses squelettes.
34
+ */
35
+ export function usePageEmpty() {
36
+ const blocks = reactive<Registry>({});
37
+ provide(PAGE_EMPTY, blocks);
38
+
39
+ return computed(() => {
40
+ const states = Object.values(blocks);
41
+ return states.length > 0 && states.every(Boolean);
42
+ });
43
+ }
44
+
45
+ /**
46
+ * Cote bloc : declare si ce bloc est vide, et le redit a chaque changement.
47
+ *
48
+ * Sans page a l'ecoute (le bloc est monte ailleurs), l'appel ne fait rien : un
49
+ * composant reste utilisable hors d'un ecran qui agrege.
50
+ */
51
+ export function reportPageEmpty(empty: () => boolean) {
52
+ const blocks = inject(PAGE_EMPTY, null);
53
+ if (!blocks) return;
54
+
55
+ const id = nextId++;
56
+ watch(empty, (value) => { blocks[id] = value; }, { immediate: true });
57
+ onScopeDispose(() => { delete blocks[id]; });
58
+ }